Module CustomFields

Revision as of 02:45, 29 August 2012 by Lrq3000 (talk | contribs)
CustomFields
Numero/ID of module 8500
User doc. of module This page
Developer doc. of module This page

Description

This module allows the implementer to easily create and manage custom fields, and then use them in PDF and ODT documents, or in any other way you want.

You can choose the datatype, the size, the label(s), the possible values, the value by default, and even constraints (links to other tables) and custom sql definitions and custom sql statements.

CustomFields has been made with the intention of being as portable, flexible, modular and reusable as possible, so that it can be adapted to any Dolibarr's module, and to (almost) any user's need (even if something isn't implemented, you can most probably just use a custom sql statement, the rest will be managed automatically, even with custom statements.).

Understanding CustomFields

Technically speaking, this module is a simple wrapper for your sql database. It respects sql standards and manage them in a standard way.

This means that you can modify your sql database with another tool (eg: phpMyAdmin), and this module will then manage it automatically.

Features overview

- Natively support the modules: Invoices, Propales and Products/Services.

- Full multilanguage support:

  • multilanguage in the administration gui (french and english for now, but can be translated by anyone using langs files)
  • multilanguage user-defined custom fields labels
  • multilanguage custom fields values (eg: yes/no select box can be translated to any language, same for enum user defined values). You can even translate the values of your dropdown boxes or your constrained fields.

- Several natively supported fields types :

  • Textbox
  • Areabox
  • YesNoBox
  • TrueFalseBox
  • DropdownBox (your own options)
  • Date
  • DateTime
  • Integer
  • Float
  • Double
  • Constrained (link to other tables)
  • Other (custom type, defined by your own SQL command)

Note: you can add your own sql datatypes, see the related chapter.

The last one is not a native support but permits you to easily set any SQL data type that is not yet implemented, and it will be managed as best as possible by the module (by default if it's unknown it will be shown as a textbox).

- Custom sql statements to create complex fields : these custom sql statements will be executed after the creation/edition of the custom field definition.

- Fully automated management of the customfields in the database and via triggers.

- Extensible to any module, be it core module or third-party user-made module (see the related chapter).

- Auto-detection and auto management of constraints (automatically find the primary key and choose the same data type and size) and auto management of their printing and edition (show them as a dropdown box).

- Smart Value Substitution for constrained fields: tell which column(s) you care, and the module will print the remote column instead of the row id (see the related chapter).

- Can define different custom fields PER module (you can have different custom fields for each module)

- Easy to use in PDF and ODT templates (see the related chapter).

- Works on mobile phones and tablets: no ajax, no javascript code, only standard html.

- Elegant presentation in datasheet.

- Supports all classic functions of a standard field: creation, edition, cloning, etc.

- Clean and isolated: do not interfer with the normal functionning of Dolibarr, everything is separated. You can just delete the customfields and it will never do anything wrong with your Dolibarr install or your other datas.

- Develop your own modules via CustomFields: CF provides many methods to automatically manage the inputs of a user and access custom fields values from anywhere in the code (from Dolibarr's core to your own module), and even a few generic functions to query any SQL request and help to manage them, with an automatic management of errors.

- Code is very easy to edit/customize/reuse/port (fully commented and modular architecture, and no duplicates functionnalities).

- Use of strict SQL standards, so it should work for any database following the minimum standards requirement (tested on MySQL, probably works for PostGreSQL and SQLite).

- Secure: only authorized users can edit customfields (one whose rights include create and modify for the module)

- Optimized and fast: cache whenever possible and super optimized sql requests and fast php code with as few loops as possible and low number of opcodes.

Usage

Enabling the module

To use this module, you must first enable it using an administrator account, via the menu option "Home - Setup - Modules".

Choose the tab where the module is listed. Then click on "Activate".

The module is now activated.

If a cog icon appears   on module thumb or at end of the line of the module, click on it to access the setup page specific to the module.

Now you can choose the tab of the module for which you want to configure your custom fields.

If the sql table does not exist yet, the module will allow you to create it automatically by clicking the button Create table.

Note: when you update Dolibarr or the module, you should disable and re enable the module to activate the new functionnalities (particularly the support of new modules).

Creating a custom field

After enabling the module and creating the table, you can now create a custom field.

Click on the button New field on the bottom right hand corner of the page.

You will then be presented with several options (bold are required):

  • Label: this option is disabled, because the label will automatically be the same as the Field Name, but you can translate it (set it) in language files (see the related chapter below).
  • Field Name: sql field name. Choose any name you want, it just has to be alphanumeric without any space (you can use underscore '_'). You can also use remote sql columns names if you set a constrained field to enable Smart Value Substitution for this field (see the related chapter).
  • Type: the type of the field. This is where you choose what kind of data your field will hold, and more importantly how it will be shown (eg: Textbox, DropdownBox, etc..). You can also choose Other and set your own sql type by entering it in the Other box just below.
  • Size, or for DropdownBox the Values: if you want to set a specific size, you can set it here. By default, the module will set the right size for most uses, but if you find that it's too limited, you set any value. Also, this is required if you set an DropdownBox type, and instead of setting the size you should here set the values that will be offered in the box (eg: "firstval","secondval","thirdval" WITH the quotes and commas).
  • Accept null values: if enabled, the field will accept empty values. Else if disabled, a value will be required, and if an empty value is entered, the last non empty value will be used instead.
  • Default value: you can set the default value when a field has not received any value by the user yet.
  • Constraint: you can link the field to another table in the database. The field will then offer as choices the records in the remote table.
  • Custom SQL Definition: this is used to add more sql commands to the sql definition of the field (such as comments, meta parameters or transformations).
  • Custom SQL Statement: any sql command can be entered here and will be issued only ONCE after the definition of the field (thus the commands won't be issued every time the field is shown, but only once when you create the field!). This is the same as using a third-party tool such as phpMyAdmin, and is just offered as a quick shortcut.

Note: in Custom SQL Statement, do NOT do a

SELECT *

, it will be rejected by the injection protection of Dolibarr - and anyway this is totally useless as the module won't show you the result of the request, you should better try sql commands like View, Procedure, Trigger, Check, etc..

At last, you can click on the Save button to create the field.

As soon as the field is created, the page will reload and the field will show up in the list of custom fields, and also it will be immediately available in the module's datasheet page (you can go there and try it).

Note: fields are ordered by ordinal_position (order of creation - first created at the top, last at the bottom), so if you want a specific order for your fields, you are advised to carefully think what order you want. Then delete and recreate fields in the order you want them to be.

Modifying a custom field

To edit a custom field, just click on the edit button at the right hand side of the admin page (one button per field).

The same form as when you create a custom field will appear with the parameters of the field, and you can then freely edit them, and click Save to commit the changes.

Deleting a custom field

To delete a custom field, simply click on the trash button at the right hand side (just at the right of the edit button).

WARNING: no confirmation will be asked, and once a custom field is deleted you can not get it back nor get back the values saved by user, they are lost forever! Anyway you can re-create the same field, but user's values will still be lost.

Translation of a custom field's label

Fields can easily be labeled or translated using the provided language files.

Just edit /customfields/langs/code_CODE/customfields-user.lang (where code_CODE is the ISO code of your region, eg: en_US) and add inside the Variable name of your custom field (shown in the admin panel, column Variable) and the translation (format: cf_something= My Label).

Eg: let's say your custom field is named "user_ref", the resulting variable will be "cf_user_ref". In the customfields-user.lang file just add:

cf_user_ref= The label you want. You can even write a very very very long sentence here.<br />You can even do a line return with <br />.

Testing your custom fields with the PDFTest module

An auxiliary module called CustomFieldsPDFTest is provided so that you can easily test your custom fields in your PDF outputs. This avoids the need to make your own PDF template and risking to do some mistakes in the php code.

Just enable the CustomFieldsPDFTest in Home>Setup>Modules and then generate a PDF document using any template.

A page will be appended to the end of the generated PDF with an extensive list of all the available custom fields, their values and their raw values (raw value = no beautify, no html encode and no translation).

You can then see if the custom fields fits your needs and contain all the informations you will need in your final PDF template. Disable it when finished, you will make your own PDF template after (see below).

Note: already generated pdf files won't be affected, only generated PDF documents after the PDFTest module is activated will have a list of custom fields appended, and if you disable the module and generate again the PDF document, the appended page will disappear.

Implementing in ODT templates

Custom fields are automatically loaded for ODT templates.

Just use the shown variable name (Variable column in the configuration page) as a tag enclosed by two braces.

Eg: for a customfield named user_ref, you will get the Variable name cf_user_ref. In your ODT, to get the value of the field, just type

{cf_user_ref}

You can also get the raw value (without any preprocessing) by appending _raw to the variable name:

{cf_user_ref_raw}

There is also full support for constrained fields, so that if you have a constraint on this field, it will automatically fetch all the linked values of the referenced tables and you will be able to use them with tags. Eg: cf_user_ref is constrained on the llx_user table:

{cf_user_ref} = rowid
{cf_user_ref_firstname} = firstname
{cf_user_ref_user_mobile} = mobile phone
etc...

As you can see, you just need to append '_' and the name of the column you want to access to show the corresponding value.

For lines, it works just the same, you just have to put the variable name inside the table that handles the product's lines, between the tags [!-- BEGIN row.lines --] and [!-- END row.lines --]

Note: an interesting use of custom fields is to use a TrueFalseBox with a conditional substitution, eg: with a custom fields cf_enablethis:

[!-- IF {cf_enablethis_raw} --]
This is enabled and this will show.
[!-- ELSE {cf_enablethis_raw} --]
Else this will show up when disabled.
[!-- ENDIF {cf_enablethis_raw} --]

We need to use the raw value, because we need to have a 0/1 value for the conditional to work (or empty/non-empty, so this can also work for empty Textbox: if there's no text, you can avoid to show anything - this in fact works for any other sql datatypes).

Implementing in PDF templates

To use custom fields in your PDF template, you first need to load the custom fields datas, then you can use them wherever you want.

  • To load the custom fields:
// Init and main vars for CustomFields
dol_include_once(DOL_DOCUMENT_ROOT.'/customfields/lib/customfields_aux.lib.php');

// Filling the $object with customfields (you can then access customfields by doing $object->customfields->cf_yourfield)
$this->customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values
$this->customfields_raw = customfields_fill_object($object, null, $outputlangs, 'raw', null); // raw values
$this->customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines' values

Note: you can place this just after:

$pdf=pdf_getInstance($this->format);
  • To access the field's value:

Beautified formatting:

$object->customfields->cf_myfield

or for the raw value:

$object->customfields->raw->cf_myfield
  • To access a product's line's value:
$lineid = $object->lines[$i]->rowid;
$object->customfields->lines->$lineid>cf_myfield

Where $lineid must be replaced by the id of the line you want to fetch (rowid of the product, so it does NOT necessary start at 0).

  • To print it with FPDF (the default PDF generation library):
$pdf->MultiCell(0,3, $object->customfields->cf_myfield, 0, 'L'); // printing the customfield
  • And if you want to print the multilanguage label of this field :
$outputlangs->load('customfields-user@customfields');
$mylabel = $customfields->findLabel("cf_myfield", $outputlangs); // where $outputlangs is the language the PDF should be outputted to

or if you want to do it automatically (useful for a loop):

$outputlangs->load('customfields-user@customfields');
$keys=array_keys(get_object_vars($object->customfields));
$mylabel = $outputlangs->trans($keys[xxx]); // where xxx is a number, you can iterate foreach($keys as $key) if you prefer

Implementing in php code (dolibarr core modules or your own module)

One of the main features of the CustomFields module is that it offers a generic way to access, add, edit and view custom fields from your own code. You can easily develop your own modules accepting user's inputs based on CustomFields.

You can use a simplifier library that eases a lot the usage of custom fields in php codes:

dol_include_once('/customfields/lib/customfields_aux.lib.php'); // include the simplifier library
$customfields = customfields_fill_object($object, null, $langs); // load the custom fields values inside $object->customfields

customfields_fill_object() takes 5 parameters:

  • $object: the object where the custom fields datas will be set
  • $fromobject: the object from which the custom fields will be fetched (this can be used to fetch custom fields from multiple modules into one $object)
  • $langs: language translation class, to localize your custom fields values
  • $prefix: prefix to add to help you organize and avoid overwriting custom fields (eg: you have two modules and want to store into one $object, with $prefix='invoice' the first will store inside $object->customfields->invoice->cf_myfield, and second='propal' will store inside $object->customfields->propal->cf_myfield).
  • $pdfformat: beautify the customfields values? (null = no beautify nor translation; false = beautify and translate; true = translation and pdf beautify with html entities encoding)

Note: $fromobject (or $object if $fromobject=null) can be a dummy object and contain only two properties: $fromobject->id and $fromobject->table_element (which is the module's name, or table's name where the module stores its datas).

You can then access easily to the custom fields values:

print($object->customfields->cf_myfield);

To load product's lines custom fields, you can use the customfields_fill_object_line() function which takes exactly the same parameters:

dol_include_once('/customfields/lib/customfields_aux.lib.php'); // include the simplifier library
$customfields = customfields_fill_object_lines($object, null, $langs); // load the custom fields values inside $object->customfields

Then you can access the line's custom fields by using:

$object->customfields->lines->$lineid->cf_myfield

You can also manually fetch the custom fields values (called $records below):

  • First, you necessarily have to instanciate the CustomFields class:

// Init and main vars //include_once(DOL_DOCUMENT_ROOT.'/customfields/class/customfields.class.php'); // OLD WAY

       dol_include_once('/customfields/class/customfields.class.php'); // NEW WAY since Dolibarr v3.3

$customfields = new CustomFields($this->db, $currentmodule); // where $currentmodule is the current module, you can replace it by if you just want to use printing functions and fetchAny.

  • Secondly, you have the fetch the records (this will fetch ALL custom fields for ALL records):

$records = $customfields->fetchAll();

  • Thirdly, you can now print all your records this way:

if (!is_null($records)) { // verify that we have at least one result foreach ($records as $record) { // in our list of records we walk each record foreach ($record as $label => $value) { // for each record, we extract the label and the value print $label.' has value: '.$value; // Simple printing, with no beautify nor multilingual support print $customfields->findLabel($customfields->varprefix.$label).' has value: '.$customfields->simpleprintField($label, $value); // Full printing method with multilingual and beautified printing of the values. Note: We need to add the varprefix for the label to be found. For printField, we need to provide the meta-informations of the current field to print the value from, depending on these meta-informations the function will choose the right presentation. } } }

  • Full final code:

// Init and main vars //include_once(DOL_DOCUMENT_ROOT.'/customfields/class/customfields.class.php'); // OLD WAY

       dol_include_once('/customfields/class/customfields.class.php'); // NEW WAY since Dolibarr v3.3

$customfields = new CustomFields($this->db, $currentmodule); // where $currentmodule is the current module, you can replace it by if you just want to use printing functions and fetchAny. // Fetch all records $records = $customfields->fetchAll(); // Walk and print the records if (!is_null($records)) { // verify that we have at least one result foreach ($records as $record) { // in our list of records we walk each record foreach ($record as $label => $value) { // for each record, we extract the label and the value print $label.' has value: '.$value; // Simple printing, with no beautify nor multilingual support print $customfields->findLabel($customfields->varprefix.$label).' has value: '.$customfields->simpleprintField($label, $value); // Full printing method with multilingual and beautified printing of the values. Note: We need to add the varprefix for the label to be found. For printField, we need to provide the meta-informations of the current field to print the value from, depending on these meta-informations the function will choose the right presentation. } } }

  • If you want to fetch only one record:

$record = $customfields->fetch($id); // Where id is of course the id of the record to fetch.

foreach ($record as $label => $value) { // for each record, we extract the label and the value print $label.' has value: '.$value; // Simple printing, with no beautify nor multilingual support print $customfields->findLabel($customfields->varprefix.$label).' has value: '.$customfields->simpleprintField($label, $value); // Full printing method with multilingual and beautified printing of the values. Note: We need to add the varprefix for the label to be found. For printField, we need to provide the meta-informations of the current field to print the value from, depending on these meta-informations the function will choose the right presentation. }


Do's and don't to avoid slowdowns when implementing CustomFields

* In PDF templates, use customfields_fill_object_lines() only ONCE before the loop, else it will be very heavy on CPU and memory usage.
* Adding a lot of customfields or product lines won't really slow down CustomFields in any way (datasheet printing, editing field, PDF/ODT generation, etc..), thank's to caching.
* Try to avoid the number of constrained customfields: adding a lot of constrained customfields will, because CustomFields automatically lookup the remote tables for every constrained field, so that you can access any remote column in your PDF. There is a cache mechanism that tries to speed up the process, so for example if you have many different constrained customfields on the same remote table (eg: with different Smart Value Substitution prefixes), then the performance won't be tampered, but if every constrained customfields is linked to a different table, there is no way around, it will slow down. If you encounter problems, try to limit the number of constrained fields you create, or try to add SQL indexes on the remote table of the constrained field, or don't use customfields_fill_object*() functions but directly access _only_ the fields you need by using the CustomFields class or your own sql query.
* Smart value substitution for constrained fields does not hamper any performance (eg: prefix of the remote column for auto replacement of the raw value by the specified column's value).
* Try to use customfields_fill_object() and customfields_fill_object_lines() only once per script, these functions are really heavy and do all the work for you, they are optimized and cache everything they can, but the drawback is that they're heavy. An easy way is to modify directly the original $object, so that the result will be transmitted to every other parts of Dolibarr (but be careful, do it only if you know that this will not conflict). You can also choose to not use these functions at all, and use directly the CustomFields class, if you have really specific needs, you can have fastest results (eg: if you don't need to fetch all the customfields but only one or a few, or if you only need the raw values).

Constraints

Adding a constrained/linked custom field

Let's say you want to make a custom field that let you choose among all the users of Dolibarr.

With CustomFields, that's very easy: at the customfield creation, just select the table you want to link in Constraints.

In our example, you'd just have to select "llx_users", and click Create button.

All the rest is done for you, everything is managed automatically.

PowerTip1: What is great is that you are not limited to Dolibarr's tables: if a third-party module or even another software share this same database as Dolibarr, you can select their tables as well and everything will be managed the same way.

PowerTip2: If that's still not enough to satisfy your needs, you can create more complex sql fields by using the Custom SQL field at the creation or update page, the sql statement that you will put there will be executed just after the creation/update of the field, so that you can create view, execute procedures. And the custom field will still be fully managed by CustomFields core without any edit to the core code.

PowerTip3: And if that's still not enough, you can overload functions to manage your fields directly with the customfields_fields_extend.lib.php library. And yes, it also works with constrained fields.

Smart Value Substitution

If you want your constrained field to show another value than the rowid, just set your custom field's name to the name of the remote field you want to show. Eg: let's say you want to show the name of the users in the llx_users table, not the rowid. Just create a table with the "name" and it will automatically show the name fields instead of the rowid. And don't forget, in the PDF and ODT, you can access all the remote fields, not only name, but firstname, phone number, email, etc..

Also, you can specify to use not just one remote field but several, by concatenating the name of all the remote fields you want to show separated by underscores. eg: name_firstname_login will show values formatted like 'Admin John admin'. Every value is separated by a space.

This can be particularly powerful if you want to show a list of choice with sublists. eg: let's say that llx_country_regions contains a list of countries and regions, like country region France Paris France Seine-Saint-Denis Canada Quebec

And you want the user to choose not only the country but also the region, you can just create a custom fields with the name 'country_region' and it will show a list of all countries and regions, from which the user will easily choose.

Advanced configuration

Changing the default variable prefix for fields name

A prefix is automatically added to each custom field's name in the code (not in the database!), to avoid any collision with other core variables or fields in the Dolibarr core code.

By default, the prefix is "cf_", so if you have a custom field named "user_ref" you will get "cf_user_ref".

This behaviour can easily be changed by editing the $varprefix value in /htdocs/customfields/class/customfields.class.php (it's at the beginning of the file, just after "class CustomFields").

Overloading functions: Adding your own management code for custom fields

The module offers an overloading facility where you can overload (read replace) functions of CustomFields by your own.

This can be used in various ways: to change how a particular field is printed, how a particular field is saved, to add your own SQL request to manage a custom field, etc.

To overload CustomFields's functions, edit the following file: /customfields/fields/customfields_fields_extend.lib.php

You will find inside all the informations you need, here is an excerpt:

/* Decription: you can overload a few functions of CustomFields to do your own stuff, mainly the printing and the management of the fields.
 *
 * Guidelines:
 * You can use and modify any variable, CustomFields will then use them afterwards (except for 'full' functions which completely overload CustomFields, so that you must take care of everything on your own).
 *
 * To access the database using Dolibarr's core functions, you can use:
 * global $db;
 * then you can: $db->query('SELECT * FROM table');
 *
 * You also can use CustomFields functions:
 * $customfields->fetchAny('*', 'table');
 * which is the equivalent of the command above.
 *
 * You can also access a variety of other things via globals, like:
 * $conf - contain all Dolibarr's config
 * $user - current user's loggued (can access his rights to check security)
 * $langs - current language of the user (for internationalization)
 *
 * Lastly, you can access all GET and POST variables using:
 * $myvar = GETPOST('myvar');
 *
 * Usage:
 * Simply put, almost all functions are procedures: you don't have to return anything, just modify the variables you want, and then CustomFields will continue where it left.
 * Exceptions are the 'full' functions (there's a 'full' in the name): these functions will stop CustomFields from processing the rest, and you have to do the rest, namely printing (because CustomFields won't print anything if you use 'full' functions, it just stops and leave you with the keys of the house).
 *
 * There are for the moment 8 possible overloading 'event' functions:
 * editview: in datasheet, when editing a customfield, you can modify the value and any other variable, and then CustomFields will continue the processing and will try to manage the best it can (mainly by showing a proper HTML form with inputs and buttons, and the right html input for the right type of data).
 * editviewfull: in datasheet, CustomFields stops processing and you do the rest (you then have to properly print an HTML form).
 * view: in datasheet, when just printing a customfield on the datasheet, you can modify the values and then CustomFields will continue the processing and printing of the form.
 * viewfull: in datasheet, when printing a customfield on the datasheet, CF stops and you do the rest of the printing (pretty simple, it's mainly just text or HTML).
 * create: in creation page, you can change the value(s) that will be available in a customfield, and then CustomFields will continue the processing/printing.
 * createfull: in creation page, CF stops and you do the rest of the processing/printing.
 * save: at edition (on datasheet), you can change the values that will be stored in the database, and then CF will commit them (you don't have to, just change the values).
 * savefull: at edition (on datasheet), CF stops the processing and you do the rest of the commit to the database (WARNING: CF will not store the data, you have to do it!)
 * BONUS: if you want to change values saving at creation and stop CF to do the processing and commit, you can use a TRIGGER in the customfield's trigger file (eg: FACTURE_CREATE, and then you can filter which field you want to process specifically, as soon as you do a return this will stop CF to process the fields at creation).
 *
 * Format:
 * All overloading functions must be named with the following convention:
 * customfields_field_$event_$module_$fieldname
 *
 * For example, for the module invoice (facture in Dolibarr, see conf_customfields.lib.php) and a field named 'myfield' and you want to overload the 'editview' event, you can use:
 * customfields_field_editview_facture_myfield
 *
 * A list of modules can be found in conf_customfields.lib.php in the $modulesarray variable.
 *
 */

Here is a quick example that will replace a field called myfield by a DropdownBox with 5 choices (null or test1-test4) and manage the edition:

function customfields_field_viewfull_mymodule_myfield (&$currentmodule, &$object, &$parameters, &$action, &$user, &$idvar, &$rightok, &$customfields, &$field, &$name, &$value) {
    global $db;

    $myarray = array(
          array(0, ''),
          array(1, 'test1'),
          array(2, 'test2'),
          array(3, 'test3'),
          array(4, 'test4'),
          );

    print $myarray[$value][1];
}

function customfields_field_editview_mymodule_myfield (&$currentmodule, &$object, &$parameters, &$action, &$user, &$idvar, &$rightok, &$customfields, &$field, &$name, &$value) {
    global $db; // allow to access database functions

    $myarray = array(
          array('id'=>0, 'value'=>''),
          array('id'=>1, 'value'=>'test1'),
          array('id'=>2, 'value'=>'test2', 'selected'=>true),
          array('id'=>3, 'value'=>'test3'),
          array('id'=>4, 'value'=>'test4'),
          );

    $value = $myarray; // just return an array of the format above
}

For more complete example cases, you can read the Module_CustomFields_Cases page.

Adding a new sql data type

Here you will learn how to add the native support for a data type.

1/ Add the sql data type support in config Why: to show this data type as a choice in the admin page.

Open /customfields/conf/conf_customfields.lib.php and search for $sql_datatypes.

Edit the $sql_datatypes to add your own type : the key being the sql data type definition (must be sql valid), the value being the name that will be shown to the user (you can choose whatever you want). Eg:

$sql_datatypes = array('boolean' => $langs->trans("TrueFalseBox"));

Note: you can set a size or value for the sql data type definition. Eg: 'enum(\'Yes\',\'No\')' => $langs->trans("YesNoBox"), // will produce an enum with the values Yes and No. Eg2: 'int(11) => $langs->trans("SomeNameYouChoose"), // will produce an int field with size 11 bits

Result: now the CustomFields module know that you intend to support a new data type, and you can asap use it in the admin page: try to add a custom field with this data type, it should work (if your sql definition is correct). You must now tell it how to manage (edit) it and how to print it.

2/ Manage the new data type (implement the html input field) Why: CustomFields must know how to manage this particular datatype you've just added.

Open /customfields/class/customfields.class.php and edit the showInputField() function. Plenty of examples and comments are provided inside, it should be pretty easy.

As a guide, you should probably take a look below the comment

// Normal non-constrained fields

first, these are the simplest data types (above it concerns only constrained fields which is more dynamic and more complicated).

Result: when going to the datasheet of a module supported by CustomFields, try to edit the custom field you created with this data type: you should see the input you've just implemented.

3/ Print correctly the data type

Here we will implement a printing function that will best represent the data type when not editing, just viewing the data in the datasheet

Why: At this stage, your data type should be printed as it is in the database, but you may want to print it differently so that it is more meaningful to a user (eg: for the TrueFalseBox, it's way better to show True or False than 1 or 0).

Open /customfields/class/customfields.class.php and edit the printField() function. Comments will guide you.

Result: now your data type prints a meaningful representation of the data in the datasheet.

4/ Optional: translate the name of the data type and the values Why: CustomFields fully supports multilanguage, so you can easily translate or put a longer description with the langs files.

You can find them at /customfields/langs/code_CODE/customfields.lang or customfields-user.lang

How to add the support of a new module

There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.

This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).

NEW WAY (simpler)

0/ Preliminary work You will need 3 things here: the module's table name, the module's context and the module's trigger action(s).

To get these informations, you can take a look inside the code of the module's files you want to implement:

or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters->context

  • module's trigger actions: search for "run_triggers(" or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions
  • module's table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding print_r($parameters) and search for $parameters->table_element

1/ With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:

A- Add the context and module's table name in the modulesarray: $modulesarray = array("invoicecard"=>"facture",

                                           "propalcard"=>"propal",
                                           "productcard"=>"product",
                                           "ordercard"=>"commande",

"yourmodulecontext"=>"yourmoduletablename"); // Edit me to add the support of another module - NOTE: Lowercase only!

B- Add the triggers (there can be several triggers): $triggersarray = array("order_create"=>"commande", "yourmoduletriggeraction1"=>"yourmoduletablename", "yourmoduletriggeraction2"=>"yourmoduletablename");

Note: generally you will be looking to implement the _CREATE and _PREBUILDDOC actions.

Done, now the module should be fully supported! If that's not the case, try the following:

- If you can save and create but cannot generate the document, see the 4th step for implementing the PREBUILDDOC trigger. - If you can neither save nor create the fields, you just see them but changes are not committed when submitting, try to implement the support via the old way (which should work all the time for any case).

Developpers Notes

These notes are aimed to developpers that want to extend the functionnalities of the module or want to use it for extreme cases. This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).

Principles

CustomFields is simply a wrapper for SQL databases.

The module is based on SQL to manage the fields and most of the stuff. It uses and understand standard SQL.

The main principle of CustomFields is:

  • one SQL field = one Dolibarr field
  • one SQL type = one way to show and edit the field in Dolibarr

Goals

This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:

  • by easily adding the support of custom fields for your own module by using this module;
  • by reusing custom fields from another module in your own module;
  • by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).

So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.

SQL structure

A CustomFields table needs to respect three parameters that never change:

  • table's name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)
  • first column: rowid, which is the primary key for each field
  • second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr's module that is associated (invoice, order, etc...).

Apart from the table's name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module's administration page or from a third-party SQL management tool (such as phpMyAdmin).

You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).

Then the module will try to manage the sql fields you manually created the best it can:

  • by printing appropriate input fields in the Dolibarr gui interface.
  • by providing the developper an easy access to these fields in php (eg: $customfields->cf_myfield or $object->customfields->cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).

SQL Indexes

The module automatically adds an index on the primary key and foreign key (first and second columns).

To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.

Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.

Architecture

Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):

Core files

files that are necessary for the CustomFields to work, they contains the core functions

/customfields/admin/customfields.php --- Administrator's configuration panel : this is where you create and manage the custom fields definitions /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too) /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic. /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types. /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables /customfields/core/modules/modCustomFields.class.php --- Dolibarr's module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations). /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. This is where you must add the actions of other modules you'd want to support (generic customfields triggers actions are provided so you just have to basically do a copy/paste, see the related chapter). Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?). /customfields/fields/customfields_fields_extend.lib.php --- Overloading functions library with users functions /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.) /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter) /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern) /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there's no really core functions but it permits to manage the printing of the custom fields records and their editing /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php)


PDFTest module

optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one's own template.

/customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions. /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr


Troubleshooting

Q: I'm trying to edit a constrained customfield parameters in the admin configuration page, but everytime I change the constraint it goes back to None ?

A: This is behaviour is probably due to some of your records containing an illegal value for the new constraint. For example, if you switch your customfield's constraint from your products' table containing 100 products to your you choose the llx_users table containing 2 users, the database won't know what to do with the illegal values higher than 2, so it won't accept the new constraint and set to None. In this case, just edit yourself the illegal values, either by fixing them or just deleting all the values for this customfields (but in this case you can just delete the customfields and recreate it).

Q: I'm trying to delete a constrained customfield in the admin configuration page, but it doesn't work!

A: This is a normal behaviour. This means that there are a few items in dolibarr (invoices/propale/orders or whatever module you're using this customfield with) that still use this constrained field, and to protect the integrity of your database, your DBMS prevent the deletion. Simple fix: either reset to an NULL (empty) value all items using the constrained field, or either delete manually in your DBMS (eg: using phpmyadmin) the constraints and then the field.

TO DO

To Do:

  • Add a confirmation when deleting a custom field (because user's datas are lost forever!).

Should do :

  • Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can't store multiple foreign keys ids in one single sql field, but would work for other types like enum)

use fieldset, input checkbox and label for, eg:

   <fieldset><legend><label for="cf_productslist"> $langs->trans('cf_productslist')</label></legend>
   <input type="checkbox" name="cf_productslist_choice1" value="1" id="cf_productslist_choice1" /><label for="cf_productslist_choice1"> $langs->trans('cf_productslist_choice1')</label>
<input type="checkbox" name="cf_productslist_choice2" value="1" id="cf_productslist_choice2" /><label for="cf_productslist_choice2"> $langs->trans('cf_productslist_choice2')</label>
etc... </fieldset>
  • Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: 'linebill_insert'=>'customfields_create').
  • Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).

Known bugs :

  • in product and service modules, if you edit a field, the proposals and other fields below won't be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).

Never/Maybe one day :

  • Add Upload field type (almost useless since we can attach files).
  • Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it's managed by a totally different table while it's still managed by the same module, CustomFields work with the paradigm: one module, one table).
  • AJAX? (but only optional, because else it won't work for mobile phones and tablets)
  • Extend meta-data storage using XML files?
  • Add an AJAX select box for constained values : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the "nom" field and then it would automatically prepend "nom_" to the field's name).
  • A way to reorder the fields without deleting/recreating (add a third special column after fk_module, or in XML meta-data file?)