Novius OS documentation

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.

Table of contents

Install Novius OS

Installation

General requirements

Have a serevr with MySQL and PHP 5.3+.

Novius OS can run with:

  • Linux, Mac OS or Windows (from Vista)
  • Apache with mod_rewrite enabled or Nginx
LAMP

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
    
Quick installation
Requirements
  • Have a command line access to the server and being granted sudo admin rights.
  • Git is installed.
Installation

Open a terminal and enter:

cd /var/www
sudo wget http://raw.github.com/novius-os/ci/master/elche/tools/install.sh && sh install.sh

Once the installation completes:

Note

  • For a local installation, the URL is probably http://localhost/novius-os/ .
  • If your server’s DOCUMENT_ROOT isn’t /var/www/, change the first command accordingly.
Installation via Zip file

We recommend you follow this procedure when installing Novius OS on shared hosting:

Advanced installation
Configure a Virtual Host

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
Replace nano with any text editor.
Replace novius-os.conf with the name you want for your Virtual Host.
Copy the following configuration in the file you just opened and save.
Change the line ServerName with your domain name when installing on a live server.
Likewise, change /var/www/novius-os with the folder you installed Novius OS into.
<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
Configure the hosts file, when installing on your computer

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
Advance installation with Git

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 :

  • novius-os : Novius OS core, which contains submodules itself (like fuel-core or fuel-orm).
  • Several submodules in local/applications: blog, news, comments, form, slideshow and other applications.
The repository default branch is latest stable version of Novius OS.
New versions will be made available in new branches.
For now, every dependent repository of novius-os/novius-os share the exact same version number. It means that any application available on our Github exists in the same versions as the core. So if you’re using the 5.0.1 version of novius-os/core, then you should also use ``novius-os/app in the same 5.0.1 version number.
After the initial clone, if you want to change the Novius OS version you’re using, don’t forget to update the submodules!
Here’s how to use the latest nightly (it’s in the dev branch):
cd /var/www/novius-os/
git checkout dev
git submodule update --recursive
Installation on Nginx server

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;
 }
}

Setup wizard

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 :-).

Step 1: Checking requirements

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.

Step 1a

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!

Step 1b
Step 2: Setting up the MySQL database

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.

Step 3: Creating the first administrator account

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.

Step 4: Finishing the installation
Step 4

Warning

We urge you to follow the recommendation of this page.

On setting contexts, refer to the principles and API documentation.

Applications

You’re redirected to the applications manager. Install the applications you need.

Applications manager
Signing in and out

To sign out, click your name in the top-right corner. A menu shows:

Signing out

You’re redirected to the sign-in form.

Signing in

Common Installation Problems

Htaccess not allowed on Apache server
Symptoms
  • You have a message Your server does not allow .htaccess file
  • You run Novius Os on an Apache server
  • You’ve installed Novius OS in a subfolder of a host, classically the default host
Workaround
  • Find the virtualhost config file, commonly in /etc/apache2/site-enabled/.
  • Edit the virtualhost config file with write permission.

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
Write permissions on Windows system
Symptoms
  • You’ve installed Novius OS on Windows
  • You have messages beginning like Give write permission to all users
Workaround

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.

Write permissions with FTP
Symptoms
  • You’ve installed Novius Os by uploading it by FTP
  • You have messages saying that some directories must be writeable
  • You can not execute commands given, you can’t access server by ssh
Workaround

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.

GD installation on Ubuntu
Symptoms
  • You’ve message saying that GD is required
  • You run Novius OS on Ubuntu
Workaround
sudo apt-get install php5-gd
sudo apt-get install libgd2-xpm-dev*
Json extension not installed
Symptoms
  • You’ve message saying that Call to undefined function json_encode() or Call to undefined function json_decode()

Some distributions have removed the standard JSON extension as of PHP 5.5rc2 due to a license conflict.

Workaround
sudo apt-get install php5-json
Forbidden when access back-office
Symptoms
  • After install wizard, when you try to access to back-office, your browser send you a page saying Forbidden

This problem exists for Web hoster Infomaniak.ch

Workaround

Edit .htaccess file. Change this line:

Options +FollowSymLinks -Indexes

By:

Options +FollowSymlinks -SymlinksIfOwnerMatch -Indexes
magic_quotes_gpc must be off
Symptoms
  • You have the message saying PHP configuration directive ‘magic_quotes_gpc’ must be off
  • You’ve use OVH Web hosting
Workaround

Add this line in the .htaccess file:

SetEnv MAGIC_QUOTES 0
404 during install
Symptoms
  • You have a 404 after the first install page
  • You’ve use 1&1 Web hosting
Workaround

Add a RewriteBase in the .htaccess file:

    <IfModule mod_rewrite.c>
        RewriteEngine   on
        RewriteBase     /novius-os-install-dir/

What’s next

First page
Open the Webpages application

If you want create a website, go to the Webpages application:

The Webpages application on the home tab
Add your first page
Add a page
Write some content and hit ‘Add’
Adding a page
Preview your work

The Visualise action allows you to preview the page before publishing it.

Preview a page
Publish your page

Once you’re happy with your work, choose ‘Will be published’ and save.

Look in awe at what you’ve achieved:

Your new front-office
Templates

Pages need templates. They contains the content written in the back-office and set the style.

Template

The template is chosen when adding or modifying a page.

Choose a template

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).

Applications

Thanks to the applications, you can add new features to Novius OS.

The applications manager

Allows you to install and uninstall applications.

Le gestionnaire d'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.

Le gestionnaire d'applications

Updates

Updating the files
Git

If you installed Novius OS with Git, run these commands from Novius OS directory:

git fetch origin
git checkout master/elche
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/
Zip

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.

  • In my_site/, delete the following directories:
    • my_site/novius-os/
    • Every my_site/local/applications/noviusos_* directories
  • Copy the following directories and files from novius-os/ to my_site/ :
    • novius-os/
    • Tous les répertoires local/applications/noviusos_*
    • novius-os/
    • Every local/applications/noviusos_* directories
    • Every local/config/*.sample files
    • public/htdocs/install/, public/htdocs/install.php and public/htdocs/migrate.php.sample
    • Every files in root directory

Now you can continue the update.

Run the migration

Before running the automated migration tools, please backup your database.

Via SSH

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
Via Browser

If you can’t access SSH, you can run the migration from your browser:

  • First, you need to rename the public/migrate.php.sample file to public/migrate.php.
  • Open the file in your browser, such as http://www.my_site.com/migrate.php.
Via back-office

If you can’t access SSH, you can run the migration from back-office of your Novius OS:

  • Connect to your back-office
  • Open the “Applications manager” application
  • Click on “Apply changes” for each applications, or on “Refresh all metadata” in toolbar if your in expert mode.

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.

Migrate your developments

If you have personnal developments, you need to follow the Migration guide from the Dubrovka version to the Elche version.

Post-install optimisations

Configure and enable XSendFile

To undestand what XSendFile is and its usefulness, go here : Media centre.

Apache

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

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',
    ),
);

Discover Novius OS

Software’s fundamentals

MVC structure

Novius OS follows the Model-View-Controller pattern, which defines how to work:

  • when designing applications;
  • when organising a project running Novius OS.
Frameworks usage

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

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.

FuelPHP’s ORM

ORM stands for Object-relational mapping.

The ORM allows to handle the database using PHP objects (and classes), allowing to express relations between them.
An example is worth a thousand words:
$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.

But Novius OS adds another notable feature to the ORM: Behaviours.
Behaviours allows to extends Model in a standardised and reusable way.

They’re similar to FuelPHP’s Observers but are more powerful:

  • As Observers, they have configurable options.
  • As Observers, they can intercept events to take action on the Model (such as before_save, trigerred before updating the database).
  • In addition, they also provide methods (static or instance) on the Model.
  • They also can provide new events.
jQuery UI / Wijmo

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:

jQuery
This framework makes HTML manipulation and DOM much simpler. It’s not especially UI-oriented.
jQuery UI
Built on top of jQuery to provide inferface interactions. Most of the Novius OS UI comes from this framework.
Wijmo
Built on top of jQuery UI. Provides additional rich-interface elements, called widgets.

There’s a hierarchy between those frameworks, Wijmo has the most impact on Novius OS’ ergonomy.

Folder organisation

In Novius OS
Root directories
  • ~/novius-os/: Novius OS core.
  • ~/local/: Your website
  • ~/public/: DOCUMENT_ROOT of the website
  • ~/logs/: logs directory
public directory

A file can either be:

  • executable or non-executable
  • provided by the developer or provided by the application

This leads to 4 possibles usage, and each of them is reflected in the ~/public/ folder:

  • ~/public/static/: equivalent to assets. Non-executable files provided by the developer or Novius OS.
  • ~/public/data/: Non-executable files generated by Novius OS.
  • ~/public/htdocs/: Executable files provided by the developer or Novius OS
  • ~/public/cache/: Executable files generated by Novius OS.

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:

  • ~/novius-os/: Novius OS files.
  • ~/apps/<application_name>/: Files from developers of applications.
These directories are symbolic links, created upon Novius OS installation or when activating applications.
These symbolic links respectively points to htdocs and static directories of Novius OS or the application.
core directories
  • ~/novius-os/framework/: Novius OS framework
  • ~/novius-os/fuel-core/: FuelPHP framework
  • ~/novius-os/packages/: FuelPHP packages
local directory
  • ~/local/applications/: Novius OS applications.
  • ~/local/cache/: Contains resized medias.
  • ~/local/classes/: Your PHP classes.
  • ~/local/config/: Your Novius OS configuration files.
  • ~/local/data/: Files generated by Novius OS.
  • ~/local/metadata/: metadata files for your website, generated by Novius OS.
  • ~/local/migrations/: migration classes.
  • ~/local/views/: Your Views files.

Note

classes and views directories should not contain a lot of files, since most of your developments should go in applications.

Within an application

MVC applies to core, and also to applications

Files organisation

There are 6 main directories:

classes
It’s where your logic belongs, .i.e. classes which defines and manipulares data. At least there are controllers and models of the application. We can also find tools used by the views or the controllers.
config
This directory gather all the informations to repesent your models. Controllers perform logical operations on your data, but also needs additional data to pass on to the views in order to display them. These informations are separated from controllers (which don’t have logical value) and from the views. The latter receives data as parameters and never fetch them on their own.
The configuration file for the controller/admin/monkey.ctrl.php controller will be located at config/controller/admin/monkey.config.php. A class and its configuration file share a similar naming convention.
lang
This directory contains translation files, with a sub-directory for each language.
migrations
This directory contains migration files.
static
This directory contains JavaScript and CSS files, and public resources (such as images).
views
This directory contains files responsible to display the data.

Version numbers

To understand how Novius OS versions are numbered, let’s take a closer look at this sample release: Dubrovka 4.1.2

  • The first part (“Dubrovka 4”) is the major version. Novius OS major versions are numbered and also named after a city to be easier to identify (in alphabetical order, e.g. ‘D’ is the 4th letter of the alphabet). Major versions bring new features—for both end users and developers—, bug fixes and breaking changes often occur.
  • The second part (“1”) is the in-between version. In-between versions mainly bring bug fixes and small improvements, although new minor features might be available too. Breaking changes don’t occur.
  • The last part (“2”) is the minor version. Minor versions are security or bug-fixing releases only. They don’t bring new features and are therefore transparent to the end user. Updating is straightforward.

Note

Prior to Novius OS Dubrovka, version numbers didn’t fully follow the pattern described above.

Differences with FuelPHP

Constants path

See API documentation for constants.

Autoloader

Two namespaces are added by Novius OS :

novius-os:points to NOSPATH.
local:points to APPPATH.
Bootstrap and entry points

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/htdocs/admin.php: Back-office entry point. Handles all URL starting with /admin/.
  • ~/novius-os/htdocs/front.php: Front-office entry point. Handles all URL ending with .html or the root of your website.
Back-office entry point

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.

UI guidelines

Novius OS interface is built upon great ergonomic guidelines. To develop applications, two of them should be known: tab navigation and App desk.

Tabs navigation

See the screencast related to tabs navigation

Tabs navigation organise the work of the end-user. The purpose is to increase productivity, by limiting repetitive tasks and pages loading.

There are two types of tabs:

  • Application tab : Application tabs doesn’t have a title, but only a big icon of the application. It contains the App Desk (see below).
  • Item tab : from the application tab, we reach the edition of visualisation of an item, which takes place in a new tab. These kind of tab shows the item’s title and a small icon of the application.
Tabs navigation

There are many advantages to tab navigations. We’ll emphasise: - several items of the same application can be edited in parallel; - extremely fast switching between different items; - the user can resume its work where he left out (opened tabs are preserved upon time).

pop-ups must be limited to modal use, i.e, when an action must absolutely be accomplished (or canceled) before carrying out the work (such as confirming a suppression, adding a link or an image to a WYSIWYG content).

The 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).

App Desk

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.

Three-Pane Interface

Alternatively, the preview inspector can be placed under the main grid.

Applications’ fundamentals

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.

Defining an 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:

Launchers
Icon on the home tab, used to launch an application.
Enhancers
They allow applications to enhance a WYSIWYG edited content.
Templates
Layout for the front-office.
Data catchers
Component which allows an application to exploit shared data.
L’App Desk

To understand the App Desk, please read the ergonomic guidelines first.

App Desk’s configuration
App Desk

The main feature of the AppDesk is several configurable elements:

  • how to display the data ;
  • the data itself ;
  • main and secondary actions.

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:

  • The same model as the one of the main grid (like a publication date), the inspectors then uses a particular column / attribute of the item to filter the results;
  • Another model (like posts’ author), the inspectors then uses a relation fo filter the results.
Controllers, forms and models

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 and behaviours

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).

Content sharing

Content nuggets

A content nugget is a coherent set of data meant to be shared.

Data structure

Content nuggets’ data are of the following nature:

  • Title
  • URL
  • Text
  • Image

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

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).

Data catchers included in Novius OS
  • Simple Twitter
  • Simple Facebook
  • Simple Google+
  • Blog

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.

Example : Simple Twitter

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.

Multi-Contexts

Multi-contexts basics
Novius OS can natively manage several websites, and each of them can have several linguistic versions.
A context is a site / language pair.
Example

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:

  • Showcase / French
  • Showcase / English
  • Showcase / Spanish
  • Mobile / French
  • Events / English
Configuration

Everything to configure the contexts is described in the API documentation

Special cases

He who can do more can do less. Your Novius OS can manage:

  • a single website with several languages;
  • several websites in a single language;
  • a single website in a single language.
The back-office interface reacts to any of these situations. The _context_ word will disappear in favor of _language_ or _site_.
It can ever vanish completely for a single website in a single language.
Adding contexts

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.

Contextable / Twinnable

Although Novius OS natively manages multiple contexts, each application decides the way it uses them on its own.

Three cases can be found.

Application not using any context

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.

Contextable application

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.

Twinnable application

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.

Example

Schema of our example table:

  • item_id (primary key)
  • item_context
  • item_common_id_property
  • item_is_main_property
  • item_title

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
The item_common_id_property column is assigned with the same value as the primary key.
The item is primary, and the item_is_main_property is set to 1.

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
The item_common_id_property column is assigned with the same item_common_id_property to which it’s linked to.
item_is_main_property is set to 0, it’s not the primary 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)
The items with ID 1, 2 and 6 are linked together, and the main item is 1 / main::en_GB.
The items with ID 3 and 4 are linked together, and the main item is 3 / main::en_GB.

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.

Media centre

General informations

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.

  • All files are store in the private directory /novius-os/local/data/media/
  • They are accessed using the URL http://your.website.com/media/folder/ressource.ext
Functioning

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:

  • a HTTP 401 error code (authorization required);
  • or the file will be sent on the standard output, but without creating a symbolic link (permissions need to be checked for each and every request).

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/.

Optimisation

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:

  • X-Sendfile is used by Apache and some others ;
  • X-Accel-Redirect is used by nginx.
Attached files (outside the media centre)

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

Roles (or “profiles”)

Permissions are always applied to a role. Then, each user is given one or many roles, from which the permissions are determined.

One role by user

For the sake of simplicity and understanding, on a default installation:

  • these roles are hidden ;
  • each user gets one unique role attributed ;
  • it’s not possible to share a role among several users.

Hence the role notion is completely hidden and the permissions are configured on a dedicated tab of the user form :

_images/user_standalone.png
Several roles by user

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:

  • on the user form, the “Permissions” tab disappear in favor of a “Roles” block ;
  • the AppDesk of the Users application gains:
    • a new “Roles” inspector ;
    • a new “Add a role” action.
  • permissions should now be configured on the role form.
_images/user_roles.png
Permission structure

They are two types of permission:

  • simple: yes or no ;
  • multiples : applied on a list of categories (values);

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).

How-to use permissions in the applications
permissions.config.php file

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 :

_images/permissions.png
API to check a permission
<?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.

CRUD

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');
             },
         ),
     ),
 );
Actions

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;
                 }
             ),
         ),
     ),
 );

Front-Office and cache

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.

Cache execution

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.

Cache generation

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).

Possible interactions

During the process Controller_Front triggers several events at key locations. By using them, it’s possible to alter the process.

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.

Alter the generated content

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.

Executing outside the cache

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
);
Suffix Handler

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.

Manage your website

Install a Novius OS application

Where to find apps

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.

Install a new application

2 solutions are available: using Git or a .zip file.

1st method : using Git

On GitHub, copy the repository URL:

_images/download_git.png

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.

2nd method: using a .zip file

On GitHub, download the application as a .zip file:

_images/download_zip.png

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-elche 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.

Activate an application

Open the applications manager (from the desktop) :

_images/app_manager_launcher.png

Clic on « Install » next to the name of your application.

_images/activate_app.png
Update an application
1st method: using Git

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/elche

Then, go in the applications manager to « Apply changes ».

2ns method: from a .zip file

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 ».

Define your site’s languages and contexts

With Novius OS, you can manage several sites and/or languages in the same back-office. We call ‘context’ a site / language pair.

You can define the contexts at any time, even after the site was launched. Just change the contexts.config.php file.

Friendly slug

All segments of URLs builded in Novius OS are cleaned by the friendly slug mechanism.

By default:

  • all characters ?, :, \, /, #, [, ], @, & and space are replaced by -.
  • transform to lower case.
  • remove trailing -.
  • replace multiple - by one.

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:

  • default setup (like describe above)
  • no_accent setup. All accent characters are replaced by the equivalent character without accent.
  • no_special setup. All characters that are not a word character, a - or a _ are replaced by -.
  • no_accent_and_special setup. Combination of no_accent and no_special setups.

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.

Default setup

To change the default setup of rules:

  • add a key to setups.
  • Set active_setup to this new key.
<?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'),
        ),
    ),
);
Setup for context

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

Friendly slug API.

Production

From localhost to your production server

You can send Novius OS on a production server using many way:

  • The simplest way would be to copy all Novius OS files as well the database from your local machine to the server. However, as the data is generally different between these two instances, this is not very convenient. And you will probably have to change configuration files.
  • You can send all files except those in local/metadata and local/data folders. You will need to install Novius OS on the production server, only for the first time. This way you will be able to easily configure mysql connection, urls and administration accounts.

However, regardless the method you choose, you will have to change few configuration settings to improve optimization.

Changing environment to production

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
Database configuration

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.

Customizing cache durations

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
    ),
);
Email configuration

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.

FuelPHP fundamentals

What is MVC?

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

MVC in Wikipedia

Where to create my new files?

Every classes follows the same precise naming convention:

  • lowercase, except the first letter of each level in uppercase ;
  • underscores are use to separate directories.

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.

How to write a view?

Views should be put in the views directory.

How to use the ORM?

An ORM does 2 things:

  • it maps your database table rows to PHP objects ;
  • it allows to establish relations between them.

FuelPHP’s ORM uses the Active Record pattern.

The following links from the FuelPHP’s documentation will help you:

See also

Creating models

Create a new application

The application wizard

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.

Add an enhancer

1. Configuration in metadata file

Metadata of an enhancer are described in the API documentation.

2. [Back-office] Create the enhancer’s controller

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
);
Configuration popup

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 :

  • $enhancer_args : Old enhancer configuration ;
  • $fieldset : if you specified fields, a Fieldset is instanciated in this variable.
Change preview
« Form » enhancer

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.

3. [Front-office] Display content on the website

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);
    }
}
4. URL enhancers

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:

  • my/page.html
  • my/page/first_level.html
  • or my/page/first_level/second_level.html

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:

  • my/blog : Page URL;
  • ski : Category virtual url ;
  • 2 : Page number.

Create a template

1. metadata configuration

Template metadata are described in the API documentation.

2. Template variation configuration

You can optionnaly define a template variation configuration if your template is customisable by context.

3. View file creation

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.
$title:The title to put in a h1 tag
$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>

Add fields

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(),
);
Standards fields

Bold text is the type property value:

  • <input type=”text“>
  • <input type=”password“>
  • <textarea>
  • <select>
  • <input type=”radio“>
  • <input type=”checkbox“>
  • <input type=”submit“>
  • <input type=”button“>
  • <input type=”file“>
<select> field
<?php
return array(
        'gender' => array(
                'label' => 'Gender',
                'form' => array(
                        'type' => 'select',
                        'options' => array(
                                'm' => 'Male',
                                'f' => 'Female',
                        ),
                ),
                'validation' => array('required'),
        ),
);
<button type=”submit”>
  • type = submit generate <input type="submit">
  • type = button generate <input type="button">

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 (enhanced fields)

renderers list is available in API documentation.

Add thumbnails view in App Desk

It is actually quite simple. You need to define two special keys in data_mapping:

  • thumbnail: thumbnail item path ;
  • thumbnailAlternate : default path when no item thumbnail path is defined.

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',
         ),
     ),
 );

Display thumbnails in differents formats

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>';

Add common fields to all contexts

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

Multi-Contexts

Common field

For define a common field, just adds it to common_fields key of Twinnable behaviour.

Example
<?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'),
        ),
    );
}
Common media and WYSIWYG

For define a common media or WYSIWYG, use providers shared_wysiwygs_context and shared_medias_context added by the Twinnable behaviour.

Add attachment

Principles

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'].

Joined to a model

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();
Details

For more details, check Attachment.

Extensions

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.

URL alias

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.

Secured attachment

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.

Translate an application

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).

_images/i18n_folder_structure.png

Dictionaries are PHP files returning a array, similarly as configuration file.

See also

I18n class API

File metadata.config.php

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.

Other files

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');
Advanced mode: configure your own dictionaries

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');

Migrations files

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:

  • Application migration files must be on the namespace {{APPLICATION_NAMESPACE}}\Migrations
  • Whenever it is possible, sql update must be on a separate file (in order to make easier manual updates). As most of the time only sql requests are executed, a migration class has been implemented in order to ease migrations. You can take a look at the API documentation.

When an application is installed or updated, migration files are executed (if they haven’t been already) through the application manager.

Create your own Behaviour

Why doing it?

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:

  • listening to events triggered by Novius OS (especially by the AppDesk and the CRUD) ;
  • adding dynamically additional methods on your models (in a reusable way).

Here are some examples to better understand:

  • adding an action in the App Desk or the CRUD ;
  • adding a column in the dataset (data_mapping) of a model ;
  • adding a field in the add/edit form of an item ;
  • adding a new myBehaviourMethod() method for all the models using this behaviour.

More concretely:

  • The Publishable behaviour, ass a field in the CRUD configuration and displays it in the form byb using the Renderer_Publishable.
  • The Urlenhancer, Twinnable and Sharable behaviours respectively adds the following actions: visualise, translate and share.
Extending the Orm_Behaviour class

To create a behaviour, you need to create a class that extends Nos\Orm_Behaviour, and implementing the following:

  • methods to listen to FuelPHP events (before_* et after_*), in the same manner as Observers ;
  • methods to listen to Novius OS events (triggered by the App Desk and the CRUD) ;
  • additional methods to add to all the models which are using your behaviour.

You can add any class as a behaviour by adding its full classname to the $_behaviours property of your model.

Properties

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',
     * )
     */
}
Listening to a FuelPHP event (Observer)

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;
        }
    }
}
Listening to a Novius OS event

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:

  • instance events, which always receive the $item as a first parameter, and optionally other parameters specific to the event ;
  • static events, which only receive parameters specific to the event.

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);
Adding dynamically an instance method on a model

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();
Adding dynamically a static method on a model

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();

