J3.x

Developing an MVC Component/Adding a front-end form: Difference between revisions

From Joomla! Documentation

Marked this version for translation
Robbiej (talk | contribs)
first draft - checkpoint save
Line 3: Line 3:
en</translate>}}
en</translate>}}


<translate>
== Web Page Under Construction !!!==
== Notes == <!--T:2-->
 
</translate>
<translate><!--T:3--> This tutorial is part of the [[S:MyLanguage/J3.x:Developing_an_MVC_Component/Introduction|Developing a MVC Component for Joomla! 3.x]] tutorial. You are encouraged to read the previous parts of the tutorial before reading this.</translate>
<translate><!--T:3--> This tutorial is part of the [[S:MyLanguage/J3.x:Developing_an_MVC_Component/Introduction|Developing a MVC Component for Joomla! 3.x]] tutorial. You are encouraged to read the previous parts of the tutorial before reading this.</translate>


'''YOUR CONTENT HERE'''
== Introduction ==
In this step we add a form to the site front end to allow users to add a new Helloworld record. You could adapt the code in this step to provide a range of front-end data entry functionality, from, eg
* a registration form to allow visitors to the site to express interest, to,
* a facility to allow suppliers to maintain the data in certain tables, but without giving them access to the back end administration functionality.
 
== Functionality ==
In this step we will build the following functionality
* a front-end form that allows someone to enter a new helloworld record, together with Save and Cancel buttons.
* additional fields in the helloworld database table to capture the user who entered the record, and the creation date/time
* validation of the data entered into the fields, including removing dangerous items such as html tags
* access control on the form, controlling who can add a new record
* an email to an administrator, to indicate that someone has entered a new record (and we'll include a field on the form where the user can include some text which just goes into the email)
* a captcha on the form
* a front-end menu item which points to the form (ie how users get access to the form)
* update to the admin helloworlds view to include the author and creation date in the display.
 
== Approach ==
Providing a form on the site front end is pretty much the same as on the admin back end, so much of what is developed in this step reflects what was done in the step [[S:MyLanguage/J3.x:Developing an MVC Component/Adding backend actions|Adding backend actions]], and in particular the Edit form for helloworld messages.
 
For this form we'll use (all in the site part)
* the same main helloworld controller (controller.php)
* a new view – which we'll call "form"
* a new layout, associated with the form view, which we'll call "edit" (like the admin layout file)
* a new model – which we'll call "form" also, to align with the view
* an xml file for the form, which we'll put in add-form.xml in the model
 
In addition we'll need to handle the HTTP POST requests arising from the user pressing the Save and Cancel buttons, and we'll do this in
* a new helloworld controller in the controllers directory (handling Save and Cancel)
* the same form.php model as above (handling Save)
 
Upon completion of Save/Cancel we'll redirect back to the same form.
 
As for the admin case, we'll consider our classes extending some of the controller and model classes which are richer in functionality that the simple legacy ones
* the model will extend JModelAdmin (which supports forms, as well as saving records to the database) - even though it's called JModelAdmin it's still available within the site part
* the controller handling the POST will extend JControllerForm (which includes the cancel() and save() methods).
 
Some of the other aspects in this step will involve using the techniques covered in earlier steps, namely [[S:MyLanguage/J3.x:Developing an MVC Component/Adding a menu type to the site part|Adding a menu type to the site part]], [[S:MyLanguage/J3.x:Developing an MVC Component/Adding verifications|Adding verifications]], [[S:MyLanguage/J3.x:Developing an MVC Component/Adding configuration|Adding configuration]] and [[S:MyLanguage/J3.x:Developing an MVC Component/Adding ACL|Adding ACL]].
 
==Adding a menu item==
To enable a menu item to point to the form, we need to put an xml file into the associated layout directory. Extend your directory structure (remembering to include the index.php file in each new directory) and put the following into
 
<span id="site/views/form/tmpl/edit.xml">
<tt>site/views/form/tmpl/edit.xml</tt>
<source lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<metadata>
<layout title="COM_HELLOWORLD_ADD_VIEW_TITLE">
<message>COM_HELLOWORLD_ADD_VIEW_DESC</message>
</layout>
</metadata>
</source>
</span>
 
==Displaying the new form view==
Create a new forms subdirectory under the site models directory, and add the definition of the form into
 
<span id="site/models/forms/add-form.xml">
<tt>site/models/forms/add-form.xml</tt>
<source lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<form
    addrulepath="/administrator/components/com_helloworld/models/rules"
    >
    <fieldset
name="details"
label="COM_HELLOWORLD_HELLOWORLD_DETAILS"
>
<field
name="id"
type="hidden"
/>
<field
name="greeting"
type="text"
label="COM_HELLOWORLD_HELLOWORLD_GREETING_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_GREETING_DESC"
size="40"
class="inputbox"
validate="greeting"
required="true"
                hint="COM_HELLOWORLD_HELLOWORLD_GREETING_HINT"
default=""
/>
        <field
name="catid"
type="category"
extension="com_helloworld"
class="inputbox"
default=""
label="COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_DESC"
required="true"
                >
<option value="0">JOPTION_SELECT_CATEGORY</option>
</field>
        <field
                name="message"
                type="textarea"
                rows="5"
                cols="80"
label="COM_HELLOWORLD_HELLOWORLD_MESSAGE_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_MESSAGE_DESC"
                hint="COM_HELLOWORLD_HELLOWORLD_MESSAGE_HINT"
required="true"
                >
        </field>
        <field
name="captcha"
type="captcha"
label="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_DESC"
                validate="captcha"
                >
</field>
        <fields name="params">
            <field
                    name="show_category"
                    type="list"
                    label="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL"
                    description="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC"
                    default=""
                    useglobal="true"
            >
                <option value="0">JHIDE</option>
                <option value="1">JSHOW</option>
            </field>
        </fields>
    </fieldset>
</form>
 
</source>
</span>
 
The above form includes a captcha. To get this working you need to
 
The existing controller (in controller.php) will not need to be changed, but we'll need to define a new view, layout and model for the new form.
 
<span id="site/views/form/view.html.php">
<tt>site/views/form/view.html.php</tt>
<source lang="php">
<?php
/**
* @package    Joomla.Administrator
* @subpackage  com_helloworld
*
* @copyright  Copyright (C) 2005 - 2015 Open Source Matters, Inc. All rights reserved.
* @license    GNU General Public License version 2 or later; see LICENSE.txt
*/
 
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
 
/**
* HelloWorld View
* This is the site view presenting the user with the ability to add a new Helloworld record
*
*/
class HelloWorldViewForm extends JViewLegacy
{
 
    protected $form = null;
    protected $canDo;
 
/**
* Display the Hello World view
*
* @param  string  $tpl  The name of the layout file to parse.
*
* @return  void
*/
public function display($tpl = null)
{
// Get the form to display
$this->form = $this->get('Form');
        // Get the javascript script file for client-side validation
        $this->script = $this->get('Script');
 
        // Check that the user has permissions to create a new helloworld record
$this->canDo = JHelperContent::getActions('com_helloworld');
        if (!($this->canDo->get('core.create')))
        {
            $app = JFactory::getApplication();
$app->enqueueMessage(JText::_('JERROR_ALERTNOAUTHOR'), 'error');
$app->setHeader('status', 403, true);
return;
        }
       
// Check for errors.
if (count($errors = $this->get('Errors')))
{
throw new Exception(implode("\n", $errors), 500);
}
 
// Call the parent display to display the layout file
parent::display($tpl);
 
// Set properties of the html document
$this->setDocument();
}
 
/**
* Method to set up the html document properties
*
* @return void
*/
protected function setDocument()
{
$document = JFactory::getDocument();
$document->setTitle(JText::_('COM_HELLOWORLD_HELLOWORLD_CREATING'));
        $document->addScript(JURI::root() . $this->script);
$document->addScript(JURI::root() . "/administrator/components/com_helloworld"
                                  . "/views/helloworld/submitbutton.js");
JText::script('COM_HELLOWORLD_HELLOWORLD_ERROR_UNACCEPTABLE');
}
}
</source>
</span>
 
And into the layout file:
 
<span id="site/views/form/templ/edit.php">
<tt>site/views/form/templ/edit.php</tt>
<source lang="php">
<?php
/**
* @package    Joomla.Administrator
* @subpackage  com_helloworld
*
* @copyright  Copyright (C) 2005 - 2015 Open Source Matters, Inc. All rights reserved.
* @license    GNU General Public License version 2 or later; see LICENSE.txt
*
* This layout file is for displaying the front end form for capturing a new helloworld message
*
*/
 
// No direct access
defined('_JEXEC') or die('Restricted access');
JHtml::_('behavior.formvalidator');
 
?>
<form action="<?php echo JRoute::_('index.php?option=com_helloworld&layout=edit'); ?>"
    method="post" name="adminForm" id="adminForm" class="form-validate">
 
    <div class="form-horizontal">
        <fieldset class="adminform">
            <legend><?php echo JText::_('COM_HELLOWORLD_LEGEND_DETAILS') ?></legend>
            <div class="row-fluid">
                <div class="span6">
                    <?php echo $this->form->renderFieldset('details');  ?>
                </div>
            </div>
        </fieldset>
    </div>
   
    <div class="btn-toolbar">
<div class="btn-group">
<button type="button" class="btn btn-primary" onclick="Joomla.submitbutton('helloworld.save')">
<span class="icon-ok"></span><?php echo JText::_('JSAVE') ?>
</button>
</div>
<div class="btn-group">
<button type="button" class="btn" onclick="Joomla.submitbutton('helloworld.cancel')">
<span class="icon-cancel"></span><?php echo JText::_('JCANCEL') ?>
</button>
</div>
</div>
 
    <input type="hidden" name="task" />
    <?php echo JHtml::_('form.token'); ?>
</form>
</source>
</span>
 
All the above is similar to the admin functionality, except there are certain fields we don't want to allow the front-end user to see (eg the permissions) and instead of the standard Joomla admin buttons (and toolbar) we use a Save button and a Cancel button styled using the Bootstrap CSS, and which result in the <tt>task</tt> parameter being set to <tt>helloworld.save</tt> or <tt>helloworld.cancel</tt>.
 
Our new model is almost exactly the same as the admin equivalent; we just have to point to our new add-form instead.
 
<span id="site/models/form.php">
<tt>site/models/form.php</tt>
<source lang="php">
<?php
/**
* @package    Joomla.Administrator
* @subpackage  com_helloworld
*
* @copyright  Copyright (C) 2005 - 2015 Open Source Matters, Inc. All rights reserved.
* @license    GNU General Public License version 2 or later; see LICENSE.txt
*/
 
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
 
/**
* HelloWorld Model
*
* @since  0.0.1
*/
class HelloWorldModelForm extends JModelAdmin
{
 
/**
* Method to get a table object, load it if necessary.
*
* @param  string  $type    The table name. Optional.
* @param  string  $prefix  The class prefix. Optional.
* @param  array  $config  Configuration array for model. Optional.
*
* @return  JTable  A JTable object
*
* @since  1.6
*/
public function getTable($type = 'HelloWorld', $prefix = 'HelloWorldTable', $config = array())
{
return JTable::getInstance($type, $prefix, $config);
}
 
    /**
* Method to get the record form.
*
* @param  array    $data      Data for the form.
* @param  boolean  $loadData  True if the form is to load its own data (default case), false if not.
*
* @return  mixed    A JForm object on success, false on failure
*
* @since  1.6
*/
public function getForm($data = array(), $loadData = true)
{
// Get the form.
$form = $this->loadForm(
'com_helloworld.form',
'add-form',
array(
'control' => 'jform',
'load_data' => $loadData
)
);
 
if (empty($form))
{
return false;
}
 
return $form;
}
 
    /**
* Method to get the data that should be injected in the form.
* As this form is for add, we're not prefilling the form with an existing record
    * But if the user has previously hit submit and the validation has found an error,
    *  then we inject what was previously entered.
    *
* @return  mixed  The data for the form.
*
* @since  1.6
*/
    protected function loadFormData()
{
// Check the session for previously entered form data.
$data = JFactory::getApplication()->getUserState(
'com_helloworld.edit.helloworld.data',
array()
);
 
return $data;
}
   
    /**
* Method to get the script that have to be included on the form
* This returns the script associated with helloworld field greeting validation
    *
* @return string Script files
*/
public function getScript()
{
return 'administrator/components/com_helloworld/models/forms/helloworld.js';
}
}
</source>
</span>
 
==Handling the form HTTP POST==
As in the admin case, we'll need to create a new controller in the /controllers subdirectory with save() and cancel() methods, and we'll need a model with a save() method. In fact, because the above model class (in site/models/form.php) extends JModelAdmin, we'll be able to reuse it without adding any more code. (For just displaying the form our model class could have extended JModelForm, but JModelAdmin extends JModelForm and provides additional functions such as the save() one which we need).
 
Note that we don't need any new view file or layout file, because we're just going to redirect the user back to the same form.
 
So the only new file we need is a new controller in a new controllers subdirectory:
 
<span id="site/controllers/helloworld.php">
<tt>site/controllers/helloworld.php</tt>
<source lang="php">
<?php
/**
* @package    Joomla.Site
* @subpackage  com_helloworld
*
* @copyright  Copyright (C) 2005 - 2015 Open Source Matters, Inc. All rights reserved.
* @license    GNU General Public License version 2 or later; see LICENSE.txt
*/
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
 
/**
* HelloWorld Controller
*
* @package    Joomla.Site
* @subpackage  com_helloworld
*
* Used to handle the http POST from the front-end form which allows
* users to enter a new helloworld message
*
*/
class HelloWorldControllerHelloWorld extends JControllerForm
    public function cancel($key = null)
    {
        parent::cancel($key);
       
        // set up the redirect back to the same form
        $this->setRedirect(
            JUri::getInstance()->current(),
            JText::_(COM_HELLOWORLD_ADD_CANCELLED)
);
    }
   
    /*
    * Function handing the save for adding a new helloworld record
    * Based on the save() function in the JControllerForm class
    */
    public function save($key = null, $urlVar = null)
    {
        // Check for request forgeries.
JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
       
        $app = JFactory::getApplication();
        $input = $app->input;
$model = $this->getModel('form');
       
        // Check that this user is allowed to add a new record
        if (!JFactory::getUser()->authorise( "core.create", "com_helloworld"))
        {
$app->enqueueMessage(JText::_('JERROR_ALERTNOAUTHOR'), 'error');
$app->setHeader('status', 403, true);
 
return;
        }
       
        // get the data from the HTTP POST request
        $data  = $input->get('jform', array(), 'array');
       
        // set up context for saving form data
        $context = "$this->option.edit.$this->context";
       
        // Validate the posted data.
        // First we need to set up an instance of the form ...
$form = $model->getForm($data, false);
 
if (!$form)
{
$app->enqueueMessage($model->getError(), 'error');
return false;
}
 
// ... and then we validate the data against it
$validData = $model->validate($form, $data);
 
// Handle the case where there are validation errors
if ($validData === false)
{
// Get the validation messages.
$errors = $model->getErrors();
 
// Display up to three validation messages to the user.
for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++)
{
if ($errors[$i] instanceof Exception)
{
$app->enqueueMessage($errors[$i]->getMessage(), 'warning');
}
else
{
$app->enqueueMessage($errors[$i], 'warning');
}
}
 
// Save the form data in the session.
$app->setUserState($context . '.data', $data);
 
// Redirect back to the same screen.
$this->setRedirect(JUri::getInstance()->current());
 
return false;
}
       
        // add the 'created by' and 'created' date fields
        $validData['created_by'] = JFactory::getUser()->get('id', 0);
        $validData['created'] = date('Y-m-d h:i:s');
       
        // Attempt to save the data.
if (!$model->save($validData))
{
            // Handle the case where the save failed
           
// Save the data in the session.
$app->setUserState($context . '.data', $validData);
 
// Redirect back to the edit screen.
$this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $model->getError()));
$this->setMessage($this->getError(), 'error');
 
