LOGIC Library

This site is the Logic involvement in sharing expertise and skills acquired in daily work. The goal is to create a solid knowledge base and share best practices in software development and systems management.

More info about us can be found on logicsistemi.it.

Developing MVC components for Joomla! 2.5 - Part 10

Our component is now working, however, this last post add 2 features that, in a production component, will be certainly present: filters and validation of fields.

Finally we clean up the component from the development parts, in order to create a distributable package.

The filters

As a starting example we add a simple filter that consists of a text box that will search in the name or surname.

You can expand the example to other filters, and if you want to add dropdown menus to select items, I recommend you to starting from a component that already exists to see how it's done. In this regard, the standard banner management of the Joomla! core iis excellent.

We start by changing the template of the list view (/admin/views/persons/tmpl/default.php).

<?php 
// no direct access
defined( '_JEXEC' ) or die( 'Restricted access' );

$option = JRequest::getCmd('option');
$view = JRequest::getCmd('view');
$listOrder = $this->state->get('list.ordering');
$listDirn = $this->state->get('list.direction');
?>
<form action="index.php" method="post" name="adminForm" id="adminForm">
    <input type="hidden" name="option" value="<?=$option?>" />
    <input type="hidden" name="task" value="" />
    <input type="hidden" name="view" value="<?=$view?>" />
    <input type="hidden" name="filter_order" value="<?=$listOrder?>" />
    <input type="hidden" name="filter_order_Dir" value="<?=$listDirn?>" />    
    <input type="hidden" name="boxchecked" value="0" />
    <?php echo JHtml::_('form.token'); ?>
    
    <fieldset id="filter-bar">
        <div>
            <label for="filter_search"><?php echo JText::_('JSEARCH_FILTER_LABEL'); ?></label>
            <input type="text" name="filter_search" id="filter_search"
               value="<?php echo $this->escape($this->state->get('filter.search')); ?>"
               title="<?php echo JText::_('COM_REGISTRY_RICERCA_PERSONS'); ?>" />
            <button type="submit"><?php echo JText::_('JSEARCH_FILTER_SUBMIT'); ?></button>
            <button type="button" onclick="document.id('filter_search').value='';this.form.submit();">
               <?php echo JText::_('JSEARCH_FILTER_CLEAR'); ?>
            </button>
        </div>
    </fieldset>
    <div class="clr"> </div>
    
    <div id="editcell">    
        <table class="adminlist">
            <thead>
                <tr>
                    <th width="1%">
                        <input type="checkbox" onclick="Joomla.checkAll(this)" title="<?=JText::_( 'JGLOBAL_CHECK_ALL' )?>" value="" name="checkall-toggle">
                    </th>
                    <th>
                        <?=JHtml::_('grid.sort', 'COM_REGISTRY_PERSONS_NAME', 'name', $listDirn, $listOrder); ?>
                    </th>
                    <th>
                        <?=JHtml::_('grid.sort', 'COM_REGISTRY_PERSONS_SURNAME', 'surname', $listDirn, $listOrder); ?>
                    </th>
                    <th>
                        <?=JHtml::_('grid.sort', 'JGLOBAL_FIELD_ID_LABEL', 'id', $listDirn, $listOrder); ?>
                    </th>
                </tr>
            </thead>
            <tfoot>
                <tr>
                    <td colspan="4">
                        <?=$this->pagination->getListFooter()?>
                    </td>
                </tr>
            </tfoot>
            <tbody>
<?
        $k = 0;
        $i = 0;
        foreach ($this->items as &$row)
        {
            $checked = JHTML::_('grid.id', $i++, $row->id );
            $link = JRoute::_( 'index.php?option=' . $option . '&task=person.edit&id=' . $row->id );
?>
                <tr class="row<?=$k?>">
                    <td><?=$checked?></td>
                    <td>
                        <a href="/<?=$link?>">
                            <?=$row->name?>
                        </a>
                    </td>
                    <td>
                        <a href="/<?=$link?>">
                            <?=$row->surname?>
                        </a>
                    </td>
                    <td>
                        <a href="/<?=$link?>">
                            <?=$row->id?>
                        </a>
                    </td>
                </tr>
<?
            $k = 1 - $k;
        }
?>
            </tbody>
        </table>
    </div>
</form>