Extend an application

Extensions mechanisms

Create a file in local

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:

  • {section}` equals to config or views ;
  • {application}` matching apps + an application name or novius-os for the core.
Configuration

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.

Views

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.

Use events to alter a configuration

Any configuration file can be altered thanks to the config|<path>. event.

Replace a view with another one

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');
Create a dedicated extension application

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.

Example:

application_2 extends my_application.

Configuration

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

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.

Bootstrap

The bootstrap file allows you to execute php code when the website / an application is loaded. It can be placed in two locations :

  • local/boostrap.php: will be executed when the website is loaded.
  • local/applications/APPLICATION/bootstrap.php: will be executed when the application APPLICATION is loaded.

It is possible to use the bootstrap to extend an application. It is there events and view redirects can be used.

Adding a field

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.

In the database
ALTER TABLE `nos_blog_post` ADD `post_source` VARCHAR(255);
In the model

2 choices:

  • Declare the new field in the model properties.
  • Activate the cache mechanism of models properties.
Declare the field

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,
    );
});
Activate the properties cache
  • 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 .

In the form

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:

'source' field inside the blog post form
In the visualisation

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
}

Changing the appearance on the website

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 :

Default list of the 'Blog' application

Personalised design on the Novius OS.org website (our goal):

Personalised list of the 'Blog' application
Changing the view
1st solution: extending the view

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.