$this->setRedirect(JUri::getInstance()->current());
 
return false;
}
       
        // clear the data in the form
        $app->setUserState($context . '.data', null);
       
        // notify the administrator that a new helloworld message has been added on the front end
       
        // get the id of the person to notify from global config
        $params  = $app->getParams();
$userid_to_email = (int) $params->get('user_to_email');
        $user_to_email = JUser::getInstance($userid_to_email);
        $to_address = $user_to_email->get("email");
       
        // get the current user (if any)
        $current_user = JFactory::getUser();
        if ($current_user)
        {
            $current_username = $current_user->get("username");
        }
        else
        {
            $current_username = "a visitor to the site";
        }
       
        // get the Mailer object, set up the email to be sent, and send it
        $mailer = JFactory::getMailer();
        $mailer->addRecipient($to_address);
        $mailer->setSubject("New helloworld message added by " . $current_username);
        $mailer->setBody("New greeting is " . $validData['greeting']);
        try
        {
            $mailer->send();
        }
        catch (Exception $e)
        {
            JLog::add('Caught exception: ' . $e->getMessage(), JLog::Error, 'jerror');
        }
       
        $this->setRedirect(
JUri::getInstance()->current(),
                JText::_('COM_HELLOWORLD_ADD_SUCCESSFUL')
);
           
        return true;
       
    }
 
}
</source>
</span>
 
