J3.x

Developing an MVC Component/Adding Checkout/es: Difference between revisions

From Joomla! Documentation

Created page with "Desarrollo de un Componente MVC/Agregar Verificación"
 
Created page with "== Colaboradores =="
 
(34 intermediate revisions by the same user not shown)
Line 1: Line 1:
<noinclude><languages /></noinclude>
<noinclude><languages /></noinclude>
{{:J3.1:Developing an MVC Component/en}}
{{:J3.1:Developing an MVC Component/es}}


This tutorial is part of the [[S:MyLanguage/J3.2:Developing an MVC Component | Developing an MVC Component for Joomla! 3.2]] tutorial. You are encouraged to read the previous parts of the tutorial before reading this.
Este artículo es parte del tutorial [[S:MyLanguage/J3.2:Developing an MVC Component|Desarrollo de un Componente MVC para Joomla! 3.2]]. Te invitamos a leer las partes anteriores del tutorial antes de leer esto.


In this step we add checkout capability to our component.
En este paso, agregamos la capacidad de comprobación a nuestro componente.


== Introduction ==  
== Introducción ==  


Checkout capability avoids the unexpected results which can arise when 2 users edit the same record simultaneously. It provides a record-locking capability which means that only 1 user can edit the record at the one time. You might want to have this capability in your component if you have many users updating your component's database records and have a significant probably of 2 users updating the same record simultaneously. This might arise if you allow updates from the front end of your site, although in this tutorial step we just consider the back end; the work required on the front end to support checkout would be similar.
La capacidad de comprobación evita los resultados inesperados que pueden surgir cuando 2 usuarios editan el mismo registro simultáneamente. Proporciona una capacidad de bloqueo de registros, lo que significa que solo 1 usuario puede editar el registro a la vez. Es posible que desees tener esta capacidad en tu componente si tienes muchos usuarios que actualizan los registros de la base de datos del componente y tienes una cantidad significativa de 2 usuarios que actualizan el mismo registro simultáneamente. Esto puede surgir si permite actualizaciones desde el lado cliente del sitio, aunque en este paso del tutorial solo consideramos el lado servidor; el trabajo requerido en el lado cliente para soportar la comprobación sería similar.


A number of core Joomla components, including com_content, support checkout, so we will mirror that functionality in our helloworld component.  
Una serie de componentes principales de Joomla, incluido com_content, son compatibles con la comprobación, por lo que vamos a reflejar esa funcionalidad en nuestro componente helloworld.  