We just added a little of HTML to display our text box, a submit button and a button to clean up the filters.

These components are very common and, as you can see, they already have adequate labels defined in Joomla!. We have only to insert the translation for the tooltip, 'cause we want a personalized message. I don't write the translation files (you can found it in the package), but try to insert the new entry by tour own.

For the rest I think that the code does not need more explanations. Upon submit the text is sent to the server along with the hidden fields that contain all the information to ensure that we continue to see the list page.

Now a question: where will be inseted the code for the search?

If you answered in the model, the MVC pattern is clear to you, otherwise look back again to the division between the different elements.

The model will become, in fact, the following:

<?php
defined('_JEXEC') or die();
jimport( 'joomla.application.component.modellist' );
class RegistryModelPersons extends JModelList
{
    public function __construct($config = array())
    {
        if (empty($config['filter_fields'])) {
            $config['filter_fields'] = array('id', 'name', 'surname');
        }
        parent::__construct($config);
    }
    function getListQuery()
    {
        $db = JFactory::getDBO();
        $query = $db->getQuery(true);
        $query->select('id, name, surname');
        $query->from('#__registry_persons as persons');
        // Filter by search in title
        $search = $this->getState('filter.search');
        if (!empty($search)) {
            $search = $db->Quote('%'.$db->escape($search, true).'%');
            $query->where('((#__registry_persons.name LIKE '.$search.')
                OR (#__registry_persons.surname LIKE '.$search.'))');
        }
        $query->order($this->getState('list.ordering', 'id').' '.
           $this->getState('list.direction', 'ASC'));
           return $query;
    }
    protected function populateState($ordering = null, $direction = null)
    {
        // Load the filter state.
        $search = $this->getUserStateFromRequest($this->context.'.filter.search', 
           'filter_search');
        $this->setState('filter.search', $search);
        // List state information.
        parent::populateState($ordering, $direction);
    }
}

In this case, the changes are more substantial and require some explanation.

The function populateState is called whe submit appens and sets the session variables for the state of Joomla! that keep the inserted informations when you change page. This mechanism is useful to ensure that when the user returns to the list are preserved the filters previously set.

First of all is the previous state was recovered and, if the page request is present the parameter filter_search, the status is updated.

Immediately after the new information is stored permanently in session and is called the parent of populateState to take care of other variables (such as pagination).

In getListQuery is simply built the where part of the query using, as a filter, the state and not the values that come from the request. For convenience we have defined a nickname for the table # __registry_persons.

The game is made and the filters are working!

JavaScript validation of fields

Joomla! performs the Javascript validation of the fields using Mootools. The operation of this library can be configured directly from the xml file that defines the different fields.

The various fields, in fact, can be easily specified as mandatory and there are standard validations ready to be used. We only have to enable this validation. The activation of the validation is done by specifying the appropriate classes.

Some modification is done in the detail page. There has already been inserted the necessary code and it is:

  • the instruction JHtml::_(‘behavior.formvalidation’); that insert required Javascript libraries
  • the class form-validate set on the form

The xml file, instead, become the following:

<?xml version="1.0" encoding="UTF-8"?>
<form>
    <fieldset>
        <field 
            name="id" 
            type="hidden" />
        <field 
            name="name" 
            type="text" 
            label="COM_REGISTRY_PERSONS_NAME"
            size="40"
            class="inputbox"
            required="true" />
        <field 
            name="surname" 
            type="text" 
            label="COM_REGISTRY_PERSONS_SURNAME"
            size="40"
            class="inputbox validate-minlenght2"
            required="true" />
    </fieldset>
</form>

Were added only the required attributes. Some of mootols standard validation can be done by setting the appropriate classes, the full list is as follows:

  • required
  • validate-username
  • validate-password
  • validate-numeric
  • validate-email

But others can be defined directly by us and we've done it by creating the minlength2 type that it's activated by creating the file person.js in the folder /admin/models/forms with the following contents:

window.addEvent('domready', function() {
    document.formvalidator.setHandler('minlenght2',
        function (value) {
            if (value.length < 2) {
                return false;
            } else {
                return true;
            }
    });
});

It defines a mechanism for validating the surname field. The mechanism is simple and with little sense, but I think it's good for teaching purposes.

Add the file person.js to the page changing the view in the following way:

<?php
// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die();
jimport( 'joomla.application.component.view' );
class RegistryViewPerson extends JView
{
    function display($tpl = null)
    {
    	// Get data from the model
	$item = $this->get( 'Item' );
	$form = $this->get( 'Form' );
	$isNew = ($item->id < 1);
	// Disable main menu
	JRequest::setVar('hidemainmenu', true);
	// Toolbar
	if ($isNew) {
	   JToolBarHelper::title( JText::_('COM_REGISTRY_PERSONS_NEW'), 
             'generic.png' );
	} else {
	   JToolBarHelper::title( JText::_('COM_REGISTRY_PERSONS_EDIT'), 
             'generic.png' );
	}
    	JToolBarHelper::apply('person.apply');
    	JToolBarHelper::save('person.save');
    	JToolBarHelper::save2new('person.save2new');
    	JToolBarHelper::cancel('person.cancel', $isNew ? 
           'JTOOLBAR_CANCEL' : 'JTOOLBAR_CLOSE');
    	$this->item = $item;
    	$this->form = $form;
    	parent::display($tpl);
    	$document = JFactory::getDocument();
    	$document->addScript(JURI::root() .
    	   '/administrator/components/com_registry/models/forms/person.js');
    	$document->addScript(JURI::root() . 
           '/administrator/components/com_registry/models/forms/validation.js');
        JText::script('COM_REGISTRY_VALIDATION_ERROR');
    }   
}

As you see, in addition to the newly created file, another file was added. The validation, in fact, is already working, but does not block the submit of the form. To make the validation blocker let's create the file validation.js with the following contents:

// Generic validation
Joomla.submitbutton = function(task)
{
	if (task == '') {
		return false;
	} else {
		var isValid=true;
		var action = task.split('.');
		if (action[1] != 'cancel' && action[1] != 'close') {
			var forms = $$('form.form-validate');
			for (var i=0;i<forms.length;i++) {
				if (!document.formvalidator.isValid(forms[i])) {
					isValid = false;
					break;
				}
			}
		}
		if (isValid) {
			Joomla.submitform(task);
			return true;
		} else {
			alert(Joomla.JText._('COM_REGISTRY_VALIDATION_ERROR'));
			return false;
		}
	}
}

The possibility of using the translation constants within the script is guaranteed by the function JText::script that creates the necessary JS code in our page.

Remember to add the tranlsation in the right file and validation should be complete.

Server-side fields validation

The validation of fields server-side is much easier to realize. Essentially it consists in modifying the XML form definition file defining the folder that contains the validation rules and specifying, for each field, which rule to use.

We start from the form:

<?xml version="1.0" encoding="UTF-8"?>
<form addrulepath="/administrator/components/com_registry/models/rules">
    <fieldset>
        <field 
            name="id" 
            type="hidden" />
        <field 
            name="name" 
            type="text" 
            label="COM_REGISTRY_PERSONS_NAME"
            size="40"
            class="inputbox"
            required="true" />
        <field 
            name="surname" 
            type="text" 
            label="COM_REGISTRY_PERSONS_SURNAME"
            size="40"
            class="inputbox validate-minlenght2"
            required="true"
            validate="surname" />
    </fieldset>
</form>

It was added the addrulepath attribute that indicates the path of the rules folder: it is the folder where we will put the rule classes.

It was also added the validate attribute on the field to be validated. It defines the rule to use. Because the classes can be combined and rules cannot, I think it's more appropriate to define the generic name for the Javascript (minlenght2) and create, instead, specific rules for each field for the PHP (surname). This, however, it is my choice.

To complete the validation we create in /admin/models/rules the file surname.php with the following content.

<?php
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
// import Joomla formrule library
jimport('joomla.form.formrule');
class JFormRuleSurname extends JFormRule
{
    protected $regex = '^.{2,}$';
}

The class JFormRule contains only one method, test, which is used to verify the field. We can proceed in 2 ways: we can rewrite the test method or use it to as it was built validating the field value using a regular expression.

We chose the second method and we redefine the attribute $regex by assigning a regular expression that will be applied by the text method.

Creating the package

We complete the package structure by adding the new files and removing the various update queries: we get what is, in fact, the first version of the redistributable package that will call 1.0.0, so all the updates introduced for development purposes may be removed.

com_registry
  admin
    controller.php
    index.html
    registry.php
    controllers
      index.html
      person.php
      persons.php
    language
      en-GB
        index.html
        en-GB.com_registry.ini
        en-GB.com_registry.sys.ini
    models
      forms
        index.html
        person.js
        person.xml
        validation.js
      rules
        surname.php
      index.html
      person.php
      persons.php
    sql
      index.html
      install.sql
      uninstall.sql
      updates
        index.html
        1.0.0.sql
    tables
      index.html
      person.php
    views
      index.html
      person
        index.html
        view.html.php
        tmpl
          edit.php
          index.html
      persons
        index.html
        view.html.php
        tmpl
          default.php
          index.html
  site
    index.html
    registry.php
  registry.xml

While the file registry.xml must be modified in the following way:

<?xml version="1.0" encoding="UTF-8"?>
<install type="component" version="2.5.0" method="upgrade">
    <name>Registry</name>
    <author>Edy Incoletti</author>
    <authorEmail>edy.incoletti@logicsistemi.it</authorEmail>
    <authorUrl>http://library.logicsistemi.it</authorUrl>
    <creationDate>May 2012</creationDate>
    <version>1.0.0</version>
    <description>Registry management for the tutorial.</description>
    
    <files folder="site">
        <filename>index.html</filename>
        <filename>registry.php</filename>
    </files>

    <install folder="admin">
        <sql>
            <file charset="utf8" driver="mysql">sql/install.sql</file>
        </sql>
    </install>
    <uninstall folder="admin">
        <sql>
            <file charset="utf8" driver="mysql">sql/uninstall.sql</file>
        </sql>
     </uninstall>
    <update>
        <schemas>
            <schemapath type="mysql">sql/updates</schemapath>
        </schemas>
    </update>

    <administration>
        <menu>COM_REGISTRY</menu>
        <files folder="admin">
            <filename>index.html</filename>
            <filename>registry.php</filename>
            <filename>controller.php</filename>
            <folder>controllers</folder>
            <folder>language</folder>
            <folder>models</folder>
            <folder>sql</folder>
            <folder>tables</folder>
            <folder>views</folder>
        </files>
    </administration>
</install>

Again we have removed everything relating to the upgrade, changed the version number and date of issue of the package. You can find his work here.

We can go on and on in a never ending tutorial, but with this latest informations all the important features you need to know to develop components for Joomla! according to the MVC pattern have been examined.

I hope my work will help you!

Comments   

 
#1 FlashDark 2013-03-05 01:23
Your tutorials are very straitforward, without any extra stuff. This was really helpful. Thanks :)
Quote
 
 
#2 Dharmesh Vasani 2013-03-15 12:29
Nice work dear
Thanks a lot.
I love the style you have explanied
Quote
 
 
#3 Joe Hildreth 2013-03-24 05:57
If I used a custom form field and my model pulls data from a separate database table, how can I populate the custom form field with this data when I edit a record.

For example, I have a list of hobbies that a person has. I select the hoobies from a hobbies table by pulling a list of hobbies by that person. How can I get this list of hobbies in my custom form element?
Quote
 
 
#4 Joe Hildreth 2013-03-24 05:57
GREAT tutorial by the way!
Quote
 
 
#5 Edy Incoletti 2013-03-25 11:52
Quoting Joe Hildreth:
If I used a custom form field and my model pulls data from a separate database table, how can I populate the custom form field with this data when I edit a record.

For example, I have a list of hobbies that a person has. I select the hoobies from a hobbies table by pulling a list of hobbies by that person. How can I get this list of hobbies in my custom form element?

The preprocessForm method of the model is the right place to do that (http://api.joomla.org/11.4/Joomla-Platform/Application/JModelForm.html#methodpreprocessForm).
Quote
 
 
#6 Carlos 2013-04-01 00:37
What is the best way to have a field or fields to capture date and time then save to one datetime column in the database?
Quote
 
 
#7 Edy Incoletti 2013-04-02 14:36
Quoting Carlos:
What is the best way to have a field or fields to capture date and time then save to one datetime column in the database?

The standard type for date in Joomla is calendar. It doesn't manage time, but if you specify this format option "%Y-%m-%d %H:%M:%S" you can have a datetime controller such that used in the articles management.
If you need something more the best way is to write your own field extension, maybe using mootols or jquery plugins.
Quote
 
 
#8 Marco 2013-04-14 04:24
That's really great.
Man can learn a lot.
But why do I find nothing for the Frontend?.
Tutorial for the Frontend, with Order Forms would be nice.
Thanks for your good work
Quote
 
 
#9 Edy Incoletti 2013-04-22 08:20
Quoting Marco:
That's really great.
Man can learn a lot.
But why do I find nothing for the Frontend?.
Tutorial for the Frontend, with Order Forms would be nice.
Thanks for your good work



Hi Marco,

The official tutorial ( http://docs.joomla.org/Developing_a_Model-View-Controller_%28MVC%29_Component_for_Joomla!2.5 ) starts from the front end. Only thing to consider: you don't need another model: front end use the admin model!
Quote
 
 
#10 Hardik 2013-04-22 12:45
Great!!! it's very helpful for me..thank you very much,,,
Quote
 
 
#11 Tushar Barate 2013-04-24 14:56
Nicely explained dude.

but i got the error

( ! ) Warning: Invalid argument supplied for foreach() in D:\DreamHouse\a dministrator\co mponents\com_se lls\views\sells \tmpl\default.p hp on line 68

what is the actual problem??
Quote
 
 
#12 Edy Incoletti 2013-04-30 08:23
Quoting Tushar Barate:
Nicely explained dude.

but i got the error

( ! ) Warning: Invalid argument supplied for foreach() in D:\DreamHouse\administrator\components\com_sells\views\sells\tmpl\default.php on line 68

what is the actual problem??


It should be an error in your query in the model. Check getListQuery and try to get the generated query before the return with $query->dump();
Quote
 
 
#13 Tushar Barate 2013-04-30 13:01
Hi Edy ,

You have explained nicely about component in back end, How would I suppose to display in the front end in the click of menu items???
Quote
 
 
#14 Scot H 2013-05-17 19:43
Great tutorial. Learned quite a bit from it. When using server side field validation, how can I keep the field values from disappearing? For instance, lets say a field must be numeric and they enter "a". It gives an error but the field resets itself to the default value.
Quote
 
 
#15 Edy Incoletti 2013-05-20 08:10
Quoting Scot H:
GWhen using server side field validation, how can I keep the field values from disappearing?


This topic requires a post and I will write it in the future. For now you can inspire from the post http://library.logicsistemi.it/en/joomla/general-topics/56-joomla-2-5-user-plugin-preserving-values-during-validation.
The key in the onContentPrepar eData method.
Quote
 
 
#16 StefanA 2013-06-01 22:16
Quoting Edy Incoletti:
Quoting Tushar Barate:
Nicely explained dude.

but i got the error

( ! ) Warning: Invalid argument supplied for foreach() in D:\DreamHouse\administrator\components\com_sells\views\sells\tmpl\default.php on line 68

what is the actual problem??


It should be an error in your query in the model. Check getListQuery and try to get the generated query before the return with $query->dump();


$query->where(' ((persons.name LIKE '.$search.')
OR (persons.surnam e LIKE '.$search.'))') ;

needs to be changed into

$query->where('((#__registry_persons.name LIKE '.$search.')
OR (#__registry_pe rsons.surname LIKE '.$search.'))') ;
Quote
 
 
#17 StefanA 2013-06-01 22:21
Hi Edy,

That's what I call a tutorial, much better than the "official" one.

Unfortunatly I get this error when trying to edit or create a new person:
Fatal error: Call to a member function getFieldset() on a non-object in D:\xampp\htdocs \joomla25nrwken do\administrato r\components\co m_registry\view s\person\tmpl\e dit.php on line 19

Can you help me what I made wrong?
Quote
 
 
#18 Edy Incoletti 2013-06-03 08:37
Quoting StefanA:
Quoting Edy Incoletti:
Quoting Tushar Barate:
Nicely explained dude.

but i got the error

( ! ) Warning: Invalid argument supplied for foreach() in D:\DreamHouse\administrator\components\com_sells\views\sells\tmpl\default.php on line 68

what is the actual problem??


It should be an error in your query in the model. Check getListQuery and try to get the generated query before the return with $query->dump();


$query->where('((persons.name LIKE '.$search.')
OR (persons.surname LIKE '.$search.'))');

needs to be changed into

$query->where('((#__registry_persons.name LIKE '.$search.')
OR (#__registry_persons.surname LIKE '.$search.'))');

Thank you, I've corrected the code!
Quote
 
 
#19 Edy Incoletti 2013-06-03 08:39
Quoting StefanA:
Hi Edy,

That's what I call a tutorial, much better than the "official" one.

Unfortunatly I get this error when trying to edit or create a new person:
Fatal error: Call to a member function getFieldset() on a non-object in D:\xampp\htdocs\joomla25nrwkendo\administrator\components\com_registry\views\person\tmpl\edit.php on line 19

Can you help me what I made wrong?

Download the package and check your code against it. Perhaps you mispelled something.
Quote
 
 
#20 Eric 2013-06-20 15:33
Hi Edy,

this tutorial rocks. Thanks a lot as I was searching for something understandable :-/.

Now I have a question, I downloaded your last version, I installed it under a Joomla 2.5.11 French version, but I had to change the
Quote
 
 
#21 Rajesh Kumar 2013-09-20 01:24
Hello sir,

what a nice explaination , plz keep it up. Now i think i will certainly work on custom components.
Overall, It was for appreciation.
Quote
 
 
#22 Rajesh Kumar 2013-09-20 01:25
Very nice!!!!!!!!
Quote
 
 
#23 wasim 2014-02-17 14:00
hey its nice tutorial. but can you add some screen shots so it makes more clear what you have done.
Quote
 
 
#24 Roxana Boga 2014-02-19 19:50
Hi, I have one question (I'm new at this). I made the back end and the front end, but for example I have a page that displays all the persons, but I don't know how to set the view if I want to click on a certain person to view the details in another page.
PS: This is the best tutorial I have ever read.
Quote
 
 
#25 Edy Incoletti 2014-02-20 09:20
Quoting Roxana Boga:
Hi, I have one question (I'm new at this). I made the back end and the front end, but for example I have a page that displays all the persons, but I don't know how to set the view if I want to click on a certain person to view the details in another page.
PS: This is the best tutorial I have ever read.

Look at parte 7 /en/joomla/developing-joomla-25-mvc-components/29-developing-mvc-components-joomla25-part7 of the tutorial. You will find the link to access a detail page:

$link = JRoute::_( 'index.php?opti on=' . $option . '&task=person.e dit&id=' . $row->id );

option is the component, while task is translated into controller and view.
Quote
 
 
#26 Roxana Boga 2014-02-20 09:49
Quoting Edy Incoletti:
Quoting Roxana Boga:
Hi, I have one question (I'm new at this). I made the back end and the front end, but for example I have a page that displays all the persons, but I don't know how to set the view if I want to click on a certain person to view the details in another page.
PS: This is the best tutorial I have ever read.

Look at parte 7 /en/joomla/developing-joomla-25-mvc-components/29-developing-mvc-components-joomla25-part7 of the tutorial. You will find the link to access a detail page:

$link = JRoute::_( 'index.php?option=' . $option . '&task=person.edit&id=' . $row->id );

option is the component, while task is translated into controller and view.


Thank you, but I didn't put the right question. I mean, how do I do this link in front end (not in back end)?
Quote
 
 
#27 Edy Incoletti 2014-02-20 16:21
Quoting Roxana Boga:

Thank you, but I didn't put the right question. I mean, how do I do this link in front end (not in back end)?

The official Joomla howto (the link is in the part 1 of this tutorial) starts from the front end.
Concepts are the same: create the entry point php file, the controller and the view and Joomla select the right view for you.
Quote
 
 
#28 Roxana Boga 2014-02-20 19:17
Quoting Edy Incoletti:
Quoting Roxana Boga:

Thank you, but I didn't put the right question. I mean, how do I do this link in front end (not in back end)?

The official Joomla howto (the link is in the part 1 of this tutorial) starts from the front end.
Concepts are the same: create the entry point php file, the controller and the view and Joomla select the right view for you.

Ok, thank you for your time. I read that tutorial 4 times (plus other unusing tutorials) and I don't think I am the only one with this problem. As I said my list of persons is showing but when I try to reach the page with one person's details, it doesn't work... otherwise I wouldn't place a question here. But thank you for this tutorial, it is great and thank you for your answers.
Quote
 
 
#29 twim32 2017-07-10 07:05
thanks! very good tutorial!
Quote