In the above file we've been able to reuse the cancel() method from the parent JControllerForm class, with the exception that we want to redirect back to the same form. (The parent cancel() method redirects back to the view showing the list of records - as in the admin case).
 
However, our save() method functionality is significantly different from that of the parent class, so it's easier just to write our own.
 
The mailing functionality relies on you having set up mail in your Joomla instance. If you're using localhost then one possibility is to configure this using a gmail account, and you can easily find the smtp settings for this on the Internet. Note that you'll need to allow "less secure apps" on the gmail account.
 
==Configuration Parameters==
We've introduced two new configuration parameters - the captcha to display on the form, and the user to email with notification of a new helloworld message being added. So update the configuration settings as follows:
 
<span id="admin/config.xml">
<tt>admin/config.xml</tt>
<source lang="xml" highlight="18-37">
<?xml version="1.0" encoding="utf-8"?>
<config>
<fieldset
name="greetings"
label="COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_LABEL"
description="COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_DESC"
>
<field
name="show_category"
type="radio"
label="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC"
default="0"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
        <field
name="captcha"
type="plugins"
            folder="captcha"
label="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_DESC"
default="0"
filter="cmd"
>
            <option value="">JOPTION_USE_DEFAULT</option>
<option value="0">JOPTION_DO_NOT_USE</option>
</field>
        <field