2nd solution: extends the configuration

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,
    ),
);
Adding the CSS
1st solution: extending the view

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.

2nd solution: directly take action on the template

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.

Add an action in the admin

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.

Placeholders

Action’s configuration contains {{placeholders}}.

  • Replaced with PHP :

    • {{model_label}}: the model name
    • {{controller_base_url}}: URL of the model’s controller
  • Replaced by the App Desk (JavaScript) :
    • {{context}}: current context (or first one when several are shown)

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.

Action’s target

There are 3 possibles targets for the actions:

  • toolbar-grid: App Desk’s toolbar
  • grid: item line in the main grid of the App Desk
  • toolbar-edit: toolbar on the editing / addition form
'grid' target of actions 'edit' target of actions
Add / Edit / Delete

add action:

  • opens a new tab ;
  • calls the action_insert_update() method on the Nos\Media\Controller_Admin_Media controller ;
  • with the $_GET['context'] parameter, allowing to pre-select the active context ;
  • is only shown in the App Desk’s toolbar.

edit action:

  • opens the edition form (the method is not specified for nosTabs, so the default open value will be used: it will focus the existing tab if it’s already opened, or will create a new one otherwise ;
  • calls the action_insert_update( $id ) method on the Nos\Media\Controller_Admin_Media controller ;
  • with an id parameter ;
  • is only shown in the main grid.

delete action:

  • calls the action_delete( $id ) method on the Nos\Media\Controller_Admin_Media controller ;
  • with an id parameter ;
  • is shown both in the main grid and the edition form, but only for existing items (not for adding new items).
 <?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();
         },
     ),
 );

