PK T9èD¯6äµøP øP novius-os-dubrovka/index.html
Welcome to Novius OS documentation. It is hosted and generated by Read The Docs.
All contributions are welcome: Reporting or fixing errors, submitting improvements or translations.
Sources are on Git Hub, we look forward to your Pull Request. Contact us if you need help with your contribution.
Have a serevr with MySQL and PHP 5.3+.
Novius OS can run with:
We describe following the install process on a server LAMP (Linux/Apache/MySQL/PHP), Debian type, for which you have admin rights. Adapt to your configuration.
Install of AMP.
sudo apt-get install apache2 php5 mysql-server libapache2-mod-php5 php5-mysql
Enable mod_rewrite of Apache.
sudo a2enmod rewrite
Open a terminal and enter:
cd /var/www
sudo wget http://raw.github.com/novius-os/ci/master/dubrovka/tools/install.sh && sh install.sh
Once the installation completes:
Note
We recommend you follow this procedure when installing Novius OS on shared hosting:
The following commands are provided as example when installing Novius OS on Ubuntu, you should adapt depending on your distribution.
sudo nano /etc/apache2/sites-available/novius-os.conf
<VirtualHost *:80>
DocumentRoot /var/www/novius-os/public
ServerName novius-os
<Directory /var/www/novius-os/public>
AllowOverride All
Options FollowSymLinks
</Directory>
</VirtualHost>
The default configuration has a public folder. This is where the DoumentRoot should point.
Enable the new VirtualHost:
sudo a2ensite novius-os.conf
Then, reload Apache to apply the new configuration.
sudo service apache2 reload
If you install Novius OS on your local computer, you must add a line in the /etc/hosts file, containing the value you entered for ServerName (novius-os in the above example).
sudo nano /etc/hosts
Add the following line:
127.0.0.1 novius-os
You should clone the repository available on GitHub:
git clone --recursive git://github.com/novius-os/novius-os.git
This command downloads the main repository, and its submodules :
cd /var/www/novius-os/
git checkout dev
git submodule update --recursive
Sample of Nginx configuration:
server {
listen 80;
server_name localhost;
root /var/www/novius-os;
index index.php index.html;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log notice;
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
error_page 404 /public/htdocs/novius-os/404.php;
autoindex off;
location @rewrites {
rewrite ^/(admin(/.*)?)$ /public/htdocs/novius-os/admin.php last;
rewrite ^/.+(.html|/)$ /public/htdocs/novius-os/front.php last;
rewrite ^/([^.]*)$ /public/htdocs/novius-os/front.php last;
rewrite ^ /public/htdocs/novius-os/front.php last;
}
rewrite ^/(static|cache|media|data|htdocs)/(.*) /public/$1/$2 break;
rewrite ^/install.php /public/htdocs/install.php last;
try_files $uri @rewrites;
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
}
}
You’ve followed the first part of the installation process. Novius OS files are now installed, the server is set-up and you accesses http://your.domain/novius-os/. You’ve done the hardest, now comes the easy part :-).
This step should be straightforward if you’ve installed Novius OS following the quick procedure. In other cases, don’t worry if a lof of red comes up! Your website just needs writing permissions in some directories. This step gives you the explanations and the commands to run to fix everything.
To fast-track this step, just copy the commands’ summary given at the bottom of the page and copy them in a terminal. You’re done!
You need a MySQL database including a user with the required rights. For shared hosting, your provider must have given you these details. In other cases, here is an example for a localhost database:
CREATE DATABASE `your_db_name` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
GRANT ALL PRIVILEGES ON `your_db_name`.* TO 'your_user_name'@localhost IDENTIFIED BY 'your_password';
FLUSH PRIVILEGES;
Fill in the four required fields according to your database configuration.
This step will create two files local/config/db.php and local/config/crypt.php and—more importantly-the tables needed for Novius OS to run.
Fill the required fields for create the first administrator account of your Novius OS. Email and password will be used for the first connection. Those informations can be modified later in back-office.
Warning
We urge you to follow the recommendation of this page.
On setting contexts, refer to the principles and API documentation.
You’re redirected to the applications manager. Install the applications you need.
To sign out, click your name in the top-right corner. A menu shows:
You’re redirected to the sign-in form.
In this example, we use the nano editor and the virtualhost config file name is 000-default :
sudo nano /etc/apache2/site-enabled/000-default
In the file, find a line like this (if Novius OS is installed in a /var/www/ subdirectory):
<Directory /var/www>
AllowOverride None
Options FollowSymLinks
</Directory>
Change AllowOverride None by AllowOverride All. Save your change and restart Apache:
sudo service apache2 restart
You can try to run your WAMP server with administrator privileges.
Or you can try to change file permission on Novius OS directory, recursively on all subfolders. Give write access for everybody (Example for windows 7). Maybe restart server after.
You can give write permissions with your FTP client. For example, a tuto for Filezilla
chmod a+w means give write permissions for all users.
sudo apt-get install php5-gd
sudo apt-get install libgd2-xpm-dev*
Some distributions have removed the standard JSON extension as of PHP 5.5rc2 due to a license conflict.
sudo apt-get install php5-json
This problem exists for Web hoster Infomaniak.ch
Edit .htaccess file. Change this line:
Options +FollowSymLinks -Indexes
By:
Options +FollowSymlinks -SymlinksIfOwnerMatch -Indexes
Add a RewriteBase in the .htaccess file:
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /novius-os-install-dir/
If you want create a website, go to the Webpages application:
The Visualise action allows you to preview the page before publishing it.
Once you’re happy with your work, choose ‘Will be published’ and save.
Look in awe at what you’ve achieved:
Pages need templates. They contains the content written in the back-office and set the style.
The template is chosen when adding or modifying a page.
New templates can be added. Templates are embedded in the applications. To add a template, one must therefore add an application (with the applications manager).
Thanks to the applications, you can add new features to Novius OS.
Allows you to install and uninstall applications.
After modifying the metadata of an application or your website (Novius OS’ instance), you must apply the changes in the applications manager. This is also the case when a native application has been modified.
If you installed Novius OS with Git, run these commands from Novius OS directory:
git fetch origin
git checkout master/dubrovka
git submodule update --recursive --init
Note
The submodule update can display a line
warning: unable to rmdir packages/log: directory not empty
in this case, execute this command
rm -rf novius-os/packages/log/
If you downloaded the Zip file, the procedure is more complex.
Let’s say you installed novius OS in my_site/.
First, backup your old directory (copy it and make a .zip file with it).
Download the new Zip of Novius OS and extract it. You now have a novius-os/ directory.
Now you can continue the update.
Before running the automated migration tools, please backup your database.
If you’re allowed to acces SSH on the server, run this command from your Novius OS directory:
sudo php oil refine migrate
sudo php oil refine migrate -m
If you can’t access SSH, you can run the migration from your browser:
If you can’t access SSH, you can run the migration from back-office of your Novius OS:
Warning
When you access to your back-office without migrated, your software is in a instable state. Sources not matches with the DB state. You will probably see error messages. You can ignore them.
If you have personnal developments, you need to follow the Migration guide from the Chiba 2 version to the Dubrovka version.
To undestand what XSendFile is and its usefulness, go here : Media centre.
Apache provides the feature through the mod_xsendfile module. Then, the .htaccess must be updated to enable it:
# Post-installation optimisation
<IfModule xsendfile_module>
XSendFile On
# Replace "novius-os-install-dir" by the real Novius OS installed directory
XSendFilePath /novius-os-install-dir/local/data
</IfModule>
Novius OS know when the module is enabled automatically and enable the XSendFile feature accordingly.
nginx provides the feature natively, but the header used is X-Accel-Redirect. In that case, you need to edit the config.php file to tell Novius OS the appropriate header :
<?php
return array(
// ...
'novius-os' => array(
// ...
'use_xsendfile' => 'X-Accel-Redirect',
),
);
Novius OS follows the Model-View-Controller pattern, which defines how to work:
Framework usages influences us a lot when designing and implementing applications, and it’s really helpful to know the role they play in. However, this documentation relates to Novius OS, so please refers to external documenations and tutorials for more informations about them.
FuelPHP is the PHP framework used by Novius OS.
The most used parts are the ORM, the cascading file system and the data validation. The framework also provides a lot of tools which make application development easier, like the Arr <http://docs.fuelphp.com/classes/arr.html> class for example.
ORM stands for Object-relational mapping.
$new_monkey = Model_Monkey::forge();
$new_monkey->monk_name = 'Julian';
$new_monkey->save();
$monkeys = Model_Monkey::find('all');
foreach ($monkeys as $monkey) {
//...
}
$monkey = Model_Monkey::find(4);
$monkey->delete();
Novius OS relies on FuelPHP’s ORM. Please refer to its documentation.
They’re similar to FuelPHP’s Observers but are more powerful:
Although actions are done server side using PHP, Novius OS is mostly written in JavaScript. This is explained by the utmost importance given to the UI and the UX (see UI guidelines).
To offer rich interfaces and interactions, Novius OS uses several JS frameworks:
There’s a hierarchy between those frameworks, Wijmo has the most impact on Novius OS’ ergonomy.
A file can either be:
This leads to 4 possibles usage, and each of them is reflected in the ~/public/ folder:
Note
Here, Novius OS refers to the core and also to any application of the website.
There’s a 5th directory ~/public/media/, used by the Media centre.
Where Novius OS can write the developer cannot, and vice-versa.
~/public/static/ and ~/public/htdocs/ have the same sub-directories structure:
Note
classes and views directories should not contain a lot of files, since most of your developments should go in applications.
MVC applies to core, and also to applications
There are 6 main directories:
To understand how Novius OS versions are numbered, let’s take a closer look at this sample release: Dubrovka 4.1.2
Note
Prior to Novius OS Dubrovka, version numbers didn’t fully follow the pattern described above.
Two namespaces are added by Novius OS :
novius-os: | points to NOSPATH. |
---|---|
local: | points to APPPATH. |
In Novius OS, front-office and back-office are two very separated areas.
So instead of a single index.php entry point from FuelPHP, Novius OS has two entry points:
Novius OS defines a special route for the back-office, which works as follow:
<?php
'routes' => array(
'^admin/(:segment)/(:any)' => '$1/admin/$2',
),
For example, the URL admin/noviusos_page/page/insert_update/113 will be transformed into noviusos_page/admin/page/insert_update/113. Which boils down to executing the action_insert_update method from the Controller_Admin_Page controller of the noviusos_page application.
See also
Novius OS interface is built upon great ergonomic guidelines. To develop applications, two of them should be known: tab navigation and App desk.
See the screencast related to the App Desk
The App Desk is the home page of an application, it allows to browse and access the items. It’s made of the following elements:
Main grid: list the items of an application, one or severals views can be chosen (thumbnails, grid, tree, etc.). Its content is filtered by the inspectors and / or a full-text search. There’s only one main grid for each App Desk.
Inspectors: they constitute the meta items of an application (such as blog authors, or media folders). Inspectors allows to filter the content displayed in the main grid (to show the posts of a particular user). Some inspectors allows to handle the data too (for instance, deleting a folder).
- Preview inspector: the preview inspector is a special case. Unlike others inspectors, it doesn’t act on the main grid, but rather the main grid acts on it: when an item is selected, details are shown in the preview inspector (image, properties, summary of the possible actions on the item).
Actions: in the vast majority of times, each App Desk must offer one and only one main action, which is Add a new item. Secondary actions can also appear as links: adding a meta item (like a folder) or other usual actions (such as exporting data).
The App Desk offers a lot of possible page layouts for developers and end-users. Meanwhile, we recommend to show a Three-Pane Interface as default.
Alternatively, the preview inspector can be placed under the main grid.
An application is defined by its models, controllers and views. They vary upon the type of the application, but some principles doesn’t change and can be reused for every application.
For an application to be added (installed) using the application manager, it needs a metadata.config.php file. This file must contain the application’s namespace, written as Provider\AppName, along its name, version and provider (at least a name).
An application should also defines one of the following in the metadata.config.php file:
To understand the App Desk, please read the ergonomic guidelines first.
The main feature of the AppDesk is several configurable elements:
The main grid can offer several views (don’t mistake them with the V of MVC) : grid, tree or thumbnails.
These views are defined with configuration files, which specify exactly which data to display and the actions.
Inspecteurs are also defined with configuration files. They tell on which attribute or relation the data of the main grid will be filtered, and the actions associated to the meta items contained in the inspector. Please note the inspectors either based on:
On the App Desk screen, the modifications on the data are operated by calling a controller.
Some operations are made directly (for instance to delete an item, only a confirmation is asked). In this case, the the App Desk’s controller is responsible to carry them out.
Other operations needs a specific view and it’s the controller of the item which is responsible. Most of the time, the view is a form (new item / edition), which is built using a configuration file and is populated using an instance of the associated Model. The controller is invoked again to save the data when submiting the form.
Observers exists in the FuelPHP framework.
They contain the logic which is directly dependent of the model. They’re expressed when a specific event is triggered. They are used to format, change or validate the properties of the model (for instance before adding a new item in the database).
Behaviours are specific to Novius OS, they embrace and extend this principle. Observers can only make specific actions when the model triggers a specific event (such as before_save). Behaviours can do more and contain a set of additional methods which defines a particular behaviour, for example translatable or publishable. They also can be triggered by events.
The main advantage of this tools is to share code between different models (horizontal reuse).
See also
A content nugget is a coherent set of data meant to be shared.
Content nuggets’ data are of the following nature:
For an application to share its content, it must have the Sharable Behaviour. This entails defining the content nugget’s structure for the application—i.e. which fields are to be shared.
Data catchers are application components which make use of the content nuggets.
They are defined in the application’s metadata.config.php file, as are other components (templates, enhancers and launchers).
The Blog data catcher is used to create a new blog post with other applications’ content, especially customer-specific applications. Let’s say you add a new product to your catalogue, this data catcher allows you to easily announce this new addition on your blog.
Here is how the data catcher for simple sharing to Twitter is defined:
<?php
return array(
'data_catchers' => array(
'noviusos_simpletwitter' => array(
'title' => 'Twitter',
'description' => '',
'iconUrl' => 'static/apps/noviusos_simpletwitter/img/twitter.png',
'action' => array(
'action' => 'window.open',
'url' => 'https://twitter.com/intent/tweet?text={{urlencode:'.\Nos\DataCatcher::TYPE_TITLE.'}}&url={{urlencode:absolute_url}}',
),
'onDemand' => true,
'specified_models' => false,
'required_data' => array(
\Nos\DataCatcher::TYPE_TITLE,
),
'optional_data' => array(
\Nos\DataCatcher::TYPE_URL,
),
),
),
);
The Simple Twitter data catcher requires content nuggets featuring at least a title. URLs are optional but are used when provided.
Your Novius OS instance can manage your showcase website, which exists in 3 languages (French, English and Spanish), your mobile website, which only exists in French and your events website, which exists in English.
Site / Language | French | English | Spanish |
---|---|---|---|
Showcase | X | X | X |
Mobile | X | ||
Events | X |
In this situation, yout Novius OS instance manages 5 contexts:
Everything to configure the contexts is described in the API documentation
He who can do more can do less. Your Novius OS can manage:
It can be done at any time! Novius OS will happily manage every new contexts, sites or languages found in the configuration. Just change the contexts.config.php file and new contexts will be taken into account straight away.
See also
Although Novius OS natively manages multiple contexts, each application decides the way it uses them on its own.
Three cases can be found.
It’s the most simple and default case. The application doesn’t manage any context and has nothing to do.
Its content will be the same across all contexts and can be used (through enhancers) by any of them.
The application manages contexts. Each item will be associated with a context, and should only be used inside of it.
Technically, tables of the application have a context column (of type varchar(25)) which contains the context code. Models of the application must implement the Contextable behaviour.
The application manages contexts and can link them together. Each item is associated with a context and can be linked to other items from a different context.
Technically, tables of the application have 3 columns:
context: | varchar(25), contains the context code of the item. |
---|---|
common_id_property: | |
int contains a common ID share among all linked items. | |
is_main_property: | |
boolean, each group of linked items will only have one main item. |
Models of the application must implement the Twinnable behaviour.
Schema of our example table:
Let’s create a first item:
item_id | item_context | item_common_id_property | item_is_main_property | item_title |
---|---|---|---|---|
1 | main::fr_FR | 1 | 1 | Premier item |
Let’s add another item in another context, and linked to the first one:
item_id | item_context | item_common_id_property | item_is_main_property | item_title |
---|---|---|---|---|
1 | main::fr_FR | 1 | 1 | Premier item |
2 | main::en_GB | 1 | 0 | First item |
Let’s see how it looks after a few more addition:
item_id | item_context | item_common_id_property | item_is_main_property | item_title |
---|---|---|---|---|
1 | main::fr_FR | 1 | 1 | Premier item |
2 | main::en_GB | 1 | 0 | First item |
3 | main::en_GB | 3 | 1 | Second item |
4 | main::fr_FR | 3 | 0 | Second item (fr) |
5 | event::fr_FR | 5 | 1 | Item du site event |
6 | main::es_ES | 1 | 0 | First item (es) |
Let’s delete the item with ID 1 :
item_id | item_context | item_common_id_property | item_is_main_property | item_title |
---|---|---|---|---|
2 | main::en_GB | 1 | 1 | First item |
3 | main::en_GB | 3 | 1 | Second item |
4 | main::fr_FR | 3 | 0 | Second item |
5 | event::fr_FR | 5 | 1 | Item du site vent |
6 | main::es_ES | 1 | 0 | First item |
The item with ID 2 now becomes the main item, but the item_common_id_property has not changed.
The media centre is the central place which gather most of the files used by the applications. It contains images, documents, videos or any other file.
When accessing a media for the first time, the 404 handle is invoked. It creates a symbolic link in the public/media directory, so subsequent requests don’t need to use the 404 handler anymore.
It works this way because we’ll be able to handle private medias in the future. The latter will return:
In the case of transformed images, the process is similar with a additional step: storing of the transformed image in the directory local/cache/media/.
When PHP sends a big file on the ouput, it blocks the process until the transfer is completely done. But it’s possible to release the process instantaneously by delegating the work to the underlying web server (usually Apache or nginx).
The XSendfile mechanisme can be used. It consists of sending a special header from the PHP script, its name can vary upon one server or another:
See also
You may not want to store all your files in the media centre. For instance, a ‘human resources’ application collects CV of candidates, and you don’t want them to be visible in the media centre.
There’s an associated file mechanism to handle this case. It works roughly the same as email attachments and allows to store a CV with its candidates data.
Permissions are always applied to a role. Then, each user is given one or many roles, from which the permissions are determined.
For the sake of simplicity and understanding, on a default installation:
Hence the role notion is completely hidden and the permissions are configured on a dedicated tab of the user form :
Meanwhile it’s possible to enable multiple roles in the main configuration file : novius-os.users.enable_roles = true.
Once enable, it becomes possible to share a role among several users. Permissions can then be configured more precisely:
They are two types of permission:
A simple permission has a meaning just by itself. For instance “Can I add a page?” or “Can I delete a locked page?”.
A multiple permissions don’t have a meaning just by themselves, and can only be be expressed depending on a category (value). For instance with “Can I write in this folder?” we need a list of folders for the permission to have a meaning. With “Can I access this application?” we need a list of applications.
A simple permission is composed of a unique perm_name column, whereas a multiple role (using categories) is composed of two columns: perm_name (the same on as a simple permission) and perm_category_key (the value).
By creating this file, each application can define its own list of permissions it needs.
They can be configured in the dedicated column when editing the permissions :
<?php
// Simple: only 1 argument, the permission name
\Nos\User\Permission::check('noviusos_app::delete_locked');
// Multiple (uses categories) : 2 arguments, the permission name + the category key
\Nos\User\Permission::check('noviusos_app::create_in_folder', $folder_id);
// Handling acces level (advanced, useful when combined with multiple roles) : 2 arguments, the permission name + the level (numeric)
\Nos\User\Permission::atLeast('noviusos_app::level', '2_moderator');
See also
API documentation of the Permission class.
Warning
The permission name is an important thing. The part preceding :: must refer to an valid application. For the permission to be granted, the user also needs to have access to this application.
It’s possible to hide some fields based on the permissions. To do so, the show_when key defines a callback function returning whether the field should be visible or not.
<?php
return array(
'fields' => array(
'my_field' => array(
'label' => 'My field',
'form' => array(
'type' => 'text',
),
'show_when' => function() {
// The field will only be visible when the user has the requested permission
return Permission::check('my_app::my_permission');
},
),
),
);
It’s possible to disabled actions based on the permissions using the disabled key.
<?php
return array(
'data_mapping' => array(/*...*/),
'actions' => array(
'delete' => array(
'label' => __('Delete'),
'primary' => false,
'icon' => 'home',
'action' => array(/*...*/),
'targets' => array(
'grid' => true,
),
'disabled' => array(
function($item) {
return !Permission::check('my_app::can_delete_item') ? __('You don\'t have the permission to delete items.') : false;
}
),
),
),
);
Novius OS is using Apache mod_rewrite (or any equivalent on other servers) to display pages in the front-office.
Every URL ending with .html, the home page and the folders are redirected to the NOSROOT/public/htdocs/novius-os/front.php file.
This file loads Novius OS and asks the Controller_Front to handle the URL.
The Controller_Front parses the URL and figure out the associated cache file path. From there, several things can happen.
When the cache file associated to the current URL exists.
The cache is saved in the NOSROOT/local/cache/page/ directory. First level of the tree is the domain name. Below, the URL path is used.
The cache file is a PHP file. First lines are responsible to check whether the cache file is still valid or it has expired. It also has a way to recreate the Controller_Front properties which were available when the cache was generated (page instance, URLs, status, headers, custom data).
If it’s still valid, the cache is executed and display the page to the user, with the saved status and headers.
When the cache file associated to the current URL doesn’t exists or has expired.
The Controller_Front will find the appropriate page based on the URL. Once the page is known, it can determine the associated template and the WYSIWYG list to insert into the template.
When WYSIWYG contains enhancer, they are executed and their content is saved.
Then the template (it’s a View) is executed with the following data: $wysiwyg array, the page $title, the $page and the Controller_Front ($main_controller).
The generated content is saved into the cache file.
Once the cache is generated, it’s being executed (see above section) to send the content to the user, with the status and headers specified during the cache generation (especially by the enhancers).
During the process Controller_Front triggers several events at key locations. By using them, it’s possible to alter the process.
See also
You also have control on the process by using the Controller_Front methods. You can retrieve the front controller instance using NosNos::main_controller(), or just use $this->main_controller inside an enhancer.
In some situations, you may want to generate your own content and skip the page template. For instance, an enhancer can send a RSS feed. It’s done using the sendContent() method of the Controller_Front.
Below is an example (enhancer code):
<?php
$this->main_controller->setHeader('Content-Type', 'application/xml');
$this->main_controller->setCacheDuration(60 * 30); // Cache duration is set to 30min
return $this->main_controller->sendContent($rss); // The $rss variable contains the RSS feed (XML content)
The cache file will only contains the RSS feed content and the HTTP response will contain a header with the correct content-type.
In some situations, the caching system is too much effective. For instance, if a portion of the template or of the enhancer should be different depending on whether the user is logged in or not. In this situation, it’s useful to tell the cache to execute a PHP code each time rather than saving its result.
To do so, the FrontCache provides the callHmvcUncached() and viewForgeUncached methods.
<?php
// This will execute a controller's action each time the cache is executed.
\Nos\FrontCache::callHmvcUncached(
'uri/controller',
array(
'id' => \My_User::get_current_user_id() // Just as an example, the My_User class doesn't really exists
)
);
// or
// This will include the view each time the cache is executed (rather than saving its result)
\Nos\FrontCache::viewForgeUncached(
'uri/view', // View path
array(
'id' => \My_User::get_current_user_id()
),
false
);
You can configure the cache path to also vary based upon any parameter, in addition to the current URL. For instance, different GET parameters doesn’t change the cache path (the same file is used for the same URL, with or without GET).
To do so, use the addCacheSuffixHandler() method from the Controller_Front.
<?php
\Nos\Nos::main_controller()->addCacheSuffixHandler(array(
array(
'type' => 'GET',
'keys' => array('my_param'),
),
));
// or
// The callback function must return a string (empty string when you don't want to alter the cache path)
\Nos\Nos::main_controller()->addCacheSuffixHandler(array(
array(
'type' => 'callable',
'callable' => array('MyClasse', 'myMethod'),
'args' => array(
'example arg'
),
),
));
In the first example, the cache system will generate one cache files for each different value of the GET[my_param] variable.
In the second example, the cache system will call the MyClasse::myMethod('example arg') method, which is responsible to return a suffix to the file path if necessary.
Novius OS’ GitHub account is a good place to start.
Check out the contributors’ page on Novius OS website for applications from the community.
You could also go straight to Fumito Mizuno and Novius Agency’s GitHub accounts which feature many apps.
2 solutions are available: using Git or a .zip file.
On GitHub, copy the repository URL:
Then, clone the repository in the /local/applications/ directory.
cd local/applications
git clone REPOSITORY_URL
Lastly, don’t forget to activate the application in the applications manager.
On GitHub, download the application as a .zip file:
Unzip the downloaded archive in the /local/applications/ directory.
Rename the created directory in order to delete the branch name (which is automatically added by GitHub). For example, you will rename novius_ftplite-master-dubrovka into novius_ftplite only.
Lastly, don’t forget to activate the application in the applications manager.
Warning
Don’t modify the actual files inside the application you just downloaded, or you won’t be able to update it later! Please use the extensions mechanisms in order to change how it behaves.
Open the applications manager (from the desktop) :
Clic on « Install » next to the name of your application.
Go into the directory containing your application, and update the repository in the desired version:
cd local/applications/novius_ftplite
git fetch
git checkout master/dubrovka
Then, go in the applications manager to « Apply changes ».
Note
Before updating an application, check that your (potential) specific developmens are compatibles.
On GitHub, download the new version of the application as a .zip file.
Then, replace the corresponding directory in local/applications (you can delete the old one to put the new one).
As when installing, don’t forget to rename the directory in order to delete the branch name (which is automatically added by GitHub).
Then, go in the applications manager to « Apply changes ».
With Novius OS, you can manage several sites and/or languages in the same back-office. We call ‘context’ a site / language pair.
See also
You can define the contexts at any time, even after the site was launched. Just change the contexts.config.php file.
See also
All segments of URLs builded in Novius OS are cleaned by the friendly slug mechanism.
By default:
But you can use others rules or define your own rule. You can also have special rules for contexts.
Four setups of rules are defined:
A sample configuration file is available in local/config/friendly_slug.config.php.sample. Just rename (or copy) it to local/config/friendly_slug.config.php, and update it to your case.
To change the default setup of rules:
<?php
return array(
'active_setup' => 'my_default',
'setups' => array(
'my_default' => array(
// Use the 'no_accent' setup
'no_accent',
// Replace space by '_'
' ' => '_',
// All characters that are not a word character, a '-' or a '_' or a '*' are replaced by '-'.
'[^\w\*\-_]' => array('replacement' => '-', 'flags' => 'i'),
),
),
);
To define specific rules to context, define a new key equal to the context ID in setups array.
<?php
return array(
'setups' => array(
'main::en_GB' => array(
//... Set here your specific rules for context main::en_GB
),
),
);
See also
You can send Novius OS on a production server using many way:
However, regardless the method you choose, you will have to change few configuration settings to improve optimization.
The first step is to change the Fuel environment (stored in Fuel::$env). This will automatically adapt few settings such as cache length or logs level. The FuelPHP website explains how to change this environment.
You can do it by changing SetEnv in the Apache configuration.
SetEnv FUEL_ENV production
// or
SetEnv NOS_ENV production
You need to add the production key into local/config/db.config.php. The configuration can be quite similar than the one of development; if you installed the instance on the production server, you just have to rename the development key to production. This is very well documented in the FuelPHP website.
Cache duration is adapted if the environment is set to production. You can however customize it by changing local/config/config.php file.
return array(
'novius-os' => array(
'cache' => true,
// When on production environment, durations are 3600 seconds by default
'cache_duration_page' => 3600, // page cache duration
'cache_duration_function' => 3600, // custom (applications) cache duration
'cache_model_properties' => false, // does Novius OS store model properties into cache. Applies only to
// models where properties where not defined
),
);
If need your Novius OS instance to send emails, you have to rename the file local/config/email.config.php.sample to local/config/email.config.php. Configuration details are very well explained on the FuelPHP website.
MVC stands for Model-View-Controller.
MVC is an approach to separating your code depending on what role it plays. Basically, a request is handled by the controller. It retrieves data using models. Then, it decides what view to use to display the data to your visitors.
See also
See also
Every classes follows the same precise naming convention:
For instance, the classes/controller/admin/login.php file contains the class named Controller_Admin_Login.
PHP classes are placed in the classes directory.
Controllers classes are found in the classes/controller directory.
Models classes belongs in the classes/model directory.
An ORM does 2 things:
FuelPHP’s ORM uses the Active Record pattern.
The following links from the FuelPHP’s documentation will help you:
See also
The application wizard allows you to easily create a new application : Models, Fields and Field groups, App Desk, Launchers, URL enhancers...
The application wizard allows you to skip repetitive tasks and focus on what is important.
Warning
The last step of the application wizard is table and files generation. This files will be in a new folder (which name you have chosen) is local/application/. Therefore, Novius OS (user Apache if Novius OS runs on a Apache) has to have write rights on this folder.
Metadata of an enhancer are described in the API documentation.
In order to manage the configuration popup as well as the enhancer’s preview, we need a controller.
Create the file my_app::classes/controller/admin/enhancer.ctrl.php extending Controller_Admin_Enhancer.
<?php
namespace My\App;
class Controller_Admin_Enhancer extends \Nos\Controller_Admin_Enhancer
{
}
As most Novius OS controllers, we can add a configuration file: my_app::config/controller/admin/enhancer.config.php
<?php
return array(
// Empty, for the moment
);
Popup display depends on the dialog key in the metadata.config.php file.
<?php
return array(
'enhancers' => array(
'my_app' => array(
'dialog' => array(
'contentUrl' => 'admin/my_app/enhancer/popup',
'ajax' => true,
),
),
),
);
Here, the popup will call the function action_popup() of the Mon\Appli\Controller_Admin_Enhancer class using ajax.
As any change on a metadata.config.php file, you need to apply changes in the application manager.
From now on, when we add an enhancer into a WYSIWYG, a popup appears, but the configuration form is empty.
Standard controller we extended expects a configuration in order to add options into the popup. In the mon_appli::config/controller/admin/enhancer.config.php file:
<?php
return array(
// Popup configuration options
'fields' => array(
'item_per_page' => array(
'label' => __('Item per page:'),
'form' => array(
'type' => 'text',
'value' => 10, // This is only the default
),
),
),
);
The fields syntax is identical to the CRUD configuration, with the ability to use renderers.
When you configure only the fields without specifying popup.layout, the controller will add a default layout for you automatically:
<?php
return array(
// Automatically generated by the controller when it finds a 'fields' configuration
'popup' => array(
'layout' => array(
'fields' => array(
'view' => 'nos::form/fields',
'params' => array(
'fields' => array(/* List of all the specified 'fields' */),
'begin' => ' ',
'end' => ' ',
),
),
),
),
);
If you want to change this default layout (for example, to add a 2nd view and include JavaScript), you must fill it entirely (including the nos::form/fields if you also need it).
Within the popup views, the following variables are accessibles :
The preview added into the WYSIWYG is loaded by calling previewUrl key in the metadata.config.php configuration file.
Generally, the same controller than the popup is called, only the action is changed to action_preview().
View provided by default uses an icon, a title (default value is the 64x64 application icon, and the enhancer title), as well as a layout (additional view files called).
my_application::config/controller/admin/enhancer.config.php :
<?php
return array(
// Popup configuration
'fields' => array(
// Already dealt with in previous part
),
// Preview configuration
'preview' => array(
// (optional) view to be used in order to render (default value is written beneath):
//'view' => 'nos::admin/enhancer/preview',
// (optional) additional view files (included par la view au-dessus)
//'layout' => array(),
'params' => array(
// (optional) default value is enhancer title
'title' => "Mon super enhancer",
// 'icon' (optional) default value is application 64x64 icon
),
),
);
Value can be callback functions for title and icon. This callback will receive only one parameter: the enhancer configuration.
One example is the « Form » enhancer where the selected form title is also displayed.
Once the page has been save and published, the enhancer will appear on the website.
The content will be generated by the controller defined on keys enhancer or urlEnhancer of the metadata.config.php file (whether we wanted a simple or URL enhancer). Don’t forget to apply changes in the application manager if you change metadata.config.php.
For instance, the value of the enhancer key for the « Form » application is noviusos_form/front/main, so it will call the action_main() method of Controller_Front of the noviusos_form application ( Nos\Form\Controller_Front class).
The first parameter of this action is the configuration table defined by user on the configuration popup.
Create a controller in my_app::controller/front.ctrl.php
<?php
namespace My\App;
class Controller_Front extends \Nos\Controller_Front_Application
{
public function action_main($enhancer_args = array())
{
// Testing
return print_r($enhancer_args, true);
}
}
URL enhancer are capable of managing URLs.
Concretely, if your URL enhancer has been added to the my/page page, then it will be able to manage urls begining with my/page/**.html, as:
There is no limitation on the level you can manage.
As on previous part, content is generated by the main action, but it is possible to get the extended url with $this->main_controller->getEnhancerUrl();.
The controller can therefore switch content depending the called URL. Here is an (simplified) example taken from the « Blog » application :
<?php
namespace Nos\Blog;
class Controller_Front extends \Nos\Controller_Front_Application
{
public function action_main($enhancer_args = array())
{
// Complete url == 'my/blog/category/ski.html'
// => $enhancer_url == 'category/ski' (without .html)
$enhancer_url = $this->main_controller->getEnhancerUrl();
$segments = explode('/', $enhancer_url);
if (empty($enhancer_url))
{
// URL is 'mon/blog.html' (page URL)
// Display list of blog posts (first page)
}
else if (count($segments) == 1)
{
// URL is 'mon/blog/blog_title.html'
// Blog post 'blog_title' is displayed
}
else if (count($segments) == 2)
{
if ($segments[0] == 'page')
{
// URL is 'my/blog/page/number.html'
$page = $segments[1];
// Display page number of blog posts list
}
else if ($segments[0] == 'category')
{
// URL is 'my/blog/category/category_name.html'
$category = $segments[1];
// Display blog posts list of 'ski' category
}
}
// URL called isn't managed by the enhancer (404 error)
throw new \Nos\NotFoundException();
}
}
When an enhancer manage URLs for some models (ORM), it must know the mapping between models and URLs. This is allowed by the static method getUrlEnhanced():
<?php
namespace Nos\Blog;
class Controller_Front extends \Nos\Controller_Front_Application
{
public static function getUrlEnhanced($params = array())
{
$item = \Arr::get($params, 'item', false);
if ($item) {
$model = get_class($item);
$page = isset($params['page']) ? $params['page'] : 1;
switch ($model)
{
// Blog post URL
case 'Nos\Blog\Model_Post' :
return urlencode($item->virtual_name()).'.html';
break;
// Category URL
case 'Nos\Blog\Model_Category' :
return 'category/'.urlencode($item->virtual_name()).($page > 1 ? '/'.$page : '').'.html';
break;
}
}
return false;
}
}
This function is related to the Behaviour_URLenhancer and url() and urls() model methods. In order to understand how to configure them, take a look at the API documentation.
Example :
<?php
// Selecting the category which ID value is 1
$category = Nos\Blog\Model_Category::find(1);
$url = $category->url(array('page' => 2));
$url value will be my/blog/category/ski/2.html:
Template metadata are described in the API documentation.
File location depends on the file key configured in the metadata.config.php file.
Inside the template, some variable can be accessed:
$wysiwyg: | A hash which keys are the WYSIWYG name configured in the metadata.config.php file and values are content the user entered. |
---|---|
$page: | Nos\model_Page instance. |
$main_controller: | |
Front controller instance. |
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="shortcut icon" href="static/favicon.ico" />
<link rel="stylesheet" type="text/css" href="static/apps/noviusos_templates_basic/css/base.css" media="all">
</head>
<body>
<header>This header will be displayed on all pages configured to use this template.</header>
<div id="menu">
</div>
<div id="content">
<?= $wysiwyg['content']; ?>
</div>
</body>
</html>
See also
/app_extend/add_field
Most fields added need a column in the model associated MYSQL table.
Fields are then added in the CRUD form using the fields key in the configuration file.
Syntax used is using a existing feature, which defines how a column displays.
Moreover, Novius OS team implemented renderers, which allows more freedom. Some renderer allow to select medias, pages, date.
Configuration example:
<?php
return array(
'name' => array(
'label' => 'Text displayed next to field',
'form' => array(
'type' => 'text',
'value' => 'Default field',
),
'validation' => array(),
);
Bold text is the type property value:
<?php
return array(
'gender' => array(
'label' => 'Gender',
'form' => array(
'type' => 'select',
'options' => array(
'm' => 'Male',
'f' => 'Female',
),
),
'validation' => array('required'),
),
);
tag property can be used to force HTML tab, for the submit button case.
FuelPHP use automatically value as button text.
<?php
return array(
'save' => array(
'form' => array(
'type' => 'submit',
'tag' => 'button',
'value' => 'Save',
),
),
);
New in version Chiba2.1.
The save key no longer required in CRUD fields configuration.
renderers list is available in API documentation.
It is actually quite simple. You need to define two special keys in data_mapping:
In the file config/common/item.config.php:
<?php
return array(
'data_mapping' => array(
'thumbnail' => array(
'value' => function ($item) {
foreach ($item->medias as $media) {
return $media->get_public_path_resized(64, 64);
}
return false;
},
),
'thumbnailAlternate' => array(
'value' => function ($item) {
return 'static/apps/mon_appli/icons/64.png';
}
),
),
);
You need then to enable the thumbnails view in the App Desk configuration my_app::config/controller/admin/appdesk.config.php :
<?php
return array(
'model' => '',
'query' => array(),
'inspectors' => array(),
'i18n' => array(),
'thumbnails' => true,
);
If you want to show the thumbnails view by default:
<?php
return array(
'model' => '',
'query' => array(),
'inspectors' => array(),
'i18n' => array(),
'thumbnails' => true,
'appdesk' => array(
'appdesk' => array(
'defaultView' => 'thumbnails',
),
),
);
You have added thumbnails to your model. Now you want to displaying them in front-office.
In list mode, you want to displaying them cropped to 150x150 pixels and in grayscale.
In your list view:
<?php
foreach ($items as $item) {
echo $item->thumbnail->getToolkitImage()->crop_resize(150, 150)->grayscale()->html(array(
'style' => 'float:right;'
));
echo '<h2><a href="', $item->url(), '">', e($item->title), '</a></h2>';
}
In item page, you want to display the thumbnail with a max width of 300 pixels and a max height of 200 pixels, and a rotation of 15 degrees.
In your item’s view:
<?php
echo $item->thumbnail->getToolkitImage()->shrink(300, 200)->rotate(15)->html();
echo '<h1>', e($item->title), '</h1>';
echo '<p>', e($item->description), '</p>';
If your application is Twinnable, you may want to have some fields of a model common to all contexts. For example, in Monkey application, the birth year, species and photo of a monkey are not context dependants. A monkey in french version will have the same birth year, the same species and the same photo that in english version.
See also
For define a common field, just adds it to common_fields key of Twinnable behaviour.
<?php
class Model_Page extends \Nos\Orm\Model
{
protected static $_behaviours = array(
'Nos\Orm_Behaviour_Twinnable' => array(
'context_property' => 'monk_context',
'common_id_property' => 'monk_context_common_id',
'is_main_property' => 'monk_context_is_main',
'common_fields' => array('monk_species_common_id', 'monk_birth_year'),
),
);
}
For define a common media or WYSIWYG, just add it to the right variable of the model.
shared_medias_context: | |
---|---|
Common medias array. | |
shared_wysiwygs_context: | |
Common WYSIWYGs array. |
Common medias and WYSIWYGs can be use like classic medias and WYSIWYGs, with accessors medias and wysiwygs of the model.
See also
<?php
class Model_Monkey extends \Nos\Orm\Model
{
// ...
public static $shared_medias_context = array(
'thumbnail',
);
public static $shared_wysiwygs_context = array(
);
}
The class Attachment allows you to manage attachments.
Attachments are saved in the local/data/files/ directory.
Usually attachments are joined to a Model but it is possible to manage them as you wish. You just need a tiny configuration in order to define an Attachment.
<?php
$attachment = \Nos\Attachment::forge('my_id', array(
'dir' => 'apps'.DS.'myapps',
));
On above example, attachment will be saved in the local/data/files/apps/myapps/my_id/ directory.
In order to save a file, you will just need:
$attachment->set($_FILES['file']['tmp_name'], $_FILES['file']['name']);
$attachment->save();
On above example, we save an uploaded file as an attachment. File location will be: local/data/files/apps/myapps/my_id/original_name.ext where original_name.ext is the original name of the uploaded file collected from $_FILES['file']['name'].
In the case a file is joined to a Model, it is even simpler. In your class, you just need to write:
class Model_Example extends \Nos\Orm\Model
{
protected static $_attachment = array(
'avatar' => array(),
'document' => array(),
);
This way, each Model_Example item will have two attachment: avatar and document.
$item = Model_Example::find('first');
$item->avatar->set($_FILES['file']['tmp_name'], $_FILES['file']['name']);
$item->avatar->save();
For more details, check Attachment.
When you create a new Attachment, you can specify a list of authorized extensions by adding the extensions key to the configuration. Value should be an array of authorized extensions.
If your file has to be an image, you can set the special key image to true.
By default, your attachment will be available in this URL:
http://www.domain.com/data/files/dir/id/file_name.extension
If dir value is, as often, apps/my_app/my_file_type/, the url can be quite extensive.
Define an alias class in your Attachment configuration. alias value will replace dir value in URL.
It is possible to secure your attachments, in order to limit access only for authenticated user for example. You just need to define on configuration the check key which value is a fonction de callback. Each time file is requested, the system will execute this function, with first parameter the current Attachment instance, in order to check the file is available in this context.
Example:
class Verification
{
public static function check($attachment)
{
return isset($_SESSION['user_connected']) && $_SESSION['user_connected'];
}
}
$attachment = \Nos\Attachment::forge('my_id', array(
'dir' => 'apps'.DS.'myapps',
'check' => array('Verification', 'check'),
));
In the upper example, if the user is logged (session key user_connected set to true), the file will be available. If not, the url will throw a 404 error.
Each application possess the lang directory where are located translation files, that will be called dictionaries.
This directory include as many subdirectories as languages we want to translate the application into. Dictionaries will be define in these subdirectories.
These language directories are named following locales, as for example fr (French), or en (English).
Dictionaries are PHP files returning a array, similarly as configuration file.
See also
metadata are a particular case, since they are cached. They need a translation file on their own. First step is to define which dictionaries metadata need to be translated.
<?php
return array(
'name' => 'My app',
'namespace' => 'My\App',
'i18n_file' => 'my_app::metadata',
// ... other keys
);
As all change on metadata, don’t forget to apply changes in the application manager.
Next, you need to create the my_app::lang/fr/metadata.lang.php dictionary:
<?php
return array(
'My app' => 'My application',
);
Novius OS automatically knows which keys has to be translated in the metadata file and will get corresponding translations.
Elsewhere, you need to use the __() function, which will retrieve (by default) the translations from the my_app::default dictionary.
<?php
// Translation will be retrieve from my_app::lang/<lang>/default.lang.php
__('Translate this');
If you don’t want to put all your translations in the default.lang.php file, you can configure in which dictionary the translations will be retrieved, in each file which uses the __() function.
It is quite simple for view and configuration files:
<?php
// Configure the __() function
Nos\I18n::current_dictionary('my_app::common');
__('Translate this'); // Translation will be collected from my_app::lang/<lang>/common.lang.php
It is a little more complicated for admin controllers, because language depends on the user and is known only after authentication, which happens in before().
prepare_i18n() has been implemented to solve this problem:
<?php
namespace Nos\Form;
class Controller_Admin_Form extends \Nos\Controller_Admin_Crud
{
public function prepare_i18n()
{
// Configure language file depending on user
parent::prepare_i18n();
// Configure the __() function
\Nos\I18n::current_dictionary('noviusos_form::common');
}
// Other methods using __()
}
It is possible to use many dictionaries in only one file ; just use an array instead of a string. Translation will be choose from the first file containing required key.
<?php
Nos\I18n::current_dictionary(array('my_app::dictionary', 'my_app::common'));
// Translation will be collected from my_app::lang/<lang>/dictionary.lang.php if it exists
// Otherwise in my_app::lang/<lang>/common.lang.php
__('Translate this');
Each application as well as the local folder can have a migrations folder. This folder contains migrations files that are a convenient way to update the database or files. The FuelPHP migration system is used. However on Novius OS there are two things you should know:
When an application is installed or updated, migration files are executed (if they haven’t been already) through the application manager.
Creating a behaviour allows add a same set of functionalities among several models, by sharing the same reusable code base.
Behaviours are an extension to the Observers mechanism existing in FuelPHP. They allow (the same way as Observers do) to listen the before_* and after_* notifications, triggered by FuelPHP.
They allow two more functionalities:
Here are some examples to better understand:
More concretely:
To create a behaviour, you need to create a class that extends Nos\Orm_Behaviour, and implementing the following:
You can add any class as a behaviour by adding its full classname to the $_behaviours property of your model.
Exactly like FuelPHP’s observers, models can use a configuration array when defining their behaviours. This configuration will be available in the $this->_properties variable from inside the behaviour.
<?php
class Model_Monkey extends Nos\Orm\Model
{
protected static $_behaviours = array(
'My_Behaviour' => array(
'my_key' => 'my_value',
),
);
}
<?php
class My_Behaviour extends Nos\Behaviour
{
/*
* In any method, $this->_properties will contain :
* array(
* 'my_key' => 'my_value',
* )
*/
}
These are the before_* and after_* events.
For example, the Behaviour_Author stores the ID of the users who created / updated an item in a column of the model, thanks to (respectively) the before_insert and before_save events available in FuelPHP’s ORM.
<?php
class Orm_Behaviour_Author extends Orm_Behaviour
{
public function before_insert(\Nos\Orm\Model $item)
{
$created_by_property = \Arr::get($this->_properties, 'created_by_property', null);
if ($created_by_property === null) {
return;
}
$user = \Session::user();
if (!empty($user)) {
$item->{$created_by_property} = $user->user_id;
}
}
}
In the same manner as observers do, a method named after the triggered event must be implemented.
For example, to listen to a form_processing event, we need to implement a form_processing() method.
The difference with events triggered by FuelPHP lies in the parameters send to these methods:
Observers events (before_* and after_*) have an unique $item parameter (the model instance), whereas events triggered by Novius OS can take several ones, depending on the event type.
Two types of events exists:
The list of available events (both instance and static) can be found in the API documentation.
An event is called on all Behaviour which implemented the corresponding method. The return value has no use : events use arguments passed by reference <http://php.net/manual/en/language.references.pass.php> to do their job.
Exemple with the form_processing instance event (triggered when an item is saved by the CRUD):
<?php
class My_Behaviour extends Nos\Behaviour
{
public function form_processing(Nos\Orm\Model $item, $data, &$json_repsonse)
{
// Examples:
// We fill in values to save in the item
// We add some keys in the JSON array
}
}
// For information: internally, Novius OS calls this event in the following manner:
$item->event('form_processing', array($data, &$json_response));
Example with the crudConfig static event:
<?php
class My_Behaviour extends Nos\Behaviour
{
public function crudConfig(&$config, $controller)
{
// Example:
// We add a field by modifying $config['fields']
}
}
// For information: internally, Novius OS canns this event in the following manner:
Model_Class::eventStatic('crudConfig', $config, $controller);
Exactly the same as events triggered by FuelPHP and instance events, dynamics methods are named after the method to add on the model, and take the $item as a first parameter (the model instance).
Unlike events, methods usually returns a value.
For example, the Behaviour_Contextable from Novius OS adds a get_context() methods on the model using it:
<?php
// Model file
class Model_Monkey extends Nos\Orm\Model
{
protected static $_behaviours = array(
'Orm_Behaviour_Contextable' => array(
'context_property' => 'monk_context',
),
);
}
// Behaviour file
class Orm_Behaviour_Contextable extends Nos\Behaviour
{
public function get_context(Orm\Model $item)
{
return $item->get($this->_properties['context_property']);
}
}
// Use case
$monkey = Model_Monkey::find('first');
// This methods is available, because the Model_Monkey uses the Behaviour_Contextable, which makes it available
$context = $monkey->get_context();
It’s the same as an instance method, but without the first $item parameter.
<?php
// Model file
class Model_Monkey extends Nos\Orm\Model
{
protected static $_behaviours = array(
'Orm_Behaviour_Twinnable' => array(
'context_property' => 'monk_context',
'common_id_property' => 'monk_context_common_id',
'is_main_property' => 'monk_context_is_main',
'common_fields' => array('monk_species_common_id', 'monk_birth_year'),
),
);
}
// Behaviour file
class Orm_Behaviour_Twinnable extends Nos\Behaviour
{
public function hasCommonFields()
{
$class = $this->_class;
return count($this->_properties['common_fields']) > 0 ||
static::sharedWysiwygsContext($class) > 0 ||
static::sharedMediaContext($class) > 0;
}
}
// Use case
Model_Monkey::hasCommonFields();
Any view, configuration or lang file can be changed via the local folder.
This is possible thanks to the cascading file system existing in FuelPHP and adapted for Novius OS. It’s very easy to do, because you only need to copy an existing file and change it how you like!
For an application, the file should be copied into local/config/apps/{application}/ or local/views/apps/{application}/.
To extend a file from the core of Novius OS, we’ll use local/config/apps/novius-os/ and local/views/novius-os/.
The ‘generic’ pattern is local/{section}/{application}/ with:
The noviusos_page application has a controller/admin/appdesk.config.php configuration file (so it’s located at noviusos_page::config/controller/admin/appdesk.config.php).
If we copy it into local/config/apps/noviusos_page/controller/admin/appdesk.config.php, then it will be merged automatically with the one asked by the application.
When we create a local/views/apps/noviusos_help/admin/help.view.php file, it will be used as a replacement of noviusos_help::admin/help.view.php!
To extend a file from the core, we’ll use novius-os as application name. For example, local/views/novius-os/admin/login.view.php.
Any configuration file can be altered thanks to the config|<path>. event.
It’s possible to call the View::redirect() method to replace any view file by another one.
<?php
// Replace the 'admin/help' view of the 'noviusos_help' application by the 'help' view of the 'local' directory
View::redirect('noviusos_help::admin/help', 'local::help');
To extend an application, a dedicated application can be created, which will alter how the first one works.
The second application defines its extending my_application through its metadata.config.php file:
<?php
return array(
'name' => 'Application 2',
// It's an extension application
'extends' => array(
'my_application',
);
);
Once application_2 is installed, it will be loaded at the same time than my_application is.
When an application extends another one, some automatic behaviours falls into place.
See also
Example:
application_2 extends my_application.
Configuration files of Controller and Model inside my_application can automatically be extended by application_2.
For instance, my_application has the following configuration file for Controller_Test: applications/mon_application/config/controller/test.config.php.
In application_2, if the matching file applications/application_2/config/apps/mon_application/controller/test.config.php exists, then it will be merged.
i.e. in My\Application\Controller_Test, the $config variable will contain the merge of the 2 files (the one of the extended my_application application, and also the one from application_2 which extends the first one).
Views of my_application can be replaced by application_2.
For instance, my_application have a view views/admin/help.view.php
If in application_2, if the matching file applications/application_2/views/apps/mon_application/admin/help.view.php exists, it will be used in place of that of my_application.
The bootstrap file allows you to execute php code when the website / an application is loaded. It can be placed in two locations :
It is possible to use the bootstrap to extend an application. It is there events and view redirects can be used.
We’ll start from an example to explain how it works.
Let’s add a Source field on blog posts, to allow us to fill in an external URL from where the original content was produced.
ALTER TABLE `nos_blog_post` ADD `post_source` VARCHAR(255);
2 choices:
We’re going to listen the event on the model config file.
<?php
Event::register_function('config|noviusos_blog::model/post', function(&$config) {
$config['properties']['post_source'] = array(
'default' => null,
'data_type' => 'varchar',
'null' => false,
);
});
Create the file local/config/config.php by copying local/config/config.php.sample (if necessary).
Uncomment the line (or create it) with the key cache_model_properties and set it to true:
<?php return array( //... 'novius-os' => array( //... 'cache_model_properties' => true, //... ), );
When activated, all models properties will be cached in the directory local/cache/fuelphp/model_properties/. When a column is added and not declared, the first call to get() or set() for this column will fetch the schema from the DB and update the cached properties .
Warning
This mechanism only works with the MySQL and MySQLi drivers.
See also
The addition / edition form of a blog post is defined in its CRUD configuration. To extend it, we’ll use an event!
In the local/bootstrap.php file (create it if necessary):
<?php
Event::register_function('config|noviusos_blog::controller/admin/post', function(&$config) {
// Add a 'post_source' field (type 'text')
$config['fields']['post_source'] = array(
'label' => 'Source originale :',
'form' => array(
'type' => 'text',
'placeholder' => 'http://',
),
);
// Display the field inside the form
// We create a new 'Source' expander in the right menu
$config['layout']['menu']['Source'] = array('post_source');
});
The form now contains an additional editable field, as you can see below:
For the view, let’s create the local/views/apps/noviusos_blognews/front/post/content.view.php file.
<?php
// Let's include the original file (it displays the content)
include APPPATH.'/applications/noviusos_blognews/views/front/post/content.view.php';
// And add the 'source' field right after
if (!empty($item->post_source)) {
?>
<p class="blognews_source">
<?= __('Source:') ?>
<a href="<?= htmlspecialchars($item->post_source) ?>">
<?= htmlspecialchars($item->post_source) ?>
</a>
</p>
<?php
}
We’ll start from an example to explain how it works.
On the Novius OS website, we personalised how the blog posts are displayed. Here’s how it looks like:
Default design of the ‘Blog’ application :
Personalised design on the Novius OS.org website (our goal):
Thanks to the cascading file system, we can copy the original noviusos_blognews::views/front/post/item.view.php file in our local directory: local::views/apps/noviusos_blognews/front/post/item.view.php
<div class="blognews_post blognews_post_item">
<div class="blognews_primary_information">
<?= \View::forge('noviusos_blognews::front/post/title', array('item' => $item)) ?>
<?= \View::forge('noviusos_blognews::front/post/summary', array('item' => $item)) ?>
</div>
<div class="blognews_secondary_information">
<?= \View::forge('noviusos_blognews::front/post/publication_date', array('item' => $item)) ?>
<?= \View::forge('noviusos_blognews::front/post/tags', array('item' => $item)) ?>
</div>
</div>
We deleted the thumbnail, author, categories and comment count from this view file.
The blog application allows to disable some elements from its configuration. In our situation, it’s possible for every elements we don’t want to display, expect the thumbnail.
When using this blog configuration file, it acts on both the list and the full item view, which is not really what we want (so this solution is just shown as an example).
Thanks to the cascading file system, we can copy the original noviusos_blognews::config/config.php file in our local directory: local::config/apps/noviusos_blognews/config.php
<?php
// We only keep the keys we want to alter
return array(
'categories' => array(
'show' => false,
),
'authors' => array(
'show' => false,
),
'comments' => array(
'show' => false,
),
);
We create the local::views/apps/noviusos_blognews/front/post/list.view.php file:
<?php
// We add our custom CSS file
\Nos\Nos::main_controller::addCss('static/css/blog_custom.css');
// We include the original file (which displays the post list)
include APPPATH.'applications/noviusos_blognews/views/front/post/list.view.php';
Our altered view first include a CSS file (to be created in public/static/css/blog_custom.css), then calls the original view.
It’s also possible to include the CSS file with the front.start event, but in this case, it will be included on every pages of your website, not only on the blog page.
In the local/bootstrap.php file (create it if necessary):
<?php
// This event is triggered when loading a page of the website
Event::register('front.start', function() {
\Nos\Nos::main_controller::addCss('css/blog_custom.css');
});
For the Novius OS website, we created our own templates, which are bundled with the appropriate CSS files to change how the blog is displayed.
Actions are defined in the config/common/{model}.config.php file.
The best way to proceed is to be inspired by the default existing actions of Novius OS.
Action’s configuration contains {{placeholders}}.
Replaced with PHP :
- {{model_label}}: the model name
- {{controller_base_url}}: URL of the model’s controller
Every other placeholders are replaced according to the data of the item: {{_id}} and {{_title}} in this case, but also any field defined in the data_mapping.
There are 3 possibles targets for the actions:
add action:
edit action:
delete action:
<?php
return array(
// Default ADD action
'add' => array(
'label' => __('Add {{model_label}}'),
'primary' => true,
// Opens a new tab on click
'action' => array(
'action' => 'nosTabs',
'method' => 'add',
'tab' => array(
'url' => '{{controller_base_url}}insert_update?context={{context}}',
),
),
// The ation is only be shown in the App Desk's toobar
'targets' => array(
'toolbar-grid' => true,
),
),
// default EDIT action
'edit' => array(
'label' => __('Edit'),
'primary' => true,
'icon' => 'pencil',
// Opens the item on click (will refocus the tab when existing)
'action' => array(
'action' => 'nosTabs',
'tab' => array(
'url' => "{{controller_base_url}}insert_update/{{_id}}",
'label' => '{{_title}}',
),
),
// The action is only be shown in the main grid
'targets' => array(
'grid' => true,
),
),
// Default DELETE action
'delete' => array(
'label' => __('Delete'),
'primary' => true,
'icon' => 'trash',
'red' => true,
// Opens a confirmation popup on click
'action' => array(
'action' => 'confirmationDialog',
'dialog' => array(
'contentUrl' => '{{controller_base_url}}delete/{{_id}}',
'title' => strtr($config['i18n']['deleting item title'], array(
'{{title}}' => '{{_title}}',
)),
),
),
// The action is shown both in the main grid and the edition form...
'targets' => array(
'grid' => true,
'toolbar-edit' => true,
),
// ...but not for new items!
'visible' => function($params) {
return !isset($params['item']) || !$params['item']->is_new();
},
),
);
Novius OS provides an event-base mechanisme to interact with the core.
There are 2 types of events:
<?php
// Exemple of notification event
Event::register('event_name', function($value)
{
// L'action 'event_name' s'est produite
});
<?php
// Exemple of an event which may alter data
Event::register_function('event_name', function(&$value)
{
// $value peut être modifiée
});
All events are documented in the API.
See also
It’s possible to create an External link page from the back-office to make a 301-redirect.
It’s also possible to configure 301-redirects using the ./htaccess file.
Last, it’s also possible to do it from the source code, like below:
<?php
Event::register_function('front.start', function($params)
{
// The Str class is from FuelPHP
if (Str::starts_with($params['url'], 'an-old-url'))
{
// Note: 10 == strlen('an-old-url')
$new_url = 'my-new-url'.substr($params['url'], 10);
// The Response class is from FuelPHP
Response::redirect($new_url, 'location', 301);
}
});
<?php
Event::register_function('noviusos_form::after_submission', function(&$answer, $enhancer_args)
{
foreach ($answer->fields as $field)
{
if ($field->anfi_field_type == 'email' && !empty($field->anfi_value)
{
$email = Email::forge();
$email->from('my@email.me', 'My email');
$email->to($field->anfi_value);
$email->subject('Your contact request');
// Textual email (use html_body() instead if you want to send HTML email)
$email->body('Thank you for contacting us. We received it and will answer to you soon.');
try
{
$email->send();
}
catch(\Exception $e)
{
// Could not send the email
}
}
}
});
Novius OS can now manage multiple contexts: one or several websites, each one with one or several languages
Added the Slideshow application
Added the Form application
Added the ‘Build your app’ wizard application
Added the Simple Google+ share sharer on the same level as Facebook and Twitter
Back-office can now speak french in addition to english
Consequences of the switch from multi-languages to multi-contexts
- The context configuration is done in a dedicated file (it’s not in config.php anymore). Two new keys contexts and sites exists now, in addition to locales
- Every columns lang, lang_common_id, lang_is_main of the database have been renamed with context
- The new context columns are now larger, from 5 to 25 characters
- The Translatable behaviour has been renamed Twinnable
- In the CRUD, the context notion has been replaced by environment, to prevent confusions (context_relation -> environment_relation, item_context -> item_environment)
- Every associated variables have been renamed, too
Updated vendors to the following versions:
- jQuery, updated from 1.7.2 to 1.8.2
- jQuery UI, updated from 1.8.22 to 1.8.24
- Wijmo, updated from 2.1.4 to 2.2.2
- tinyMCE, updated from 3.5.6 to 3.5.7
- FuelPHP and its packages (including email), updated from 1.2 to 1.4
Changes in the pages API:
- New class Tools_Url
- Model_Page->get_link() -> Model_Page->link()
- Model_Page->get_href() -> Model_Page->url()
- Model_Page::get_url() -> Tools_Url::page()
- Deleted Model_Page::get_url_absolute()
- Every methods now returns absolutes URLs
Merged and enhanced configuration files for app-desk, inspector and CRUD:
- New common configuration file for model-specific data
- Possibility to format a grid column from the PHP configuration (not only JavaScript).
- Possibilité de formatter une colonne d’une grid via la configuration PHP (et plus seulement en Javascript)
In Controller_Crud, the from_item was renamed to init_item and is only called for new items
New Attachment class to handle files attached to an item, without adding them to the media centre
widget have been replaced to renderers. Classes and view files were updated accordingly.
Every view and configuration file are extendable in the local/config directory from the website
Created a new controller for enhancer‘s popup in the Wysiwyg, with a default preview
The upload.disabled_extensions configuration key was moved to novius-os.upload.disabled_extensions
$page and $main_controller variables are now available within the template
The time picker renderer can be used outside a Fieldset (standalone way)
The front.start event has an additional cache_path parameter.
- The context configuration is done in a dedicated file (it’s not in config.php anymore). Two new keys contexts and sites exists now, in addition to locales
- Every columns lang, lang_common_id, lang_is_main of the database have been renamed with context
- The new context columns are now larger, from 5 to 25 characters
- The Translatable behaviour has been renamed Twinnable
- In the CRUD, the context notion has been replaced by environment, to prevent confusions (context_relation -> environment_relation, item_context -> item_environment)
- Every associated variables have been renamed, too
- Model_Page->get_link() -> Model_Page->link()
- Model_Page->get_href() -> Model_Page->url()
- Model_Page::get_url() -> Tools_Url::page()
- Deleted Model_Page::get_url_absolute()
- Every methods now returns absolutes URLs
- front.start now takes an array as argument, containing the url key
- Create the contexts.config.php (based on the sample).
- Change config.php and add the novius-os key (based on the sample).
- Search _lang, replace it with _context (i.e. page_lang => page_context)
- Search Nos\Model_, replace it with Nos\{{app}}\Model_ (i.e. Nos\Model_Page => Nos\Page\Model_Page, Nos\Model_Media => Nos\Media\Model_Media)
- Search Translatable, replace it with Contextable
- Search Model_Page->get_link(), replace it with Model_Page->link()
- Search Model_Page->get_href(), replace it with Model_Page->url()
- Search Model_Page::get_url(), replace it with Tools_Url::page()
- Search Nos\Widget_Page_Selector, replace it with Nos\Page\Renderer_Selector
- Search Nos\Widget_Media, replace it with Nos\Renderer_Media
- Search Nos\Widget_, replace it with Nos\{{Renderer_Version}}
<?php
return array(
'launcher_name' => array(
'url' => '{{your_url_here}}',
),
);
// Replace with
return array(
'launcher_name' => array(
'action' => array(
'action' => 'nosTabs',
'tab' => array(
'url' => '{{your_url_here}}',
),
),
),
);
<?php
return array(
'icons' => array(
64 => 'static/apps/noviusos_page/img/64/page.png',
32 => 'static/apps/noviusos_page/img/32/page.png',
16 => 'static/apps/noviusos_page/img/16/page.png',
),
);
<?php
return array(
'field_name' => array(
'widget' => 'Nos\Widget_Media',
'widget_options' => array(),
),
);
// À remplacer par :
return array(
'field_name' => array(
'renderer' => 'Nos\Renderer_Media',
'renderer_options' => array(),
),
);
This section relies on the hypothetical existence of a lib_agenda application.
Models have a new common configuration file which contains: * a data_mapping * a list of actions
In appdesk.config.php :
Delete the selectedView and views keys (if you only have one view with no JS config file).
Spot the main model of your appdesk (it’s in the query.model key).
Note
Pay attention to have 'hideContexts' => true, in your appdesk’s configuration if you items are not Contextable.
data_mapping is the merge of the dataset and appdesk.grid.columns keys.
<?php
// Old code of appdesk.config.php
return array(
'query' => array(
'model' => 'Lib\Agenda\Model_Event',
'order_by' => array('evt_date_begin' => 'DESC'),
'limit' => 20,
),
// ...
'dataset' => array(
'id' => 'evt_id',
'title' => 'evt_title',
'periode' => array(
'search_column' => 'evt_date_begin',
'dataType' => 'datetime',
'value' => function ($object) {
// ...
},
},
),
// ...
'appdesk' => array(
// ...
'grid' => array(
'urlJson' => 'admin/lib_agenda/appdesk/json',
'columns' => array(
'id' => array(
'headerText' => __('Id'),
'dataKey' => 'id'
),
'title' => array(
'headerText' => __('Name'),
'dataKey' => 'title'
),
'periode' => array(
'headerText' => __('Dates'),
'dataKey' => 'periode'
),
'published' => array(
'headerText' => __('Status'),
'dataKey' => 'publication_status'
),
'actions' => array(
'actions' => array('update', 'delete'),
),
),
),
// ...
),
);
<?php
// New code in appdesk.config.php
return array(
'query' => array(
'order_by' => array('evt_date_begin' => 'DESC'),
'limit' => 20,
),
// Tells what is the model, and wich 'common' file to load
'model' => 'Lib\Agenda\Model_Event',
// ...
// MOVE / MERGE the 'dataset' key into
// ...
'appdesk' => array(
// ...
// MOVE / MERGE the 'grid' key into
// ...
),
);
<?php
// Code in the new 'event.config.php' file
return array(
// Merge of 'appdesk.dataset' and 'appdesk.grid.columns'
'data_mapping' => array(
'id' => array(
'title' => __('Id'),
'column' => 'evt_id'
),
'title' => array(
'title' => __('Name'),
'column' => 'evt_title'
),
'periode' => array(
'title' => __('Dates'),
'search_column' => 'evt_date_begin',
'value' => function ($object) {
// ...
}
),
'published' => array(
'title' => __('Status'),
'method' => 'publication_status'
),
),
);
Some remarks: * headerText can be written title (easier to remember, used in the natives apps) * datakey can be written``column`` * value can still contain a callback function * method is a new option which execute a methid instand of retrieving a column
Actions of the main model (the one on the grid) must also be moved in the common file.
<?php
// Old code of appdesk.config.php
return array(
// ...
'appdesk' => array(
// ...
// MOVE the 'actions' key into 'config/common/{{model_name}}.config.php'
'actions' => array(
'edit' => array(
// ...
),
'delete' => array(
// ...
),
),
// ...
),
// ...
);
<?php
// new code in appdesk.config.php
return array(
// ...
'appdesk' => array(
// ...
// The 'actions' key is not here anymore
// ...
),
// ...
);
<?php
// New code in 'config/common/event.config.php'
return array(
'data_mapping' => array(
// ...
),
// Configuration array moved from 'appdesk.actions'
'actions' => array(
'Lib\Agenda\Model_Event.edit' => array(
// ...
),
'Lib\Agenda\Model_Event.delete' => array(
// ...
),
),
);
From the moment the common file is used, the following generic actions appear: * add * edit (and not update !) * visualise (if appropriate, i.e. if the model has the Urlrenhancer behaviour) * delete * share (if appropriate)
In our lib_agenda application, Model_Event had an update action, which now appeas twice... (because we used the wrong name update instead of edit).
So, to fix it in our lib_agenda application, we need to: * rename update to edit * Delete the edit and delete keys, since we’re doing the default processing * It’s possible to keep only the keys we need to overwrite (to change the default texts for instance...)
Notes : * In the Novius OS version used, it was needed to prefix actions by the model name, it’s no longer necessary in the final version * {{controller_base_url}} can be used in action’s URLs. In the lib_agenda application, it’s replaced by lib_agenda/admin/agenda/ * A new targets keys allows to define where actions must appear (see comments).
<?php
// Example of placeholder {{controller_base_url}} + 'targets'
array(
'Lib\Agenda\Model_Event.edit' => array(
'action' =>
'action' => 'nosTabs',
'tab' => array(
'url' => "{{controller_base_url}}insert_update/{{id}}",
'label' => __('Modifier'),
),
),
'label' => __('Modifier'),
'primary' => true,
'icon' => 'pencil',
// New key to define where this action appear
'targets' => array(
'grid' => true, // In the grid (in the last 'actions' column)
'toolbar-grid' => true, // On the appdesk, in the toolbar (previously configured using 'appdesk.button')
'toolbar-edit' => true, // On the item form (top-right corner)
),
)
);
The default configuration for the targets is like below: * grid : edit + visualise + delete * toolbar-grid : add * toolbar-edit : visualise + share + delete
Note
For now, appdesk.appdesk.buttons is still defined, and has priority over the default configuration. Given we have 2 actions ‘Add an event’ and ‘Add a category’ from 2 different models, we can’t delete it (for now).
Texts can be configured using the i18n key.
Please see the framework/config/common_i18n.config.php to get the list of possible keys.
<?php
return array(
'i18n' => array(
// Crud
'notification item added' => __('And voilà ! The page has been added.'),
'notification item deleted' => __('The page has been deleted.'),
// General errors
'notification item does not exist anymore' => __('This page doesn’t exist any more. It has been deleted.'),
'notification item not found' => __('We cannot find this page.'),
// Blank slate
'translate error parent not available in context' => __('We’re afraid this page cannot be added in {{context}} because its <a>parent</a> is not available in this context yet.'),
'translate error parent not available in language' => __('We’re afraid this page cannot be added in {{language}} because its <a>parent</a> is not available in this language yet.'),
// Deletion popup
'deleting item title' => __('Deleting the page ‘{{title}}’'),
# Delete action's labels
'deleting button 1 item' => __('Yes, delete this page'),
'deleting button N items' => __('Yes, delete these {{count}} pages'),
'1 item' => __('1 page'),
'N items' => __('{{count}} pages'),
# Keep only if the model has the behaviour Contextable
'deleting with N contexts' => __('This page exists in <strong>{{context_count}} contexts</strong>.'),
'deleting with N languages' => __('This page exists in <strong>{{language_count}} languages</strong>.'),
# Keep only if the model has the behaviours Contextable + Tree
'deleting with N contexts and N children' => __('This page exists in <strong>{{context_count}} contexts</strong> and has <strong>{{children_count}} sub-pages</strong>.'),
'deleting with N contexts and 1 child' => __('This page exists in <strong>{{context_count}} contexts</strong> and has <strong>one sub-page</strong>.'),
'deleting with N languages and N children' => __('This page exists in <strong>{{language_count}} languages</strong> and has <strong>{{children_count}} sub-pages</strong>.'),
'deleting with N languages and 1 child' => __('This page exists in <strong>{{language_count}} languages</strong> and has <strong>one sub-page</strong>.'),
# Keep only if the model has the behaviour Tree
'deleting with 1 child' => __('This page has <strong>1 sub-page</strong>.'),
'deleting with N children' => __('This page has <strong>{{children_count}} sub-pages</strong>.'),
),
);
In the 0.1 version, the inspectors are configured in 3 different places: * The appdesk.appdesk.inspectors key * The inputs key * The inspector/{model}.config.php configuration file
In the 0.2 version, inputs must be moved into their corresponding inspector/{model}.config.php file.
<?php
// Old code from appdesk.config.php
return array(
// ...
'inputs' => array(
// This input communicates with the filter for the category inspector
// We move the key (evt_cat_id) into 'input.key' and the callback function into 'input.query'
'evt_cat_id' => function($value, $query) {
// ...
},
),
// ...
);
<?php
// New code in config/controller/admin/inspector/category.config.php
return array(
// ...
'input' => array(
'key' => 'evt_cat_id',
'query' => function($value, $query) {
// ...
},
),
// ...
);
<?php
// Old code from appdesk.config.php
return array(
// ...
'appdesk' => array(
'appdesk' => array(
// ...
'inspectors' => array(
// This array must be moved in the configuration file of the inspector, under a new 'appdesk' key
'startdate' => array(
'label' => __('Start date'),
'url' => 'admin/lib_agenda/inspector/date/list',
'inputName' => 'startdate',
'vertical' => true
),
// ...
),
),
// ...
),
);
Here, the inspector doesn’t have a configuration file yet, we need to create one:
<?php
// New file config/controller/admin/inspector/date.config.php
return array(
'input' => array(
'key' => 'evt_date_begin',
// the 'qeury' keys is not needed, the date inspector will generate it automatically using the key
),
// Old 'appdesk.appdesk.inspectors.startdate'
'appdesk' => array(
'label' => __('Start date'),
),
);
The idea is to encapsulate the appdesk.appdesk.inspectors.{{inspector_name}} array inside an appdesk key of the inspector’s configuration file.
<?php
// Old code from appdesk.config.php
return array(
// ...
'inputs' => array(
// ...
'evt_published' => function($value, $query) {
// ...
},
),
// ...
'appdesk' => array(
'appdesk' => array(
// ...
'inspectors' => array(
'published' => array(
'vertical' => true,
'url' => 'admin/lib_agenda/inspector/published/list',
'inputName' => 'evt_published',
'grid' => array(
'columns' => array(
'title' => array(
'visible' => false,
'dataKey' => 'title',
),
'icon_title' => array(
'headerText' => __('Status'),
'dataKey' => 'icon_title',
),
'id' => array(
'visible' => false,
'dataKey' => 'id',
),
),
),
),
// ...
),
// ...
),
// ...
),
);
The published inspector already has its configuration file. Let’s complete it with a new appdesk key:
<?php
// New file config/controller/admin/inspector/date.config.php
return array(
'data' => array(
// ...
),
// Old 'appdesk.appdesk.inspectors.published'
'input' => array(
'key' => 'evt_published',
'query' => function($value, $query) {
// ...
},
),
// Old 'input.evt_published'
'appdesk' => array(
'vertical' => true,
'inputName' => 'evt_published',
'url' => 'admin/lib_agenda/inspector/published/list',
'grid' => array(
'columns' => array(
'title' => array(
'visible' => false,
'dataKey' => 'title',
),
'icon_title' => array(
'headerText' => __('Status'),
'dataKey' => 'icon_title',
),
'id' => array(
'visible' => false,
'dataKey' => 'id',
),
),
),
),
);
Front improvements
- Profiling is activated by default on front in the DEVELOPMENT environment.
- Setting configuration novius-os.cache by default always at true.
- Setting configurations novius-os.cache_duration_page and novius-os.cache_duration_function at 600 by default, except in PRODUCTION at 3600.
- New events: front.pageFound and front.response.
- New methods in Controller_Front: getContext, disableCaching, setCacheDuration, setStatus, setHeader, getCustomData, setCustomData, sendContent, addCacheSuffixHandler.
- Status and headers are now save in cache.
- Mechanism to adapt the cache path with suffixes, depending GET parameters or what you want (with callables).
- Mechanism to execute code when using the cache.
Models properties
- All models (core and native apps) now defines the $_properties
- Implement a cache mechanism on models properties, using FuelPHP cache. Attempt to auto-refresh when an unknown properties is called.
Migrations are now dispatched per application.
New metadata key requires which allows to define that an application requires another one.
It is now possible to use href="##..." in enhancers or templates; occurences will be replaced by href="#..." without prepending the base_url.
CRUD: When returning a string in the disabled key, it is displayed as a title. disabled and visible keys can now be simple values, callbacks or array of callbacks.
Resized images are now secured: you can’t generate a lot of thumbnails and flood the server anymore.
Permissions
- Ability to define per-application permissions with a configuration file
- New API to check permissions for a user, or a specific role
- Ability to enable multi-roles on the users with the novius-os.users.enable_roles configuration.
Few changes are needed to migrate to the next version. The new API is compatible with the old one. From this version, Novius OS handles depreciations. Here a deprecated items from Chiba 1:
Pages are now cached by default (for 10 minutes) ; it could result to unexpected behaviour for custom applications using enhancer and / or urlenhancer.
Some migration files need to be executed. If you use oil, the new command is php oil refine migrate -m as migration files are now organised differently.
Windows support (Vista and upper).
Better install wizard (UI, more tests, choose of languages)
Advanced permissions for all natives applications.
Comments application:
- Administration interface
- Emails are sent when new comments are posted, to post author and others commenters.
New in version Chiba2.1.
New in version Chiba: 2.1
New in version Chiba: 2.2
Renderer: The class NosRenderer_Date_Picker was factorized into NosRenderer_Datetime_Picker
Media: Media and folders deletions are manage by models, not by CRUD controller
i18n: In the i18n class, adding addPriorityDictionary and addPriorityMessages methods
A related application, novius_taskmanager, has been implemented in order to allow tasks management and execution from an browser.
Form: Improve layout of the answer email.
New in version Chiba: 2.3
New in version Chiba: 2.3.1
New in version Chiba: 2.3.2
If a column of dataset contains HTML, you must add a key isSafeHtml for not encode it.
<?php
return array(
'data_mapping' => array(
// ...
'column_with_html' => array(
'title' => 'Column with HTML',
'column' => 'col_html',
'isSafeHtml' => true,
),
// ...
),
);
See also
In CRUD, when updating an item, success callback function is called after save (not before), like when inserting. If you use success in your developments, check that your code is compatible with a call after save.
Now, ->url() and ->urlResized() methods return absolutes URLs. You have two choices for update your developments:
check that you don’t concatenate base_url before where you use those methods.
Add a parameter equals to false at call-time.
<?php $attachement->url(false);
See also
Migration tries to guess the context of existing comments, but if you’ve implemented comments on a non contextable model, migration won’t be able to do it. In this case, you habe to set the context manually (comm_context column of nos_comment table) if you want to see those comments in new administration interface.
If you want to revert to the previous configuration:
Extend the related configuration file noviusos_blog::config or noviusos_news::config
Edit configuration like that:
<?php return array( 'thumbnail' => array( 'front' => array( 'list' => array( 'link_to_item' => false, 'max_width' => 200. ), 'item' => array( 'link_to_fullsize' => false, ), ), ), );
All URL enhancers must implement a getUrlEnhanced() method.
Those updates are not mandatory but desirable to be able to migrate without trouble when next version is released.
Deprecated code:
<?php
public static function get_url_model($item, $params = array())
{
$model = get_class($item);
switch ($model) {
case 'A\Class':
return $item->virtual_name).'.html';
break;
}
return false;
}
Replace with:
<?php
public static function getURLEnhanced($params = array())
{
$item = \Arr::get($params, 'item', false);
if ($item) {
$model = get_class($item);
switch ($model) {
case 'A\Class':
return $item->virtual_name).'.html';
break;
}
}
return false;
}
All snake_case methods are deprecated:
See also
See also
Model_Page->link() is deprecated, use Model_Page->htmlAnchor() instead.
Warning
Model_Page->link() returns only href and target attributs, Model_Page->htmlAnchor() returns the whole HTML tag <a>.
See also
New in version Chiba: 2.1
Those updates are not mandatory but desirable to be able to migrate without trouble when next version is released.
The set_renderer_options() method is deprecated, use setRendererOptions() instead.
The parse_options() method is deprecated, use parseOptions() instead.
The Chiba 2.1 version of Slideshow application has to make some migrations in the DB. See Run the migration.
UI Integration of the Novius OS new logo
UI: The rendering is improved when switching tab before end of loading
Install: Improving messages in the step one of the install wizard for facilitate understanding of the potential problems
i18n: Add plural mechanism and implement plural translation
Renderer: Selector for pages can now send multiple pages using checkboxes
Renderer: Add a generic Renderer::renderer() method for all renderers that extended Renderer
WYSIWYG: Refactoring TinyMCE Novius OS specific features. Explode all features in plugins, much more modular.
404: allow to use novius_ftplite app to add custom robots.txt (favicon or humans.txt)
Migration: Add an incremental ID and an execution date in migration table
App manager: Buttons are disabled after the click to prevent to recall the same action two times
Blog/News: Add a specific title for an author’s posts list
Blog/News: Changing page_title and meta title for posts list of category, tag and author
Comments: The comment context can be passed by parameters in API
New in version 4.1.
Front Controller:
- New methods setItemDisplayed() and getItemDisplayed().
- setItemDisplayed() set automatically title, h1, meta_description and meta_keywords.
- setItemDisplayed() triggers the event front.setItemDisplayed.
- New setH1() method.
- setTitle(), setH1(), setMetaDescription(), setMetaKeywords() methods take a template by second parameter (the default template can be set by config). The page’s property is available in the template with a placeholder.
- The method addJavascriptInline() detects the use of tag <script>.
Appdesk:
- The search bar layout is improved
- New possible config key multiContextHide for inspectors
- Performance improved with a javascript refactoring: use of wijsplitter only if need.
- Improving the resize process.
Relation Twinnable_ManyMany: Improving of the join() method. Adding the main_context condition.
Behaviour Twinnable: Improving performance of save operation by avoiding to save twins if not needed.
Behaviour sortable: Add config key sort_twins, default to true.
Take a look of these three changelog of FuelPHP for backward compatibility notes:
Take a look of release notes of Wijmo between 2013v3.20 and 2013v1.4: http://wijmo.com/wiki/index.php/Version_Histories
The config key migrations.enabled_types.metadata is no longer supported, and method Migration::canUpdateMetadata() no longer exist. During migration, all files in local/metadata are supposed be writable.
A new event migrate.exception is triggered if a migration throws an exception. This event can stop exception propagation.
This code do nothing in Dubrovka:
<?php
\Event::register_function('config|an_application::metadata', function (&$config) {
//...
});
You must do that:
<?php
\Event::register_function('config|!an_application::metadata', function (&$config) {
//...
});
Add a leading ! on the metadata path.
Those updates are not mandatory but desirable to be able to migrate without trouble when next version is released.
The translation system of Novius OS now respect plural forms of languages. Some i18n keys of CRUD config are affected.
These keys now must contain an array of different plurals of translation, and not the translated text:
These keys are unnecessary:
Second argument can be just an array, not an array with an args key containing an array.
Deprecated code:
<?php
\Nos::hmvc('request/url/', array('args' => array($first_parameter, $second_parameter)));
Replace with:
<?php
\Nos::hmvc('request/url/', array($first_parameter, $second_parameter));
The method \Config::loadConfiguration(). Use \Config::load().
Deprecated code:
<?php
$config = \Config::loadConfiguration('application_name', 'file_name');
//or
$config = \Config::loadConfiguration('application_name::file_name');
Replace with:
<?php
$config = \Config::load('application_name::file_name', true);
The method \Nos\Application::applicationRequiredFromMetadata() is not intended to be called outside the \Nos\Application class. It will become protected in future.
You can get all applications dependencies by loading the app_dependencies metadata file.
<?php
$dependencies = \Nos\Config_Data::get('app_dependencies', array());
In metadata files, the extends key containing an array with an application key is deprecated.
The extends key must contain only an array with applications names in values.
Deprecated code:
<?php
return array(
'name' => 'Application name',
//...
'extends' => array(
'application' => 'application_name',
'extend_configuration' => false,
),
);
Replace with:
<?php
return array(
'name' => 'Application name',
//...
'extends' => array(
'application_name',
),
);
Config files extended by application extending mechanism must be defined in a subdirectory apps/application_name/
For sample, if your application A extends the sample.config.php file of the application B.
Deprecated location: local/applications/application_a/config/sample.config.php
Move to: local/applications/application_a/config/apps/application_b/sample.config.php
The use of advanced theme is deprecated, use only theme nos.
Theme nos is now an extension of advanced theme. All configuration keys starting with theme_nos_ are deprecated and should be replaced by their equivalent starting theme_advanced_.
Novius OS is ready to be translated into any language (It has already been translated into French, Japanese, Russian, Spanish and Interlingue). Here is the process to translate Novius OS into your language.
Remember we are here to help. Your contribution is very appreciated, thank you.
The best way to get started with Novius OS translation is to begin with the front-office texts (~220 words only). They shouldn’t take you more than an hour to translate. Here are the steps to follow:
Go to translate.novius-os.org.
In the projects’ list (on the right-hand side), click Novius OS latest version (Novius OS versions are released in alphabetical order). If your language is not listed on the next page, let us know, we’ll add it for you.
There are only four files to translate. No account required to suggest translations.
- Blog application: noviusos_blognews > front.po
- Forms application: noviusos_forms > front.po
- Comments application: noviusos_comments > front.po
- Core: framework > front.po
That’s it, you’re done! Many thanks for your contribution. Now that you’re started, what about translating more? That’d be fantastic. Keep reading, we tell you all you need to know about Novius OS translation.
We take copywriting and translation very seriously. We have therefore established guidelines common to all applications and languages in order to provide every Novius OS user with consistent and enjoyable copy.
The copy style guide is to be read by every translator. If it’s not available in your language then it is the first document to translate.
Note
We’re happy to have the style guide translated by a professionnal translator. This key document may indeed prove hard to localise. Contact us about this translation.
Novius OS translation server is available at translate.novius-os.org. It is powered by Pootle. Here is a quick guide to Pootle. (You may also want to have a look at Evernote’s handy tour of Pootle.)
If you prefer to translate offline (or just don’t like Pootle!), you can use OmegaT or any other CAT software which handles PO files.
Go to the version and language you want to translate (see below) and click the ‘Download’ link. Once you’re done translating, contact us and we’ll grant you the rights to upload language files.
If on translate.novius-os.org home page, your language is not listed under Languages, just contact us. We’ll add it for you.
Once your language is available on translate.novius-os.org, click it. Then click the version of Novius OS you want to translate. If you don’t know which one to choose, go for the latest version (Novius OS versions are released in alphabetical order).
You now see a list of directories. The directories starting with noviusos_ are applications. framework contains the strings from the core.
To start translating, don’t click Continue translation but a number in the Need translation column. This allows you to choose a directory. Please follow these priorities:
Everybody can suggest translations. A suggestion will be reviewed by an approved translator before being submitted. Only submitted translations are applied to Novius OS. Unreviewed or rejected suggestions stay in Pootle.
Note
You don’t even need to create an account to suggest translations. Nevertheless creating an account only takes you a minute and allows you to select your languages and avoid the CAPTCHA protection.
Being an approved translators is a great way to make a difference to the Novius OS project. Everyone can apply: Just drop us a few lines about yourself and include your Pootle username. We’ll review your application before granting you extended permissions.
Don’t translate in the dark! When you don’t understand a string, need to know the context or have any doubt, please contact us. Our job is to make translation easy. We’re happy to add notes to translator in the translation files for others to benefit from your feedback. We can also provide you with screenshots or indications to find a string in the UI of Novius OS.
When your question or comment regards a specific string, please give us the link to the string. You’ll find it the bottom right corner:
This documentation is powered by Read The Docs which uses Rich Structured Text (RST) files. This format is human-readable and therefore easier to translate.
If on this page, your language is not listed under Translations, contact us. We’ll set up the GitHub repo for you.
Once the repo is ready, clone it. You may also want to clone the English repo so you can copy files from the original version to the translation.
That’s it, you’re all set to start translating. Thank you very much for your contribution!
This style guide is intended for all persons writing copy for Novius OS: applications developers and designers, contributors to the core, translators. It establishes guidelines common to all applications and languages in order to provide every Novius OS user with consistent and enjoyable copy.
This guide is based on Aaron Walter’s design personas. MailChimp’s Voice and Tone guide, created by Walter’s team, has also been a source of inspiration.
Translation is more than just a word-to-word job. One must remain faithful to the original design while adapting it for a new audience, for a different culture. A literal translation of Novius OS copy does not make sense. The translation must read as if the copy had originally been written in the target language. The first document to translate are these guidelines so that a localised style guide is available.
Novius OS is designed for professionals. They don’t use it for fun, they have a job to do. Novius OS must show its users they share a common goal: to get the job done as efficiently as possible. Novius OS therefore adopts a professional and straight-to-the-point tone.
This said, using Novius OS doesn’t have to be boring. The usual flavourless software speech (e.g. Please enter a valid value) is to be avoided. We don’t Novius OS to be seen as yet another tool.
Novius OS can make its users smile—especially when they succeeded in carrying out their job—but not laugh. The software is not their friend. It is rather a trustworthy and knowledgeable colleague they’re happy to work with. A team of colleagues actually, as Novius OS says ‘We’ not ‘I’.
Finally, although many Novius OS followers are developers, copy must be jargon-free. What’s more, bro-ish tone (e.g Sorry dude, I screwed up!) is inappropriate.
Novius OS is written in British English. So could you please be kind enough to restrain from using the spelling ‘Media Center’ instead of the more gentrified ‘Media Centre’?
For quotation marks, we follow the rule given by Oxford Dictionnaries —single inverted commas e.g. ‘Hello world’. Besides please use apostrophes (’) not the prime symbol (‘).
Sign in: | Let’s get started! |
---|---|
Log out: | Sign out (see you!) |
Add new item: | Add a new page [1] |
Save an item: | Save [1] |
New item added: | All done! The blog post has been added. [2] |
---|---|
Item updated: | OK, changes are saved. [2] |
User mistake: | You must add a title for the product to be saved. Sorry about that! [3] |
---|---|
System error: | Something went wrong. Please try again and contact your developer or Novius OS if the problem persists. We apologise for the inconvenience caused. [4] |
Error prevention (warning): | |
Answers to this form have already been received. Modifying the form may alter the collected data. [5] | |
Error prevention (confirm button): | |
Don’t worry, I know what I’m doing. [5] |
[1] | (1, 2) No need to say more. We want clearly labelled primary actions. |
[2] | (1, 2) A little diversity is welcome as long as the style is consistent. E.g. ‘There you go!’, ‘All wrapped up!’ or ‘Great!’ to start a success message. |
[3] | Don’t assume this is the user’s fault. Maybe it wasn’t very clear the field was mandatory. |
[4] | Don’t ’Oops!’ the user. She/he might have lost time or data. |
[5] | (1, 2) When you need the user to pay attention, get her/him involved by turning the copy into a conversation. |