name="user_to_email"
type="user"
label="COM_HELLOWORLD_HELLOWORLD_FIELD_USER_TO_EMAIL_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_FIELD_USER_TO_EMAIL_DESC"
default="0"
>
</field>
</fieldset>
    <fieldset
name="permissions"
label="JCONFIG_PERMISSIONS_LABEL"
description="JCONFIG_PERMISSIONS_DESC"
>
<field
name="rules"
type="rules"
label="JCONFIG_PERMISSIONS_LABEL"
class="inputbox"
validate="rules"
filter="rules"
component="com_helloworld"
section="component"
/>
</fieldset>
</config>
 
</source>
</span>
 
 


{{-}}
{{-}}

Revision as of 11:15, 4 November 2017

Joomla! 
3.x
Tutorial
Developing an MVC Component



This is a multiple-article series of tutorials on how to develop a Model-View-Controller Component for Joomla! VersionJoomla 3.x.

Begin with the Introduction, and navigate the articles in this series by using the navigation button at the bottom or the box to the right (the Articles in this series).



Web Page Under Construction !!!

This tutorial is part of the Developing a MVC Component for Joomla! 3.x tutorial. You are encouraged to read the previous parts of the tutorial before reading this.

Introduction

In this step we add a form to the site front end to allow users to add a new Helloworld record. You could adapt the code in this step to provide a range of front-end data entry functionality, from, eg

  • a registration form to allow visitors to the site to express interest, to,
  • a facility to allow suppliers to maintain the data in certain tables, but without giving them access to the back end administration functionality.