Alter a behaviour of the front-office

Novius OS provides an event-base mechanisme to interact with the core.

There are 2 types of events:

  • Those which can alter data ;
  • Those which only notify an action occurred.
<?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

Events

Redirecting depending on the URL

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);
    }
});
Sending a thank-you mail from a contact form
<?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
        }
        }
    }
});

Version notes

Release notes 0.2

New features
  • 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

  • Back-office tabs can be closed with a middle click
    New button Close all other tabs
Developpers
  • 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.

Migration guide from the 0.1 version to the 0.2 version

Mandatory breaking changes
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
Changes in the pages API
  • 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
Changes in the events API
  • front.start now takes an array as argument, containing the url key
Procedure to follow
  • 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}}
Changes in the applications
Metadata
  • Launchers: the url key has been replaced by action (standard nosAction syntax)
  • Launchers: the icon64 key has been replaced by icon
<?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}}',
            ),
        ),
    ),
);
  • An application now proved a list of icons:
<?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',
    ),
);
  • A launcher without icon will use the 64-icon from its application
  • A tab without iconUrl will use the 32-icon from its application
  • An enhancer without iconUrl will use the 16-icon from its application
CRUD configuration
  • widget was renamed to renderer
<?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(),
    ),
);
“Full” 0.2 migration

This section relies on the hypothetical existence of a lib_agenda application.

Appdesk

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).

  • Create the associated config/common/{model_name}.config.php file
    • {{model_name}} relates to the name of the model, in lowercase and without the Model_ prefix (for instance, Model_Page becomes page)
    • Model_Page will relates to the config/common/page.config.php file