A video accompanying this step can be found at [https://youtu.be/NfPHjM4d-_w Adding Checkout].
Puede encontrar un vídeo que acompaña este paso en [https://youtu.be/NfPHjM4d-_w Agregar Comprobación].


{{#widget:YouTube|id=NfPHjM4d-_w}}
{{#widget:YouTube|id=NfPHjM4d-_w}}


== Functionality ==
== Functionalidad ==


We want to support the following aspects
Queremos cubrir los siguientes aspectos.
# checking out a record when a user accesses it for editing
# controlar un registro cuando un usuario accede a él para editarlo
# checking in a record whenever the user has finished editing (by clicking either Save or Cancel)
# marcar un registro cuando el usuario haya terminado de editar (haciendo clic en Guardar o en Cancelar)
# preventing other users from editing the record when it's checked out
# evitar que otros usuarios editen el registro cuando está controlado
# displaying on our helloworlds view the records which are checked out (using the little padlock symbol)
# mostrar en nuestra vista de helloworlds los registros que están controlados (usando el pequeño símbolo del candado)
# allowing an authorised user to override the checkout, and to checkin a record manually
# permitiendo a un usuario autorizado liberar el proceso de control y controlar un registro manualmente


== Approach ==
== Enfoque ==


As usual, Joomla provides a lot of core functionality which we can reuse, provide we align with the standard Joomla approach.  
Como es habitual, Joomla proporciona una gran cantidad de funciones básicas que podemos reutilizar, siempre que nos alineemos con el enfoque estándar de Joomla.  


To our database record we need to add 2 fields
Necesitamos agregar 2 campos a nuestro registro de base de datos .
# checked_out – which holds the id of the user who has checked out the record
# checked_out - que contiene el id del usuario que ha revisado el registro
# checked_out_time – a date/timestamp field which records when the record was checked out
# checked_out_time - un campo de fecha/hora que registra cuando el registro fue revisado


The very presence of these fields in our database record switches on the checkout functionality in the JTable class, and as checkin and checkout functionality is already in the Admin and Form Controllers/Model (which ours inherit from), there's no need to do anything else to enable the checkout functionality working for editing helloworld records. We just need to add a few language strings for the checkin messages.  
La presencia de estos campos en nuestro registro de base de datos cambia la funcionalidad de control en la clase JTable, y como la funcionalidad de registro y control ya está en Admin/Form Controllers/Model (la cual es nuestra herencia), no hay necesidad de hacer nada más para habilitar el funcionamiento de la función de control para editar los registros de helloworld. Solo necesitamos agregar algunas cadenas de idioma para los mensajes de registro.  


(If you're adding this sort of control you may wish to add also "modified" and "modified_by" fields  to capture the user who last modified the record, and when that occurred. Joomla automatically populates these fields for us as well.)
(Si estás agregando este tipo de control, es posible que desees agregar también los campos "modificado" y "modificado por" para capturar al usuario que modificó el registro por última vez, y cuando eso ocurrió. Joomla también rellena estos campos automáticamente para nosotros).  


All that we are left to handle by ourselves is the display in our helloworlds view, and for that we need to do the following:  
Todo lo que nos queda por manejar es la visualización en nuestra vista de hellowworlds, y para eso necesitamos hacer lo siguiente:  
# include the checked_out and checked_out_time fields in our select query in the model,
# incluir los campos checked_out y checked_out_time en nuestra consulta de selección en el modelo,
# include a Checkin button in the toolbar,
# incluir un botón de registro en la barra de herramientas,
# display the little padlock symbol against records which are checked out, and enable clicking on it based on whether the user is authorised to checkin records which are checked out.
# mostrar el pequeño símbolo del candado contra los registros que se han controlado, y habilitar hacer clic en él en función de si el usuario está autorizado para liberar los registros que están controlados.


== Updating the Database ==
== Actualización de la base de datos ==
Add the checkout fields to the database record:
Agregar los campos de control al registro de la base de datos.


<span id="admin/sql/install.mysql.utf8.sql">
<span id="admin/sql/install.mysql.utf8.sql">
Line 80: Line 80:
</span>
</span>


New SQL update file:
Nuevo archivo de actualización de SQL:


<span id="admin/sql/updates/mysql/0.0.24.sql">
<span id="admin/sql/updates/mysql/0.0.24.sql">
Line 90: Line 90:
</span>
</span>


== Helloworlds MVC ==
== MVC Helloworlds ==


Our updated model:
Nuestro modelo actualizado:


<span id="admin/models/helloworlds.php">
<span id="admin/models/helloworlds.php">
Line 251: Line 251:
</span>
</span>


On our view we'll display the Checkin button if the user can edit helloworld records, or has core.manage permission on the com_checkin component.  
Desde nuestro punto de vista, mostraremos el botón Registrar si el usuario puede editar los registros de helloworld o si tiene el permiso core.manage en el componente com_checkin.  


<span id="admin/views/helloworlds/view.html.php">
<span id="admin/views/helloworlds/view.html.php">
Line 385: Line 385:
</span>
</span>


In our layout file we highlight checked-out records by displaying the little padlock symbol using the <tt>JHtmlJGrid::checkedout()</tt> function. This function allows us to pass the user who has checked the record out and the checkout date/time, so that they can be displayed as a tooltip. We also pass the URL to the checkin functionality, and enable or disable this link depending upon whether the current user has com_checkin core.manage permission, or is the same as the user who checked the record out.
En nuestro archivo de diseño, resaltamos los registros controlados mostrando el pequeño símbolo del candado utilizando la función <tt>JHtmlJGrid::checkedout()</tt>. Esta función nos permite pasar al usuario que ha marcado el registro y la fecha/hora de control, para que puedan mostrarse como información emergente. También pasamos la URL a la funcionalidad de registro, y habilitamos o inhabilitamos este enlace dependiendo de si el usuario actual tiene permiso com_checkin core.manage, o es el mismo que el usuario que marco el registro.


(Note that at time of writing the current version of Joomla (3.8) has a bug here - the JModelForm code which verifies if the user can checkin the record compares the user's permissions with com_checkin core.admin instead of com_checkin core.manage).
(Ten en cuenta que al momento de escribir esto, la versión actual de Joomla (3.8) tiene un error aquí: el código JModelForm que verifica si el usuario puede marcar el registro compara los permisos del usuario con com_checkin core.admin en lugar de com_checkin core.manage).


<span id="admin/views/helloworlds/tmpl/default.php">
<span id="admin/views/helloworlds/tmpl/default.php">
Line 549: Line 549:
</span>
</span>


== Updated Language Strings ==
== Actualización cadenas de idioma ==


<span id="admin/language/en-GB/en-GB.com_helloworld.ini">
<span id="admin/language/en-GB/en-GB.com_helloworld.ini">
Line 655: Line 655:
</span>
</span>


== Packaging the Component ==
== Empaquetado del componente ==


Contents of your code directory. Each file link below takes you to the step in the tutorial which has the latest version of that source code file.
Contenido de su directorio de código. Cada enlace a un archivo a continuación te lleva al paso en el tutorial que tiene la última versión de ese archivo de código fuente.


* ''[[S:MyLanguage/J3.2:Developing_an_MVC_Component/Adding Checkout#helloworld.xml|helloworld.xml]]''
* ''[[S:MyLanguage/J3.2:Developing_an_MVC_Component/Adding Checkout#helloworld.xml|helloworld.xml]]''
Line 870: Line 870:
</span>
</span>


== Contributors ==
== Colaboradores ==
*[[User:Robbiej|Robbie Jackson]]
*[[User:Robbiej|Robbie Jackson]]


<div class="row">  
<div class="row">  
<div class="large-6 columns">{{Basic button|S:MyLanguage/J3.x:Developing_an_MVC_Component/Adding Associations|Prev: Adding Associations|class=expand success}}</div>
<div class="large-6 columns">{{Basic button|S:MyLanguage/J3.x:Developing_an_MVC_Component/Adding Associations|Prev: Agregar Asociaciones|class=expand success}}</div>
<div class="large-6 columns">{{Basic button|S:MyLanguage/J3.x:Developing_an_MVC_Component/Adding Ordering|Next: Adding Ordering|class=expand}}</div>
<div class="large-6 columns">{{Basic button|S:MyLanguage/J3.x:Developing_an_MVC_Component/Adding Ordering|Sig: Agregar Ordenamiento|class=expand}}</div>
</div>
</div>
__NOTOC__
__NOTOC__

Latest revision as of 14:11, 5 February 2019

Joomla! 
3.x
Tutorial
Desarrollo de un Componente MVC

Agregar una variable de petición en el tipo de menú

Utilizando la base de datos

Lado servidor básico

Agregar gestión de idioma

Agregar acciones del lado servidor

Agregar decoraciones del lado servidor

Agregar verificaciones

Agregar categorías

Agregar configuración

  1. Agregar ACL

Agregar un archivo de secuencia de comandos instalar-desinstalar-actualizar

Agregar un formulario del lado cliente

  1. Agregar una imagen
  2. Agregar un mapa
  3. Agregar AJAX
  4. Agregar un alias

Usar la facilidad filtro de idioma

  1. Agregar una Modal
  2. Agregar Asociaciones
  3. Agregar Comprobación
  4. Agregar Ordenamiento
  5. Agregar Niveles
  6. Agregar Control de Versiones
  7. Agregar Etiquetas
  8. Agregar Accesos
  9. Agregar procesos por lote
  10. Agregar Caché
  11. Agregar un Canal de Noticias

Agregar un servidor de actualización

  1. Agregar campos personalizados
  2. Upgrading to Joomla4



Esta es una serie multi-artículos de tutoriales sobre cómo desarrollar un Componente Modelo-Vista-Controlador para Joomla! VersiónJoomla 3.x.

Comenzar con la Introducción, y navegar por los artículos de esta serie usando el botón de navegación en la parte inferior o en el cuadro de la derecha (los "Artículos de esta serie").



Este artículo es parte del tutorial Desarrollo de un Componente MVC para Joomla! 3.2. Te invitamos a leer las partes anteriores del tutorial antes de leer esto.

En este paso, agregamos la capacidad de comprobación a nuestro componente.

Introducción

La capacidad de comprobación evita los resultados inesperados que pueden surgir cuando 2 usuarios editan el mismo registro simultáneamente. Proporciona una capacidad de bloqueo de registros, lo que significa que solo 1 usuario puede editar el registro a la vez. Es posible que desees tener esta capacidad en tu componente si tienes muchos usuarios que actualizan los registros de la base de datos del componente y tienes una cantidad significativa de 2 usuarios que actualizan el mismo registro simultáneamente. Esto puede surgir si permite actualizaciones desde el lado cliente del sitio, aunque en este paso del tutorial solo consideramos el lado servidor; el trabajo requerido en el lado cliente para soportar la comprobación sería similar.

Una serie de componentes principales de Joomla, incluido com_content, son compatibles con la comprobación, por lo que vamos a reflejar esa funcionalidad en nuestro componente helloworld.

Puede encontrar un vídeo que acompaña este paso en Agregar Comprobación.

Functionalidad

Queremos cubrir los siguientes aspectos.

  1. controlar un registro cuando un usuario accede a él para editarlo
  2. marcar un registro cuando el usuario haya terminado de editar (haciendo clic en Guardar o en Cancelar)
  3. evitar que otros usuarios editen el registro cuando está controlado
  4. mostrar en nuestra vista de helloworlds los registros que están controlados (usando el pequeño símbolo del candado)
  5. permitiendo a un usuario autorizado liberar el proceso de control y controlar un registro manualmente

Enfoque

Como es habitual, Joomla proporciona una gran cantidad de funciones básicas que podemos reutilizar, siempre que nos alineemos con el enfoque estándar de Joomla.

Necesitamos agregar 2 campos a nuestro registro de base de datos .

  1. checked_out - que contiene el id del usuario que ha revisado el registro
  2. checked_out_time - un campo de fecha/hora que registra cuando el registro fue revisado

La presencia de estos campos en nuestro registro de base de datos cambia la funcionalidad de control en la clase JTable, y como la funcionalidad de registro y control ya está en Admin/Form Controllers/Model (la cual es nuestra herencia), no hay necesidad de hacer nada más para habilitar el funcionamiento de la función de control para editar los registros de helloworld. Solo necesitamos agregar algunas cadenas de idioma para los mensajes de registro.

(Si estás agregando este tipo de control, es posible que desees agregar también los campos "modificado" y "modificado por" para capturar al usuario que modificó el registro por última vez, y cuando eso ocurrió. Joomla también rellena estos campos automáticamente para nosotros).

Todo lo que nos queda por manejar es la visualización en nuestra vista de hellowworlds, y para eso necesitamos hacer lo siguiente:

  1. incluir los campos checked_out y checked_out_time en nuestra consulta de selección en el modelo,
  2. incluir un botón de registro en la barra de herramientas,
  3. mostrar el pequeño símbolo del candado contra los registros que se han controlado, y habilitar hacer clic en él en función de si el usuario está autorizado para liberar los registros que están controlados.

Actualización de la base de datos

Agregar los campos de control al registro de la base de datos.

admin/sql/install.mysql.utf8.sql

DROP TABLE IF EXISTS `#__helloworld`;

CREATE TABLE `#__helloworld` (
	`id`       INT(11)     NOT NULL AUTO_INCREMENT,
	`asset_id` INT(10)     NOT NULL DEFAULT '0',
	`created`  DATETIME    NOT NULL DEFAULT '0000-00-00 00:00:00',
	`created_by`  INT(10) UNSIGNED NOT NULL DEFAULT '0',
	`checked_out` INT(10) NOT NULL DEFAULT '0',
	`checked_out_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
	`greeting` VARCHAR(25) NOT NULL,
	`alias`  VARCHAR(40)  NOT NULL DEFAULT '',
	`language`  CHAR(7)  NOT NULL DEFAULT '*',
	`published` tinyint(4) NOT NULL DEFAULT '1',
	`catid`	    int(11)    NOT NULL DEFAULT '0',
	`params`   VARCHAR(1024) NOT NULL DEFAULT '',
	`image`   VARCHAR(1024) NOT NULL DEFAULT '',
	`latitude` DECIMAL(9,7) NOT NULL DEFAULT 0.0,
	`longitude` DECIMAL(10,7) NOT NULL DEFAULT 0.0,
	PRIMARY KEY (`id`)
)
	ENGINE =MyISAM
	AUTO_INCREMENT =0
	DEFAULT CHARSET =utf8;

CREATE UNIQUE INDEX `aliasindex` ON `#__helloworld` (`alias`, `catid`);

INSERT INTO `#__helloworld` (`greeting`,`alias`,`language`) VALUES
('Hello World!','hello-world','en-GB'),
('Goodbye World!','goodbye-world','en-GB');

Nuevo archivo de actualización de SQL:

/admin/sql/updates/mysql/0.0.24.sql

ALTER TABLE `#__helloworld` ADD COLUMN `checked_out` INT(10) NOT NULL DEFAULT '0' AFTER `created_by`;
ALTER TABLE `#__helloworld` ADD COLUMN `checked_out_time` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `checked_out`;

MVC Helloworlds

Nuestro modelo actualizado:

admin/models/helloworlds.php

<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_helloworld
 *
 * @copyright   Copyright (C) 2005 - 2018 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');

/**
 * HelloWorldList Model
 *
 * @since  0.0.1
 */
class HelloWorldModelHelloWorlds extends JModelList
{
	/**
	 * Constructor.
	 *
	 * @param   array  $config  An optional associative array of configuration settings.
	 *
	 * @see     JController
	 * @since   1.6
	 */
	public function __construct($config = array())
	{
		if (empty($config['filter_fields']))
		{
			$config['filter_fields'] = array(
				'id',
				'greeting',
				'author',
				'created',
				'language',
				'association',
				'published'
			);
		}

		parent::__construct($config);
	}

	protected function populateState($ordering = null, $direction = null)
	{
		$app = JFactory::getApplication();

		// Adjust the context to support modal layouts.
		if ($layout = $app->input->get('layout'))
		{
			$this->context .= '.' . $layout;
		}

		// Adjust the context to support forced languages.
		$forcedLanguage = $app->input->get('forcedLanguage', '', 'CMD');
		if ($forcedLanguage)
		{
			$this->context .= '.' . $forcedLanguage;
		}

		parent::populateState($ordering, $direction);
        
		// If there's a forced language then define that filter for the query where clause
		if (!empty($forcedLanguage))
		{
			$this->setState('filter.language', $forcedLanguage);
		}
	}

	/**
	 * Method to build an SQL query to load the list data.
	 *
	 * @return      string  An SQL query
	 */
	protected function getListQuery()
	{
		// Initialize variables.
		$db    = JFactory::getDbo();
		$query = $db->getQuery(true);

		// Create the base select statement.
		$query->select('a.id as id, a.greeting as greeting, a.published as published, a.created as created, 
			  a.checked_out as checked_out, a.checked_out_time as checked_out_time,
			  a.image as imageInfo, a.latitude as latitude, a.longitude as longitude, a.alias as alias, a.language as language')
			  ->from($db->quoteName('#__helloworld', 'a'));

		// Join over the categories.
		$query->select($db->quoteName('c.title', 'category_title'))
			->join('LEFT', $db->quoteName('#__categories', 'c') . ' ON c.id = a.catid');
        
		// Join with users table to get the username of the author
		$query->select($db->quoteName('u.username', 'author'))
			->join('LEFT', $db->quoteName('#__users', 'u') . ' ON u.id = a.created_by');

		// Join with users table to get the username of the person who checked the record out
		$query->select($db->quoteName('u2.username', 'editor'))
			->join('LEFT', $db->quoteName('#__users', 'u2') . ' ON u2.id = a.checked_out');

		// Join with languages table to get the language title and image to display
		// Put these into fields called language_title and language_image so that 
		// we can use the little com_content layout to display the map symbol
		$query->select($db->quoteName('l.title', 'language_title') . "," .$db->quoteName('l.image', 'language_image'))
			->join('LEFT', $db->quoteName('#__languages', 'l') . ' ON l.lang_code = a.language');

		// Join over the associations - we just want to know if there are any, at this stage
		if (JLanguageAssociations::isEnabled())
		{
			$query->select('COUNT(asso2.id)>1 as association')
				->join('LEFT', '#__associations AS asso ON asso.id = a.id AND asso.context=' . $db->quote('com_helloworld.item'))
				->join('LEFT', '#__associations AS asso2 ON asso2.key = asso.key')
				->group('a.id');
		}

		// Filter: like / search
		$search = $this->getState('filter.search');

		if (!empty($search))
		{
			$like = $db->quote('%' . $search . '%');
			$query->where('greeting LIKE ' . $like);
		}

		// Filter by published state
		$published = $this->getState('filter.published');

		if (is_numeric($published))
		{
			$query->where('a.published = ' . (int) $published);
		}
		elseif ($published === '')
		{
			$query->where('(a.published IN (0, 1))');
		}

		// Filter by language, if the user has set that in the filter field
		$language = $this->getState('filter.language');
		if ($language)
		{
			$query->where('a.language = ' . $db->quote($language));
		}

		// Add the list ordering clause.
		$orderCol	= $this->state->get('list.ordering', 'greeting');
		$orderDirn 	= $this->state->get('list.direction', 'asc');

		$query->order($db->escape($orderCol) . ' ' . $db->escape($orderDirn));

		return $query;
	}
}

Desde nuestro punto de vista, mostraremos el botón Registrar si el usuario puede editar los registros de helloworld o si tiene el permiso core.manage en el componente com_checkin.

admin/views/helloworlds/view.html.php

<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_helloworld
 *
 * @copyright   Copyright (C) 2005 - 2018 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');

/**
 * HelloWorlds View
 *
 * @since  0.0.1
 */
class HelloWorldViewHelloWorlds extends JViewLegacy
{
	/**
	 * Display the Hello World view
	 *
	 * @param   string  $tpl  The name of the template file to parse; automatically searches through the template paths.
	 *
	 * @return  void
	 */
	function display($tpl = null)
	{
		// Get application
		$app = JFactory::getApplication();

		// Get data from the model
		$this->items			= $this->get('Items');
		$this->pagination		= $this->get('Pagination');
		$this->state			= $this->get('State');
		$this->filterForm    	= $this->get('FilterForm');
		$this->activeFilters 	= $this->get('ActiveFilters');
        
		// What Access Permissions does this user have? What can (s)he do?
		$this->canDo = JHelperContent::getActions('com_helloworld');

		// Check for errors.
		if (count($errors = $this->get('Errors')))
		{
			JError::raiseError(500, implode('<br />', $errors));

			return false;
		}
        
		// Set the sidebar submenu and toolbar, but not on the modal window
		if ($this->getLayout() !== 'modal')
		{
			HelloWorldHelper::addSubmenu('helloworlds');
			$this->addToolBar();
		}
		else
		{
			// If it's being displayed to select a record as an association, then forcedLanguage is set
			if ($forcedLanguage = $app->input->get('forcedLanguage', '', 'CMD'))
			{
				// Transform the language selector filter into an hidden field, so it can't be set
				$languageXml = new SimpleXMLElement('<field name="language" type="hidden" default="' . $forcedLanguage . '" />');
				$this->filterForm->setField($languageXml, 'filter', true);

				// Also, unset the active language filter so the search tools is not open by default with this filter.
				unset($this->activeFilters['language']);
			}
		}

		// Display the template
		parent::display($tpl);

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

	/**
	 * Add the page title and toolbar.
	 *
	 * @return  void
	 *
	 * @since   1.6
	 */
	protected function addToolBar()
	{
		$title = JText::_('COM_HELLOWORLD_MANAGER_HELLOWORLDS');

		if ($this->pagination->total)
		{
			$title .= "<span style='font-size: 0.5em; vertical-align: middle;'>(" . $this->pagination->total . ")</span>";
		}

		JToolBarHelper::title($title, 'helloworld');
		if ($this->canDo->get('core.create')) 
		{
			JToolBarHelper::addNew('helloworld.add', 'JTOOLBAR_NEW');
		}
		if ($this->canDo->get('core.edit')) 
		{
			JToolBarHelper::editList('helloworld.edit', 'JTOOLBAR_EDIT');
		}
		if ($this->canDo->get('core.delete')) 
		{
			JToolBarHelper::deleteList('', 'helloworlds.delete', 'JTOOLBAR_DELETE');
		}
		if ($this->canDo->get('core.edit') || JFactory::getUser()->authorise('core.manage', 'com_checkin'))
		{
			JToolBarHelper::checkin('helloworlds.checkin');
		}
		if ($this->canDo->get('core.admin')) 
		{
			JToolBarHelper::divider();
			JToolBarHelper::preferences('com_helloworld');
		}
	}
	/**
	 * Method to set up the document properties
	 *
	 * @return void
	 */
	protected function setDocument() 
	{
		$document = JFactory::getDocument();
		$document->setTitle(JText::_('COM_HELLOWORLD_ADMINISTRATION'));
	}
}

En nuestro archivo de diseño, resaltamos los registros controlados mostrando el pequeño símbolo del candado utilizando la función JHtmlJGrid::checkedout(). Esta función nos permite pasar al usuario que ha marcado el registro y la fecha/hora de control, para que puedan mostrarse como información emergente. También pasamos la URL a la funcionalidad de registro, y habilitamos o inhabilitamos este enlace dependiendo de si el usuario actual tiene permiso com_checkin core.manage, o es el mismo que el usuario que marco el registro.

(Ten en cuenta que al momento de escribir esto, la versión actual de Joomla (3.8) tiene un error aquí: el código JModelForm que verifica si el usuario puede marcar el registro compara los permisos del usuario con com_checkin core.admin en lugar de com_checkin core.manage).

admin/views/helloworlds/tmpl/default.php

<?php
/**
 * @package     Joomla.Administrator
 * @subpackage  com_helloworld
 *
 * @copyright   Copyright (C) 2005 - 2018 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');

use Joomla\Registry\Registry;

JHtml::_('formbehavior.chosen', 'select');

$listOrder     = $this->escape($this->state->get('list.ordering'));
$listDirn      = $this->escape($this->state->get('list.direction'));
$user = JFactory::getUser();
$userId = $user->get('id');
$assoc = JLanguageAssociations::isEnabled();
$authorFieldwidth = $assoc ? "10%" : "25%";
JLoader::register('JHtmlHelloworlds', JPATH_ADMINISTRATOR . '/components/com_helloworld/helpers/html/helloworlds.php');
?>
<form action="index.php?option=com_helloworld&view=helloworlds" method="post" id="adminForm" name="adminForm">
	<div id="j-sidebar-container" class="span2">
		<?php echo JHtmlSidebar::render(); ?>
	</div>
	<div id="j-main-container" class="span10">
        <div class="row-fluid">
            <div class="span6">
                <?php echo JText::_('COM_HELLOWORLD_HELLOWORLDS_FILTER'); ?>
                <?php
                    echo JLayoutHelper::render(
                        'joomla.searchtools.default',
                        array('view' => $this)
                    );
                ?>
            </div>
        </div>
        <table class="table table-striped table-hover">
            <thead>
            <tr>
                <th width="1%"><?php echo JText::_('COM_HELLOWORLD_NUM'); ?></th>
                <th width="2%">
                    <?php echo JHtml::_('grid.checkall'); ?>
                </th>
                <th width="15%">
                    <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_HELLOWORLDS_NAME', 'greeting', $listDirn, $listOrder); ?>
                </th>
                <th width="15%">
                    <?php echo JText::_('COM_HELLOWORLD_HELLOWORLDS_POSITION'); ?>
                </th>
                <th width="15%">
                    <?php echo JText::_('COM_HELLOWORLD_HELLOWORLDS_IMAGE'); ?>
                </th>
                <?php if ($assoc) : ?>
                    <th width="15%">
                        <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_HELLOWORLDS_ASSOCIATIONS', 'association', $listDirn, $listOrder); ?>
                    </th>
                <?php endif; ?>
                <th width="<?php echo $authorFieldwidth; ?>">
                    <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_AUTHOR', 'author', $listDirn, $listOrder); ?>
                </th>
                <th width="10%">
                    <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_LANGUAGE', 'language', $listDirn, $listOrder); ?>
                </th>
                <th width="10%">
                    <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_CREATED_DATE', 'created', $listDirn, $listOrder); ?>
                </th>
                <th width="5%">
                    <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_PUBLISHED', 'published', $listDirn, $listOrder); ?>
                </th>
                <th width="2%">
                    <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_ID', 'id', $listDirn, $listOrder); ?>
                </th>
            </tr>
            </thead>
            <tfoot>
                <tr>
                    <td colspan="5">
                        <?php echo $this->pagination->getListFooter(); ?>
                    </td>
                </tr>
            </tfoot>
            <tbody>
                <?php if (!empty($this->items)) : ?>
                    <?php foreach ($this->items as $i => $row) :
                        $link = JRoute::_('index.php?option=com_helloworld&task=helloworld.edit&id=' . $row->id);
                        $row->image = new Registry;
                        $row->image->loadString($row->imageInfo);
                    ?>
                        <tr>
                            <td><?php echo $this->pagination->getRowOffset($i); ?></td>
                            <td>
                                <?php echo JHtml::_('grid.id', $i, $row->id); ?>
                            </td>
                            <td>
                                <?php if ($row->checked_out) : ?>
                                    <?php $canCheckin = $user->authorise('core.manage', 'com_checkin') || $row->checked_out == $userId; ?>
                                    <?php echo JHtml::_('jgrid.checkedout', $i, $row->editor, $row->checked_out_time, 'helloworlds.', $canCheckin); ?>
                                <?php endif; ?>
                                <a href="<?php echo $link; ?>" title="<?php echo JText::_('COM_HELLOWORLD_EDIT_HELLOWORLD'); ?>">
                                    <?php echo $row->greeting; ?>
                                </a>
                                <span class="small break-word">
                                	<?php echo JText::sprintf('JGLOBAL_LIST_ALIAS', $this->escape($row->alias)); ?>
                                </span>
                                <div class="small">
                                    <?php echo JText::_('JCATEGORY') . ': ' . $this->escape($row->category_title); ?>
                                </div>
                            </td>
                            <td align="center">
                                <?php echo "[" . $row->latitude . ", " . $row->longitude . "]"; ?>
                            </td>
                            <td align="center">
                                <?php
                                    $caption = $row->image->get('caption') ? : '' ;
                                    $src = JURI::root() . ($row->image->get('image') ? : '' );
                                    $html = '<p class="hasTooltip" style="display: inline-block" data-html="true" data-toggle="tooltip" data-placement="right" title="<img width=\'100px\' height=\'100px\' src=\'%s\'>">%s</p>';
                                    echo sprintf($html, $src, $caption);  ?>
                            </td>
                            <?php if ($assoc) : ?>
                                <td align="center">
                                    <?php if ($row->association) : ?>
                                        <?php echo JHtml::_('helloworlds.association', $row->id); ?>
                                    <?php endif; ?>
                                </td>
                            <?php endif; ?>
                            <td align="center">
                                <?php echo $row->author; ?>
                            </td>
                            <td align="center">
                                <?php echo JLayoutHelper::render('joomla.content.language', $row); ?>
                            </td>
                            <td align="center">
                                <?php echo substr($row->created, 0, 10); ?>
                            </td>
                            <td align="center">
                                <?php echo JHtml::_('jgrid.published', $row->published, $i, 'helloworlds.', true, 'cb'); ?>
                            </td>
                            <td align="center">
                                <?php echo $row->id; ?>
                            </td>
                        </tr>
                    <?php endforeach; ?>
                <?php endif; ?>
            </tbody>
        </table>
        <input type="hidden" name="task" value=""/>
        <input type="hidden" name="boxchecked" value="0"/>
        <?php echo JHtml::_('form.token'); ?>
    </div>
</form>

Actualización cadenas de idioma

admin/language/en-GB/en-GB.com_helloworld.ini

; Joomla! Project
; Copyright (C) 2005 - 2018 Open Source Matters. All rights reserved.
; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php
; Note : All ini files need to be saved as UTF-8

COM_HELLOWORLD_ADMINISTRATION="HelloWorld - Administration"
COM_HELLOWORLD_ADMINISTRATION_CATEGORIES="HelloWorld - Categories"
COM_HELLOWORLD_NUM="#"
COM_HELLOWORLD_HELLOWORLDS_FILTER="Filters"
COM_HELLOWORLD_AUTHOR="Author"
COM_HELLOWORLD_LANGUAGE="Language"
COM_HELLOWORLD_CREATED_DATE="Created"
COM_HELLOWORLD_PUBLISHED="Published"
COM_HELLOWORLD_HELLOWORLDS_NAME="Name"
COM_HELLOWORLD_HELLOWORLDS_POSITION="Position"
COM_HELLOWORLD_HELLOWORLDS_IMAGE="Image"
COM_HELLOWORLD_HELLOWORLDS_ASSOCIATIONS="Associations"
COM_HELLOWORLD_ID="Id"

COM_HELLOWORLD_HELLOWORLD_CREATING="HelloWorld - Creating"
COM_HELLOWORLD_HELLOWORLD_DETAILS="Details"
COM_HELLOWORLD_HELLOWORLD_EDITING="HelloWorld - Editing"
COM_HELLOWORLD_HELLOWORLD_ERROR_UNACCEPTABLE="Some values are unacceptable"
COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_DESC="The category the messages belongs to"
COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_LABEL="Category"
COM_HELLOWORLD_HELLOWORLD_FIELD_GREETING_DESC="This message will be displayed"
COM_HELLOWORLD_HELLOWORLD_FIELD_GREETING_LABEL="Message"
COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL="Show category"
COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC="If set to Show, the title of the message&rsquo;s category will show."
COM_HELLOWORLD_HELLOWORLD_FIELD_LATITUDE_LABEL="Latitude"
COM_HELLOWORLD_HELLOWORLD_FIELD_LATITUDE_DESC="Enter the position latitude, between -90 and +90 degrees"
COM_HELLOWORLD_HELLOWORLD_FIELD_LONGITUDE_LABEL="Longitude"
COM_HELLOWORLD_HELLOWORLD_FIELD_LONGITUDE_DESC="Enter the position longitude, between -180 and +180 degrees"
COM_HELLOWORLD_HELLOWORLD_FIELD_LANGUAGE_DESC="Select the appropriate language"
COM_HELLOWORLD_IMAGE_FIELDS="Image details"
COM_HELLOWORLD_HELLOWORLD_FIELD_IMAGE_LABEL="Select image"
COM_HELLOWORLD_HELLOWORLD_FIELD_IMAGE_DESC="Select an image from the library, or upload a new one"
COM_HELLOWORLD_HELLOWORLD_FIELD_ALT_LABEL="Alt text"
COM_HELLOWORLD_HELLOWORLD_FIELD_ALT_DESC="Alternative text (if image cannot be displayed)"
COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTION_LABEL="Caption"
COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTION_DESC="Provide a caption for the image"
COM_HELLOWORLD_HELLOWORLD_HEADING_GREETING="Greeting"
COM_HELLOWORLD_HELLOWORLD_HEADING_ID="Id"
COM_HELLOWORLD_MANAGER_HELLOWORLD_EDIT="HelloWorld manager: Edit Message"
COM_HELLOWORLD_MANAGER_HELLOWORLD_NEW="HelloWorld manager: New Message"
COM_HELLOWORLD_MANAGER_HELLOWORLDS="HelloWorld manager"
COM_HELLOWORLD_EDIT_HELLOWORLD="Edit message"
COM_HELLOWORLD_N_ITEMS_DELETED_1="One message deleted"
COM_HELLOWORLD_N_ITEMS_DELETED_MORE="%d messages deleted"
COM_HELLOWORLD_N_ITEMS_PUBLISHED="%d message(s) published"
COM_HELLOWORLD_N_ITEMS_UNPUBLISHED="%d message(s) unpublished"
COM_HELLOWORLD_HELLOWORLD_GREETING_LABEL="Greeting"
COM_HELLOWORLD_HELLOWORLD_GREETING_DESC="Add Hello World Greeting"
COM_HELLOWORLD_SUBMENU_MESSAGES="Messages"
COM_HELLOWORLD_SUBMENU_CATEGORIES="Categories"
COM_HELLOWORLD_CONFIGURATION="HelloWorld Configuration"
COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_LABEL="Messages settings"
COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_DESC="Settings that will be applied to all messages by default"
COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_LABEL="Captcha"
COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_DESC="Select Captcha to use on front end form"
COM_HELLOWORLD_HELLOWORLD_FIELD_USER_TO_EMAIL_LABEL="User to email"
COM_HELLOWORLD_HELLOWORLD_FIELD_USER_TO_EMAIL_DESC="Select user to email when a new message is entered on front end"
COM_HELLOWORLD_FIELDSET_RULES="Message Permissions"
COM_HELLOWORLD_FIELD_RULES_LABEL="Permissions"
COM_HELLOWORLD_ACCESS_DELETE_DESC="Is this group allowed to edit this message?"
COM_HELLOWORLD_ACCESS_DELETE_DESC="Is this group allowed to delete this message?"
COM_HELLOWORLD_TAB_NEW_MESSAGE="New Message"
COM_HELLOWORLD_TAB_EDIT_MESSAGE="Message Details"
COM_HELLOWORLD_TAB_PARAMS="Parameters"
COM_HELLOWORLD_TAB_ASSOCIATIONS="Associations"
COM_HELLOWORLD_TAB_PERMISSIONS="Permissions"
COM_HELLOWORLD_TAB_IMAGE="Image"
COM_HELLOWORLD_LEGEND_DETAILS="Message Details"
COM_HELLOWORLD_LEGEND_PARAMS="Message Parameters"
COM_HELLOWORLD_LEGEND_ASSOCIATIONS="Message Associations"
COM_HELLOWORLD_LEGEND_PERMISSIONS="Message Permissions"
COM_HELLOWORLD_LEGEND_IMAGE="Image info"
; Column ordering in the Helloworlds view
COM_HELLOWORLD_ORDERING_ASC="Greeting ascending"
COM_HELLOWORLD_ORDERING_DESC="Greeting descending"
COM_HELLOWORLD_AUTHOR_ASC="Author ascending"
COM_HELLOWORLD_AUTHOR_DESC="Author descending"
COM_HELLOWORLD_CREATED_ASC="Creation date ascending"
COM_HELLOWORLD_CREATED_DESC="Creation date descending"
COM_HELLOWORLD_PUBLISHED_ASC="Unpublished first"
COM_HELLOWORLD_PUBLISHED_DESC="Published first"
COM_HELLOWORLD_LANGUAGE_ASC="Language ascending"
COM_HELLOWORLD_LANGUAGE_DESC="Language descending"
COM_HELLOWORLD_ASSOCIATION_ASC="Association ascending"
COM_HELLOWORLD_ASSOCIATION_DESC="Association descending"
; Helloworld menuitem - selecting a greeting via modal
COM_HELLOWORLD_MENUITEM_SELECT_MODAL_TITLE="Select greeting"
COM_HELLOWORLD_MENUITEM_SELECT_HELLOWORLD="Select"
COM_HELLOWORLD_MENUITEM_SELECT_BUTTON_TOOLTIP="Select a helloworld greeting"
; Checking in records
COM_HELLOWORLD_N_ITEMS_CHECKED_IN_0="No records checked in."
COM_HELLOWORLD_N_ITEMS_CHECKED_IN_1="%d record successfully checked in."
COM_HELLOWORLD_N_ITEMS_CHECKED_IN_MORE="%d records successfully checked in."

Empaquetado del componente

Contenido de su directorio de código. Cada enlace a un archivo a continuación te lleva al paso en el tutorial que tiene la última versión de ese archivo de código fuente.

helloworld.xml

<?xml version="1.0" encoding="utf-8"?>
<extension type="component" version="3.0" method="upgrade">

	<name>COM_HELLOWORLD</name>
	<!-- The following elements are optional and free of formatting constraints -->
	<creationDate>January 2018</creationDate>
	<author>John Doe</author>
	<authorEmail>john.doe@example.org</authorEmail>
	<authorUrl>http://www.example.org</authorUrl>
	<copyright>Copyright Info</copyright>
	<license>License Info</license>
	<!--  The version string is recorded in the components table -->
	<version>0.0.24</version>
	<!-- The description is optional and defaults to the name -->
	<description>COM_HELLOWORLD_DESCRIPTION</description>

	<!-- Runs on install/uninstall/update; New in 2.5 -->
	<scriptfile>script.php</scriptfile>

	<install> <!-- Runs on install -->
		<sql>
			<file driver="mysql" charset="utf8">sql/install.mysql.utf8.sql</file>
		</sql>
	</install>
	<uninstall> <!-- Runs on uninstall -->
		<sql>
			<file driver="mysql" charset="utf8">sql/uninstall.mysql.utf8.sql</file>
		</sql>
	</uninstall>
	<update> <!-- Runs on update; New since J2.5 -->
		<schemas>
			<schemapath type="mysql">sql/updates/mysql</schemapath>
		</schemas>
	</update>

	<!-- Site Main File Copy Section -->
	<!-- Note the folder attribute: This attribute describes the folder
		to copy FROM in the package to install therefore files copied
		in this section are copied from /site/ in the package -->
	<files folder="site">
		<filename>index.html</filename>
		<filename>helloworld.php</filename>
		<filename>controller.php</filename>
		<filename>router.php</filename>
		<folder>controllers</folder>
		<folder>views</folder>
		<folder>models</folder>
		<folder>helpers</folder>
	</files>

		<languages folder="site/language">
			<language tag="en-GB">en-GB/en-GB.com_helloworld.ini</language>
			<language tag="fr-FR">fr-FR/fr-FR.com_helloworld.ini</language>
		</languages>

	<media destination="com_helloworld" folder="media">
		<filename>index.html</filename>
		<folder>images</folder>
		<folder>js</folder>
		<folder>css</folder>
	</media>

	<administration>
		<!-- Administration Menu Section -->
		<menu link='index.php?option=com_helloworld' img="../media/com_helloworld/images/tux-16x16.png">COM_HELLOWORLD_MENU</menu>
		<!-- Administration Main File Copy Section -->
		<!-- Note the folder attribute: This attribute describes the folder
			to copy FROM in the package to install therefore files copied
			in this section are copied from /admin/ in the package -->
		<files folder="admin">
			<!-- Admin Main File Copy Section -->
			<filename>index.html</filename>
			<filename>config.xml</filename>
			<filename>helloworld.php</filename>
			<filename>controller.php</filename>
			<filename>access.xml</filename>
			<!-- SQL files section -->
			<folder>sql</folder>
			<!-- tables files section -->
			<folder>tables</folder>
			<!-- models files section -->
			<folder>models</folder>
			<!-- views files section -->
			<folder>views</folder>
			<!-- controllers files section -->
			<folder>controllers</folder>
			<!-- helpers files section -->
			<folder>helpers</folder>
		</files>
		<languages folder="admin/language">
			<language tag="en-GB">en-GB/en-GB.com_helloworld.ini</language>
			<language tag="en-GB">en-GB/en-GB.com_helloworld.sys.ini</language>
			<language tag="fr-FR">fr-FR/fr-FR.com_helloworld.ini</language>
			<language tag="fr-FR">fr-FR/fr-FR.com_helloworld.sys.ini</language>
		</languages>
	</administration>

</extension>

Colaboradores