Functionality

In this step we will build the following functionality

  • a front-end form that allows someone to enter a new helloworld record, together with Save and Cancel buttons.
  • additional fields in the helloworld database table to capture the user who entered the record, and the creation date/time
  • validation of the data entered into the fields, including removing dangerous items such as html tags
  • access control on the form, controlling who can add a new record
  • an email to an administrator, to indicate that someone has entered a new record (and we'll include a field on the form where the user can include some text which just goes into the email)
  • a captcha on the form
  • a front-end menu item which points to the form (ie how users get access to the form)
  • update to the admin helloworlds view to include the author and creation date in the display.

Approach

Providing a form on the site front end is pretty much the same as on the admin back end, so much of what is developed in this step reflects what was done in the step Adding backend actions, and in particular the Edit form for helloworld messages.

For this form we'll use (all in the site part)

  • the same main helloworld controller (controller.php)
  • a new view – which we'll call "form"
  • a new layout, associated with the form view, which we'll call "edit" (like the admin layout file)
  • a new model – which we'll call "form" also, to align with the view
  • an xml file for the form, which we'll put in add-form.xml in the model

In addition we'll need to handle the HTTP POST requests arising from the user pressing the Save and Cancel buttons, and we'll do this in

  • a new helloworld controller in the controllers directory (handling Save and Cancel)
  • the same form.php model as above (handling Save)

Upon completion of Save/Cancel we'll redirect back to the same form.

As for the admin case, we'll consider our classes extending some of the controller and model classes which are richer in functionality that the simple legacy ones

  • the model will extend JModelAdmin (which supports forms, as well as saving records to the database) - even though it's called JModelAdmin it's still available within the site part
  • the controller handling the POST will extend JControllerForm (which includes the cancel() and save() methods).

Some of the other aspects in this step will involve using the techniques covered in earlier steps, namely Adding a menu type to the site part, Adding verifications, Adding configuration and Adding ACL.

Adding a menu item

To enable a menu item to point to the form, we need to put an xml file into the associated layout directory. Extend your directory structure (remembering to include the index.php file in each new directory) and put the following into

site/views/form/tmpl/edit.xml

<?xml version="1.0" encoding="utf-8"?>
<metadata>
	<layout title="COM_HELLOWORLD_ADD_VIEW_TITLE">
		<message>COM_HELLOWORLD_ADD_VIEW_DESC</message>
	</layout>
</metadata>

Displaying the new form view

Create a new forms subdirectory under the site models directory, and add the definition of the form into

site/models/forms/add-form.xml

<?xml version="1.0" encoding="utf-8"?>
<form
    addrulepath="/administrator/components/com_helloworld/models/rules"
    >
    <fieldset
				name="details"
				label="COM_HELLOWORLD_HELLOWORLD_DETAILS"
	>
		<field
				name="id"
				type="hidden"
				/>
		<field
				name="greeting"
				type="text"
				label="COM_HELLOWORLD_HELLOWORLD_GREETING_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_GREETING_DESC"
				size="40"
				class="inputbox"
				validate="greeting"
				required="true"
                hint="COM_HELLOWORLD_HELLOWORLD_GREETING_HINT"
				default=""
				/>
        <field
				name="catid"
				type="category"
				extension="com_helloworld"
				class="inputbox"
				default=""
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_DESC"
				required="true"
                >
			<option value="0">JOPTION_SELECT_CATEGORY</option>
		</field>
        <field
                name="message"
                type="textarea"
                rows="5"
                cols="80"
				label="COM_HELLOWORLD_HELLOWORLD_MESSAGE_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_MESSAGE_DESC"
                hint="COM_HELLOWORLD_HELLOWORLD_MESSAGE_HINT"
				required="true"
                >
        </field>
        <field
				name="captcha"
				type="captcha"
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_DESC"
                validate="captcha"
                >
		</field>
        <fields name="params">
            <field
                    name="show_category"
                    type="list"
                    label="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL"
                    description="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC"
                    default=""
                    useglobal="true"
            >
                <option value="0">JHIDE</option>
                <option value="1">JSHOW</option>
            </field>
        </fields>
    </fieldset>
</form>

The above form includes a captcha. To get this working you need to

The existing controller (in controller.php) will not need to be changed, but we'll need to define a new view, layout and model for the new form.

site/views/form/view.html.php

<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_helloworld
 *
 * @copyright   Copyright (C) 2005 - 2015 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

// No direct access to this file
defined('_JEXEC') or die('Restricted access');

/**
 * HelloWorld View
 * This is the site view presenting the user with the ability to add a new Helloworld record
 * 
 */
class HelloWorldViewForm extends JViewLegacy
{

    protected $form = null;
    protected $canDo;

	/**
	 * Display the Hello World view
	 *
	 * @param   string  $tpl  The name of the layout file to parse.
	 *
	 * @return  void
	 */
	public function display($tpl = null)
	{
		// Get the form to display
		$this->form = $this->get('Form');
        // Get the javascript script file for client-side validation
        $this->script = $this->get('Script'); 

        // Check that the user has permissions to create a new helloworld record
		$this->canDo = JHelperContent::getActions('com_helloworld');
        if (!($this->canDo->get('core.create'))) 
        {
            $app = JFactory::getApplication(); 
			$app->enqueueMessage(JText::_('JERROR_ALERTNOAUTHOR'), 'error');
			$app->setHeader('status', 403, true);
			return;
        }
        
		// Check for errors.
		if (count($errors = $this->get('Errors')))
		{
			throw new Exception(implode("\n", $errors), 500);
		}

		// Call the parent display to display the layout file
		parent::display($tpl);

		// Set properties of the html document
		$this->setDocument();
	}

	/**
	 * Method to set up the html document properties
	 *
	 * @return void
	 */
	protected function setDocument() 
	{
		$document = JFactory::getDocument();
		$document->setTitle(JText::_('COM_HELLOWORLD_HELLOWORLD_CREATING'));
        $document->addScript(JURI::root() . $this->script);
		$document->addScript(JURI::root() . "/administrator/components/com_helloworld"
		                                  . "/views/helloworld/submitbutton.js");
		JText::script('COM_HELLOWORLD_HELLOWORLD_ERROR_UNACCEPTABLE');
	}
}

And into the layout file:

site/views/form/templ/edit.php

<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_helloworld
 *
 * @copyright   Copyright (C) 2005 - 2015 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 *
 * This layout file is for displaying the front end form for capturing a new helloworld message
 *
 */

// No direct access
defined('_JEXEC') or die('Restricted access');
JHtml::_('behavior.formvalidator');

?>
<form action="<?php echo JRoute::_('index.php?option=com_helloworld&layout=edit'); ?>"
    method="post" name="adminForm" id="adminForm" class="form-validate">

    <div class="form-horizontal">
        <fieldset class="adminform">
            <legend><?php echo JText::_('COM_HELLOWORLD_LEGEND_DETAILS') ?></legend>
            <div class="row-fluid">
                <div class="span6">
                    <?php echo $this->form->renderFieldset('details');  ?>
                </div>
            </div>
        </fieldset>
    </div>
    
    <div class="btn-toolbar">
			<div class="btn-group">
				<button type="button" class="btn btn-primary" onclick="Joomla.submitbutton('helloworld.save')">
					<span class="icon-ok"></span><?php echo JText::_('JSAVE') ?>
				</button>
			</div>
			<div class="btn-group">
				<button type="button" class="btn" onclick="Joomla.submitbutton('helloworld.cancel')">
					<span class="icon-cancel"></span><?php echo JText::_('JCANCEL') ?>
				</button>
			</div>
		</div>

    <input type="hidden" name="task" />
    <?php echo JHtml::_('form.token'); ?>
</form>

All the above is similar to the admin functionality, except there are certain fields we don't want to allow the front-end user to see (eg the permissions) and instead of the standard Joomla admin buttons (and toolbar) we use a Save button and a Cancel button styled using the Bootstrap CSS, and which result in the task parameter being set to helloworld.save or helloworld.cancel.

Our new model is almost exactly the same as the admin equivalent; we just have to point to our new add-form instead.

site/models/form.php

<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_helloworld
 *
 * @copyright   Copyright (C) 2005 - 2015 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

// No direct access to this file
defined('_JEXEC') or die('Restricted access');

/**
 * HelloWorld Model
 *
 * @since  0.0.1
 */
class HelloWorldModelForm extends JModelAdmin
{

	/**
	 * Method to get a table object, load it if necessary.
	 *
	 * @param   string  $type    The table name. Optional.
	 * @param   string  $prefix  The class prefix. Optional.
	 * @param   array   $config  Configuration array for model. Optional.
	 *
	 * @return  JTable  A JTable object
	 *
	 * @since   1.6
	 */
	public function getTable($type = 'HelloWorld', $prefix = 'HelloWorldTable', $config = array())
	{
		return JTable::getInstance($type, $prefix, $config);
	}

    /**
	 * Method to get the record form.
	 *
	 * @param   array    $data      Data for the form.
	 * @param   boolean  $loadData  True if the form is to load its own data (default case), false if not.
	 *
	 * @return  mixed    A JForm object on success, false on failure
	 *
	 * @since   1.6
	 */
	public function getForm($data = array(), $loadData = true)
	{
		// Get the form.
		$form = $this->loadForm(
			'com_helloworld.form',
			'add-form',
			array(
				'control' => 'jform',
				'load_data' => $loadData
			)
		);

		if (empty($form))
		{
			return false;
		}

		return $form;
	}

    /**
	 * Method to get the data that should be injected in the form.
	 * As this form is for add, we're not prefilling the form with an existing record
     * But if the user has previously hit submit and the validation has found an error,
     *   then we inject what was previously entered.
     *
	 * @return  mixed  The data for the form.
	 *
	 * @since   1.6
	 */
    protected function loadFormData()
	{
		// Check the session for previously entered form data.
		$data = JFactory::getApplication()->getUserState(
			'com_helloworld.edit.helloworld.data',
			array()
		);

		return $data;
	}
    
    /**
	 * Method to get the script that have to be included on the form
	 * This returns the script associated with helloworld field greeting validation
     *
	 * @return string	Script files
	 */
	public function getScript() 
	{
		return 'administrator/components/com_helloworld/models/forms/helloworld.js';
	}
}

Handling the form HTTP POST

As in the admin case, we'll need to create a new controller in the /controllers subdirectory with save() and cancel() methods, and we'll need a model with a save() method. In fact, because the above model class (in site/models/form.php) extends JModelAdmin, we'll be able to reuse it without adding any more code. (For just displaying the form our model class could have extended JModelForm, but JModelAdmin extends JModelForm and provides additional functions such as the save() one which we need).

Note that we don't need any new view file or layout file, because we're just going to redirect the user back to the same form.

So the only new file we need is a new controller in a new controllers subdirectory:

site/controllers/helloworld.php

<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_helloworld
 *
 * @copyright   Copyright (C) 2005 - 2015 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */
// No direct access to this file
defined('_JEXEC') or die('Restricted access');

/**
 * HelloWorld Controller
 *
 * @package     Joomla.Site
 * @subpackage  com_helloworld
 *
 * Used to handle the http POST from the front-end form which allows 
 * users to enter a new helloworld message
 *
 */
class HelloWorldControllerHelloWorld extends JControllerForm
{   
    public function cancel($key = null)
    {
        parent::cancel($key);
        
        // set up the redirect back to the same form
        $this->setRedirect(
            JUri::getInstance()->current(), 
            JText::_(COM_HELLOWORLD_ADD_CANCELLED)
		);
    }
    
    /*
     * Function handing the save for adding a new helloworld record
     * Based on the save() function in the JControllerForm class
     */
    public function save($key = null, $urlVar = null)
    {
        // Check for request forgeries.
		JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
        
        $app = JFactory::getApplication(); 
        $input = $app->input; 
		$model = $this->getModel('form');
        
        // Check that this user is allowed to add a new record
        if (!JFactory::getUser()->authorise( "core.create", "com_helloworld"))
        {
			$app->enqueueMessage(JText::_('JERROR_ALERTNOAUTHOR'), 'error');
			$app->setHeader('status', 403, true);

			return;
        }
        
        // get the data from the HTTP POST request
        $data  = $input->get('jform', array(), 'array');
        
        // set up context for saving form data
        $context = "$this->option.edit.$this->context";
        
        // Validate the posted data.
        // First we need to set up an instance of the form ...
		$form = $model->getForm($data, false);

		if (!$form)
		{
			$app->enqueueMessage($model->getError(), 'error');
			return false;
		}

		// ... and then we validate the data against it
		$validData = $model->validate($form, $data);

		// Handle the case where there are validation errors
		if ($validData === false)
		{
			// Get the validation messages.
			$errors = $model->getErrors();

			// Display up to three validation messages to the user.
			for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++)
			{
				if ($errors[$i] instanceof Exception)
				{
					$app->enqueueMessage($errors[$i]->getMessage(), 'warning');
				}
				else
				{
					$app->enqueueMessage($errors[$i], 'warning');
				}
			}

			// Save the form data in the session.
			$app->setUserState($context . '.data', $data);

			// Redirect back to the same screen.
			$this->setRedirect(JUri::getInstance()->current());

			return false;
		}
        
        // add the 'created by' and 'created' date fields
        $validData['created_by'] = JFactory::getUser()->get('id', 0);
        $validData['created'] = date('Y-m-d h:i:s');
        
        // Attempt to save the data.
		if (!$model->save($validData))
		{
            // Handle the case where the save failed
            
			// Save the data in the session.
			$app->setUserState($context . '.data', $validData);

			// Redirect back to the edit screen.
			$this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $model->getError()));
			$this->setMessage($this->getError(), 'error');

			$this->setRedirect(JUri::getInstance()->current());

			return false;
		}
        
        // clear the data in the form
        $app->setUserState($context . '.data', null);
        
        // notify the administrator that a new helloworld message has been added on the front end
        
        // get the id of the person to notify from global config
        $params   = $app->getParams();
		$userid_to_email = (int) $params->get('user_to_email');
        $user_to_email = JUser::getInstance($userid_to_email);
        $to_address = $user_to_email->get("email");
        
        // get the current user (if any)
        $current_user = JFactory::getUser();
        if ($current_user) 
        {
            $current_username = $current_user->get("username");
        }
        else 
        {
            $current_username = "a visitor to the site";
        }
        
        // get the Mailer object, set up the email to be sent, and send it
        $mailer = JFactory::getMailer();
        $mailer->addRecipient($to_address);
        $mailer->setSubject("New helloworld message added by " . $current_username);
        $mailer->setBody("New greeting is " . $validData['greeting']);
        try 
        {
            $mailer->send(); 
        }
        catch (Exception $e)
        {
            JLog::add('Caught exception: ' . $e->getMessage(), JLog::Error, 'jerror');
        }
        
        $this->setRedirect(
				JUri::getInstance()->current(),
                JText::_('COM_HELLOWORLD_ADD_SUCCESSFUL')
			);
            
        return true;
        
    }

}