Note

Pay attention to have 'hideContexts' => true, in your appdesk’s configuration if you items are not Contextable.

Data mapping

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

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).

I18n and translations

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>.'),
    ),
);
Inspectors

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.

Category
 <?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) {
            // ...
        },
    ),
    // ...
);
Date
<?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.

published
<?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',
                 ),
             ),
         ),
     ),
 );

Release notes Chiba 1

New features
  • The behaviour publishable (Behaviour_Publishable) now allows to choose publication start and end dates.
Form
  • Adding a progress bar on multi-page form.
Blog / News
  • Display related posts of authors.
Developer
  • 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.
Deprecated
  • Moved Nos\Renderer_Media to Nos\Media\Renderer_Media.
  • Launchers configuration: the url key is deprecated. Use action instead.
  • The widget key is deprecated in renderer configuration. Use the renderer key and update the class name.
  • The widget_options key is deprecated in renderer configuration. Use the renderer_options key.
  • \Config::extendable_load() is deprecated. Renamed to \Config::loadConfiguration().
  • Orm_Behaviour_Publishable configuration: the publication_bool_property key is deprecated. Use publication_state_property instead.

Migration guide from the 0.2 version to the Chiba 1 version

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:

  • Nos\Renderer_Media should be renamed to Nos\Media\Renderer_Media.
  • Launchers configuration: the url key is deprecated. Use ‘action’ instead.
  • The widget key is deprecated in renderer configuration. Use the renderer key and update the class name.
  • The widget_options key is deprecated in renderer configuration. Use the renderer_options key.
  • \Config::extendable_load() is deprecated. Renamed to \Config::loadConfiguration().
  • Orm_Behaviour_Publishable configuration: the publication_bool_property key is deprecated. Use publication_state_property instead.

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.

Release notes Chiba 2

New features
  • 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.

  • Add media mass upload feature.
Developer
Vendors update
  • FuelPHP 1.6
  • jQuery 1.9.1
  • jQuery UI 1.10.3
  • Wijmo 2013v1.4
  • require.js 2.1.6
Improvements
  • i18n: Default dictionary app::default is used if no dictionary is set with Nos\I18n::current_dictionary().
  • DB: Change interclassement on all columns containing a slug.
  • ORM: Improvement of the model properties’ cache mechanism, just one query of columns from DB by request.
  • ORM: 4 new relation types, twinnable_belongs_to, twinnable_has_one, twinnable_has_many, twinnable_many_many.
  • ORM: Model class, new addRelation(), configModel(), getApplication() methods.
  • Behaviour: New behaviour author, used by Page, Media, Blog/News, Slideshow, Form.
  • Behaviour: Refactoring behaviour implementation (behaviours can intercept model events).
  • Behaviour Twinnable: Models now can have fields, medias and WYSIWYGs common to all contexts.
  • Behaviour Twinnable: new findMainOrContext(), hasCommonFields(), isCommonField() methods.
  • Behaviour URLEnhancer: New methods deleteCacheEnhancer() and deleteCacheItem().
  • Behaviour URLEnhancer: Delete front’s cache of the item on deleting and updating.
  • Enhancer: In the configuration popup, new ability to define a layout and fields configuration instead of a view, much like the CRUD.
  • Enhancer: In enhancer configuration, new possible key valid_container, which is callable. Can restrict the enhancer availability depending on container.
  • Enhancer: The HTML output generated for the front-office is wrapped in a div with classes noviusos_enhancer and the enhancer name (noviusos_blog, noviusos_news, noviusos_slideshow, noviusos_form)
  • Renderer: New datetime picker renderer to manage both date and time in the same input.
  • WYSIWYG: New WYSIWYG configuration mechanism, with a wysiwygOptions event registrable by behaviour (and used by twinnable), and wysiwyg config sample file.
  • WYSIWYG: In Nos::parse_wysiwyg(), replacing anchors by URL#anchor only in front.
  • SEO: New friendly slug configuration mechanism, with a friendlySlug event registrable by behaviour (and used by twinnable), and friendly_slug config sample file.
  • OsTabs: New reload method in API.
  • OsTabs: Change in tabs opening position. Tab added without index now is added at selected + 1, excepted on the desktop, which always adds the new tab at the end.
  • Appdesk: Two new keys, css and notify in appdesk configuration.
  • Appdesk: Ability to ignore a cellFormatter based on a column value.
  • Appdesk: Now custom cellFormatters are allowed in appdesks.
  • Grid: New align key on actions configuration.
  • Grid: New option for the initial opening depth on tree grid.
  • UI: Using .ui-priority-primary instead .primary on button and .title on textbox inputs.
  • UI: Use browser native select, checkbox and radio, no more use of Wijmo widgets for those inputs.
  • Page: Setting the home page is not allowed in multi-context view.
  • Page: Deleting or unpublishing the home page is not allowed.
  • Page: Increased title and url columns characters length.
  • Media: New field filesize. Display filesize and dimensions in appdesk preview and CRUD form.
  • Media: Refactoring get_img_tag() and get_img_tag_resized() methods of Model_Media, uses HTML::img() for returning a tag with attributes.
  • Media: You can now transform (crop, rotate, rounded, watermark, resize, shrink, grayscale, border) Media and Attachment images with Toolkit_Image API.
  • Media: New “Renew media’s cache” action in Media appdesk toolbar, visible for expert users.
  • Media: Increased title and url columns characters length.
  • Comments: New API for use of noviusos_comments application.
  • Form: New message view for the confirmation.
  • Blog/News: Thumbnail is now configurable (size & link).
  • Misc: New events 404.mediaFound, 404.attachmentFound, admin.loginFail and nos.deprecated.
  • Misc: All URLs are now urlencoded when use in a href or in a redirection.
  • Misc: New temp directory in local/data, assign to novius-os.temp_dir config key by default.
  • Front: is_preview is true only when you are logged in.