In the above file we've been able to reuse the cancel() method from the parent JControllerForm class, with the exception that we want to redirect back to the same form. (The parent cancel() method redirects back to the view showing the list of records - as in the admin case).

However, our save() method functionality is significantly different from that of the parent class, so it's easier just to write our own.

The mailing functionality relies on you having set up mail in your Joomla instance. If you're using localhost then one possibility is to configure this using a gmail account, and you can easily find the smtp settings for this on the Internet. Note that you'll need to allow "less secure apps" on the gmail account.

Configuration Parameters

We've introduced two new configuration parameters - the captcha to display on the form, and the user to email with notification of a new helloworld message being added. So update the configuration settings as follows:

admin/config.xml

<?xml version="1.0" encoding="utf-8"?>
<config>
	<fieldset
		name="greetings"
		label="COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_LABEL"
		description="COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_DESC"
	>
		<field
			name="show_category"
			type="radio"
			label="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL"
			description="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC"
			default="0"
		>
			<option value="0">JHIDE</option>
			<option value="1">JSHOW</option>
		</field>
        <field
			name="captcha"
			type="plugins"
            folder="captcha"
			label="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_LABEL"
			description="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_DESC"
			default="0"
			filter="cmd"
		>
            <option value="">JOPTION_USE_DEFAULT</option>
			<option value="0">JOPTION_DO_NOT_USE</option>
		</field>
        <field
			name="user_to_email"
			type="user"
			label="COM_HELLOWORLD_HELLOWORLD_FIELD_USER_TO_EMAIL_LABEL"
			description="COM_HELLOWORLD_HELLOWORLD_FIELD_USER_TO_EMAIL_DESC"
			default="0"
		>
		</field>
	</fieldset>
    <fieldset
		name="permissions"
		label="JCONFIG_PERMISSIONS_LABEL"
		description="JCONFIG_PERMISSIONS_DESC"
	>
		<field
			name="rules"
			type="rules"
			label="JCONFIG_PERMISSIONS_LABEL"
			class="inputbox"
			validate="rules"
			filter="rules"
			component="com_helloworld"
			section="component"
		/>
	</fieldset>
</config>