New in version Chiba: 2.1

  • Media: Bugfix, images transformed was only display for users connected to back-office. For others, they return a 403.
  • Media: Bugfix on media permissions; when updating a user, his writing rights on medias were disabled.
  • CRUD: The configuration of button save is no more required in CRUD fields settings.
  • ORM: In Models, when use cache_model_properties, new possibility to set a callback (check_property_callback, see local/config/config.php.sample) to check if the property is a potential unknow column, and avoid a show field SQL request.
  • Renderer: New class Nos\Renderer for factorizing code between all renderers.
  • Templates basic: Refactoring for better factorization of code between top and left menu templates.
  • Slideshow: Refactoring configuration and organization. Widgets for displaying slideshow in front are manage by a formats config for better extendable.
  • Blog/News and Comments: Better clean-up of front-cache when a post or a comment is inserted, updated or deleted.

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

  • Tasks: FuelPHP tasks have been adapted to Novius OS. Tasks namespace now depends on application namespaces allowing two tasks with similar names in many applications.

    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

  • PHP: Version 5.5 officialy supported
  • Renderer: new option null_allowed (default to false) on Nos\Renderer_Datetime_Picker
  • Misc: Improve Toolkit_Image->sizes(), Media image is not loaded in memory
  • WYSIWYG: In popup image, new fields border, align, vspace and hspace to easily update style
  • CRUD: The javascript for context common fields is improved. Now, unsupported inputs can implement their own blocking process
  • CRUD: Blocking process for context common fields is improved. Now it work also on not input fields (ie: renderer builded on a <div>)
  • CRUD: Blocking process for context common fields supports virtual name renderer fields
  • Profiler: Some items from config are not displayed for security issues
  • Profiler: New methods markDeltaStart() and markDeltaStop() to study time durations
  • ORM: New parameter through_where in many_many relation configuration
  • Form: Adding a replyto field to sending emails if an email is present in the answer. Depends on the add_replyto_to_first_email config key of noviusos_form.config.php file (default true)
  • Form: Move submit email field on the top of the admin form
  • AppWizard: Added check on local/applications folder permission

New in version Chiba: 2.3.1

  • UI: When inserting during a pick process, picks automatically the new item (media, page)

New in version Chiba: 2.3.2

  • Security: Fix XSS in profiler

Migration guide from the Chiba 1 version to the Chiba 2 version

Upgrade your Novius OS and its applications

See Updates page if you didn’t already do it.

Migrate your developments
Breaking changes
Model: columns of dataset are now encoded

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,
        ),
        // ...
    ),
);
CRUD: success callback is called after save

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.

Attachment: ->url() and ->urlResized() return absolute URLs

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

Attachment

Comments: they now are contextable

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.

Blog/News: the default size of thumbnails have change and they are clickable
  • The default size of thumbnails have change from 200 to 120 pixels on list, remains 200 on item.
  • Thumbnails are clickable.

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,
                    ),
                ),
            ),
        );
    
URL Enhancer: mandatory getUrlEnhanced() method

All URL enhancers must implement a getUrlEnhanced() method.

Deprecated

Those updates are not mandatory but desirable to be able to migrate without trouble when next version is released.

Enhancer: get_url_model($item, $params) becomes getURLEnhanced($params)

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;
}
Media: Changes in Model_Media API

All snake_case methods are deprecated:

  • delete_from_disk becomes deleteFromDisk
  • delete_public_cache becomes deleteCache
  • get_path becomes _getVirtualPath
  • get_private_path becomes path
  • get_img_tag becomes htmlImg
  • get_img_tag_resized becomes htmlImgResized
  • is_image becomes isImage
  • get_public_path becomes url
  • get_public_path_resized becomes urlResized

See also

Methods

Media: Changes in Model_Folder API
  • delete_from_disk becomes deleteFromDisk
  • delete_public_cache becomes deleteCache

See also

Methods

Event user_login

The user_login event is deprecated, use admin.loginSuccess instead.

Migration Chiba 2 to Chiba 2.1

New in version Chiba: 2.1

Deprecated

Those updates are not mandatory but desirable to be able to migrate without trouble when next version is released.

Renderer_Selector->set_renderer_options()

The set_renderer_options() method is deprecated, use setRendererOptions() instead.

Renderer_Media->parse_options()

The parse_options() method is deprecated, use parseOptions() instead.

Slideshow : front-office views and configuration
  • The configuration file noviusos_slideshow::slideshow has been refactored for a better separation between slideshow’s formats. Voir API documentation of Slideshow.
  • The configuration file noviusos_slideshow::flexslider is deprecated, use noviusos_slideshow::formats/flexslider instead.
  • The view noviusos_slideshow::slideshow_js is deprecated, use noviusos_slideshow::flexslider/javascript instead instead.
  • The view noviusos_slideshow::slideshow is deprecated, use noviusos_slideshow::flexslider/slideshow instead instead.

The Chiba 2.1 version of Slideshow application has to make some migrations in the DB. See Run the migration.

Release notes Dubrovka

New features
  • Applications can extend multiple applications
  • The extend application mechanism works also for views and lang files, not only for config files
  • Russian translations
  • Spanish translations
  • Interlingue (Occidental) translations
  • Japanese translations updated
Developer
Vendors update
  • FuelPHP 1.7.1
  • Wijmo 2013v3.20
  • jQuery 1.10.2
  • require.js 2.1.9
  • TinyMCE 3.5.10
  • JQuery.cookie 1.4
  • JQuery.form 3.46.0-2013.11.21
  • JQuery.validation 1.11.1
  • JQuery.mousewheel 3.1.6
  • JQuery.datetimepicker 1.4.3
Improvements
  • 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)

  • Behaviour: All aliases in where and order_by options of find() work whatever the level where the alias is used and even in chaining methods.
    Concern: context in Contextable, published in Publishable, default_sort in Sortable, parent in Tree and context_main in Twinnable.
  • 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.

Migration guide from the Chiba 2 version to the Dubrovka version

Upgrade your Novius OS and its applications

See Updates page if you didn’t already do it.

Migrate your developments
Breaking changes
FuelPHP from 1.6 to 1.7.1

Take a look of these three changelog of FuelPHP for backward compatibility notes:

Wijmo from 2013v1.4 to 2013v3.20

Take a look of release notes of Wijmo between 2013v3.20 and 2013v1.4: http://wijmo.com/wiki/index.php/Version_Histories

End of support for config key migrations.enabled_types.metadata

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.

Event name for extend metadata configuration

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.

Deprecated

Those updates are not mandatory but desirable to be able to migrate without trouble when next version is released.

Some i18n keys of CRUD config for plural forms

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:

  • deleting with N contexts
  • deleting with N languages
  • deleting with N children
  • deleting button N items
  • N items
  • showNbItems

These keys are unnecessary:

  • 1 child
  • deleting button 1 item
  • deleting button 0 items
  • 1 item
Nos::hmvc() API is simplified

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));
Method Config::loadConfiguration()

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);
NosApplication::applicationRequiredFromMetadata() scope public

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, extends.application key

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

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

WYSIWYG theme

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_.

Release notes Elche

New features
  • New Menu application to manage website menu.
  • New Template variation application to manage template variations by context.
  • New Bootstrap customisable template application which use power of template variation. You can customize elements, layout, skin, menus.
  • UI: Background image on body and sides always visible
  • UI: New tray bar with new fullscreen feature
  • UI: Appdesk and CRUD Toolbar at bottom
  • UI: On Appstab, launchers now draggable on a grid, not only sortable
  • Renderer: New renderer Renderer_Item_Picker based on native appdesk of model
  • Renderer: New renderer Renderer_Select_Model to select item of a model. Manage contexable and twinnable behaviour.
Developer
  • ORM: Add Providers feature. Possibility to add providers on model. Providers are accessors to relation’s items by a key.
  • ORM: Wysiwygs and Medias becomes native providers.
  • ORM: New shared_wysiwygs_context and shared_medias_context providers.
  • Enhancer: Config popup now run form validation if some fields have contraints.
  • Grid: cellFormatters on columns work on all noslistgrid widget, not only on principal grid of appdesk
Vendors update
  • jQuery 1.11.1
  • jQuery UI 1.10.4
  • Wijmo 2014v1.34
  • require.js 2.1.11
  • pNotify 2.0
  • JQuery.cookie 1.4.1
  • JQuery.form 3.50.0-2014.0.2.05
  • JQuery.mousewheel 3.1.11
  • JQuery.datetimepicker 1.4.4
  • Webshims 1.13, in Form application,
New features
  • Japanese translation.
  • ORM: Twinnable relations now allow to fix a context instead of using the one on the current model. This allow to use the front office context insted of the model context (wich can be different).

Migration guide from the Dubrovka version to the Elche version

Upgrade your Novius OS and its applications

See Updates page if you didn’t already do it.

Migrate your developments
Breaking changes
Shared Wysiwygs and Medias context
  • All shared Wysiwygs and Medias context must be access with providers shared_wysiwygs_context and shared_medias_context
  • Remove declaration of variables shared_wysiwygs_context and shared_medias_context from model
Deprecated

Those updates are not mandatory but desirable to be able to migrate without trouble when next version is released.

Nos:parse_wysiwyg(), Nos:parse_enhancers() and Nos:get_enhancer_content()

These methods are deprecated, use:

  • Tools_Wysiwyg::parse() instead of Nos:parse_wysiwyg()
  • Tools_Wysiwyg::parseEnhancers() instead of Nos:parse_enhancers()
  • Tools_Enhancer::content() instead of Nos:get_enhancer_content()

Contribute to Novius OS

Translate Novius OS

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.

Quick start guide

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:

  1. Go to translate.novius-os.org.

  2. 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.

  3. 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.

Copy style guide

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.

How to translate

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.)

Translate offline

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.

Available languages
Available languages

If on translate.novius-os.org home page, your language is not listed under Languages, just contact us. We’ll add it for you.

Translation files

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.

Translation directories

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:

  • Top priority, the core: framework.
  • Then the native applications: Webpages (noviusos_pages), Media Centre (noviusos_media), Users (noviusos_user) and Applications manager (noviusos_appmanager).
  • And finally the non-native applications.
Suggesting and submitting translations

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.

When translating
Placeholders and tags
  • Some strings include variables, e.g. ‘Welcome back, {{user}}’. Obviously, Novius OS end users don’t see these variables which are replaced by the actual value, e.g. ‘Welcome back, Joe’. Variables are to be kept, untranslated, e.g. ‘Re-bonjour {{user}}’.
  • HTML tags are also to be kept and not to be translated. In most cases, you’ll find a start tag and an end tag, e.g. ‘This page has <strong>one sub-page</strong>’. Text between tags must be translated, e.g. ‘Cette page a <strong>une sous-page</strong>’.
  • For non-breaking spaces, please use the HTML entity, i.e. &nbsp;.
Dispelling doubts

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:

String URL
Translating the documentation

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!

Copy style guide (English)

Introduction

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.

About translation

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.

Personality and tone

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.

Personality traits
  • Professionnal but not boring.
  • Straight-to-the-point but not bossy.
  • Consistent but not repetitive.
  • Friendly but not bro-ish.
  • Knowledgeable but not haughty.
English language

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 (‘).

Copy examples
Actions:
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]
Success:
New item added:All done! The blog post has been added. [2]
Item updated:OK, changes are saved. [2]
Errors:
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.
Read the Docs v: elche
Versions
latest
elche
dubrovka
chiba.2
chiba.1
Downloads
PDF
HTML
Epub
On Read the Docs
Project Home
Builds

Free document hosting provided by Read the Docs.

Table Of Contents

This Page