Es gibt mehrere Gründe dafür, einem Anwender das Editieren im Frontend zu ermöglichen. Zum einen empfinden es die Nutzer als benutzerfreundlicher, direkt auf der Website zu arbeiten, als sich in das Backend einzuloggen. Oder, einem Administrator ist es wichtig, den Zugriff auf den Administrationsbereich nicht freizugeben. Deshalb statten wir unsere Komponente im nächsten Schritt mit der Möglichkeit aus, Items im Frontend zu bearbeiten.
Für Ungeduldige: Sieh dir den geänderten Programmcode in der Diff-Ansicht[^codeberg.org/astrid/j4examplecode/compare/t24b...t25] an und übernimm diese Änderungen in deine Entwicklungsversion.
Schritt für Schritt
Neue Dateien
administrator/components/com_foos/src/Service/HTML/Icon.php
Die folgende Datei enthält alle Informationen, um ein Icon, über das die Bearbeitung geöffnet wird, im Frontend anzuzeigen - vorausgesetzt, der Betrachter darf bearbeiten.
administrator/components/com_foos/src/Service/HTML/Icon.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t25/src/administrator/components/com_foos/src/Service/HTML/Icon.php
<?php
/**
* @package Joomla.Site
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace FooNamespace\Component\Foos\Administrator\Service\HTML;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\User\UserFactoryInterface;
use FooNamespace\Component\Foos\Site\Helper\RouteHelper;
use Joomla\Registry\Registry;
\defined('_JEXEC') or die;
/**
* Content Component HTML Helper
*
* @since __BUMP_VERSION__
*/
class Icon
{
/**
* The user factory
*
* @var UserFactoryInterface
*
* @since __BUMP_VERSION__
*/
private $userFactory;
/**
* Service constructor
*
* @param UserFactoryInterface $userFactory The userFactory
*
* @since __BUMP_VERSION__
*/
public function __construct(UserFactoryInterface $userFactory)
{
$this->userFactory = $userFactory;
}
/**
* Method to generate a link to the create item page for the given category
*
* @param object $category The category information
* @param Registry $params The item parameters
* @param array $attribs Optional attributes for the link
*
* @return string The HTML markup for the create item link
*
* @since __BUMP_VERSION__
*/
public function create($category, $params, $attribs = [])
{
$uri = Uri::getInstance();
$url = 'index.php?option=com_$foo&task=$foo.add&return=' . base64_encode($uri) . '&id=0&catid=' . $category->id;
$text = '';
if ($params->get('show_icons')) {
$text .= '<span class="icon-plus icon-fw" aria-hidden="true"></span>';
}
$text .= Text::_('COM_FOOS_NEW_FOOS');
// Add the button classes to the attribs array
if (isset($attribs['class'])) {
$attribs['class'] .= ' btn btn-primary';
} else {
$attribs['class'] = 'btn btn-primary';
}
$button = HTMLHelper::_('link', Route::_($url), $text, $attribs);
return $button;
}
/**
* Display an edit icon for the $foo.
*
* This icon will not display in a popup window, nor if the $foo is trashed.
* Edit access checks must be performed in the calling code.
*
* @param object $foo The $foo information
* @param Registry $params The item parameters
* @param array $attribs Optional attributes for the link
* @param boolean $legacy True to use legacy images, false to use icomoon based graphic
*
* @return string The HTML for the $foo edit icon.
*
* @since __BUMP_VERSION__
*/
public function edit($foo, $params, $attribs = [], $legacy = false)
{
$user = Factory::getUser();
$uri = Uri::getInstance();
// Ignore if in a popup window.
if ($params && $params->get('popup')) {
return '';
}
// Ignore if the state is negative (trashed).
if ($foo->published < 0) {
return '';
}
// Show checked_out icon if the $foo is checked out by a different user
if (
property_exists($foo, 'checked_out')
&& property_exists($foo, 'checked_out_time')
&& !is_null($foo->checked_out)
&& $foo->checked_out !== $user->get('id')
) {
$checkoutUser = $this->userFactory->loadUserById($foo->checked_out);
$date = HTMLHelper::_('date', $foo->checked_out_time);
$tooltip = Text::sprintf('COM_FOOS_CHECKED_OUT_BY', $checkoutUser->name)
. ' <br> ' . $date;
$text = LayoutHelper::render('joomla.content.icons.edit_lock', ['$foo' => $foo, 'tooltip' => $tooltip, 'legacy' => $legacy]);
$attribs['aria-describedby'] = 'edit$foo-' . (int) $foo->id;
$output = HTMLHelper::_('link', '#', $text, $attribs);
return $output;
}
if (!isset($foo->slug)) {
$foo->slug = "";
}
$fooUrl = RouteHelper::getFooRoute($foo->slug, $foo->catid, $foo->language);
$url = $fooUrl . '&task=$foo.edit&id=' . $foo->id . '&return=' . base64_encode($uri);
if ((int) $foo->published === 0) {
$tooltip = Text::_('COM_FOOS_EDIT_UNPUBLISHED_FOOS');
} else {
$tooltip = Text::_('COM_FOOS_EDIT_PUBLISHED_FOOS');
}
$nowDate = strtotime(Factory::getDate());
$icon = $foo->published ? 'edit' : 'eye-slash';
if (
($foo->publish_up !== null && strtotime($foo->publish_up) > $nowDate)
|| ($foo->publish_down !== null && strtotime($foo->publish_down) < $nowDate)
) {
$icon = 'eye-slash';
}
$aria_described = 'edit$foo-' . (int) $foo->id;
$text = '<span class="icon-' . $icon . '" aria-hidden="true"></span>';
$text .= Text::_('JGLOBAL_EDIT');
$text .= '<div role="tooltip" id="' . $aria_described . '">' . $tooltip . '</div>';
$attribs['aria-describedby'] = $aria_described;
$output = HTMLHelper::_('link', Route::_($url), $text, $attribs);
return $output;
}
}
components/com_foos/forms/foo.xml
Wir passen die XML Datei an, die Joomla verwendet, um das Formular aufzubauen.
components/com_foos/forms/foo.xml
<!-- https://codeberg.org/astrid/j4examplecode/raw/branch/t25/src/components/com_foos/forms/foo.xml -->
<?xml version="1.0" encoding="utf-8"?>
<form>
<fieldset
addruleprefix="FooNamespace\Component\Foos\Administrator\Rule"
addfieldprefix="FooNamespace\Component\Foos\Administrator\Field"
>
<field
name="id"
type="number"
label="JGLOBAL_FIELD_ID_LABEL"
default="0"
class="readonly"
readonly="true"
/>
<field
name="name"
type="text"
validate="Letter"
class="validate-letter"
label="COM_FOOS_FIELD_NAME_LABEL"
size="40"
required="true"
/>
<field
name="alias"
type="text"
label="JFIELD_ALIAS_LABEL"
size="45"
hint="JFIELD_ALIAS_PLACEHOLDER"
/>
</fieldset>
<fieldset name="language" label="JFIELD_LANGUAGE_LABEL">
<field
name="language"
type="contentlanguage"
label="JFIELD_LANGUAGE_LABEL"
>
<option value="*">JALL</option>
</field>
</fieldset>
<fieldset name="publishing" label="JGLOBAL_FIELDSET_PUBLISHING">
<field
name="featured"
type="list"
label="JFEATURED"
default="0"
validate="options"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="published"
type="list"
label="JSTATUS"
default="1"
id="published"
class="custom-select-color-state"
size="1"
>
<option value="1">JPUBLISHED</option>
<option value="0">JUNPUBLISHED</option>
<option value="2">JARCHIVED</option>
<option value="-2">JTRASHED</option>
</field>
<field
name="publish_up"
type="calendar"
label="COM_FOOS_FIELD_PUBLISH_UP_LABEL"
translateformat="true"
showtime="true"
size="22"
filter="user_utc"
/>
<field
name="publish_down"
type="calendar"
label="COM_FOOS_FIELD_PUBLISH_DOWN_LABEL"
translateformat="true"
showtime="true"
size="22"
filter="user_utc"
/>
<field
name="catid"
type="categoryedit"
label="JCATEGORY"
extension="com_foos"
addfieldprefix="Joomla\Component\Categories\Administrator\Field"
required="true"
default=""
/>
<field
name="access"
type="accesslevel"
label="JFIELD_ACCESS_LABEL"
size="1"
/>
<field
name="checked_out"
type="hidden"
filter="unset"
/>
<field
name="checked_out_time"
type="hidden"
filter="unset"
/>
</fieldset>
<fields name="params" label="JGLOBAL_FIELDSET_DISPLAY_OPTIONS">
<fieldset name="display" label="JGLOBAL_FIELDSET_DISPLAY_OPTIONS">
<field
name="show_name"
type="list"
label="COM_FOOS_FIELD_PARAMS_NAME_LABEL"
useglobal="true"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="foos_layout"
type="componentlayout"
label="JFIELD_ALT_LAYOUT_LABEL"
class="custom-select"
extension="com_foos"
view="foo"
useglobal="true"
/>
</fieldset>
</fields>
</form>
components/com_foos/src/Controller/FooController.php
Die Datei components/com_foos/src/Controller/FooController.php
enhält die Logik für die Bearbeitung im Formular.
Beachte die Funktion
save
. Diese ist imFormController
nicht üblich, weil Joomla alles für dich übernimmt. Da beim Erstellen eines Elementes die ID erst erstellt wird und deshalb nicht bekannt ist, leitet Joomla nach dem Erstellen zur Übersichtsseite weiter. Diese haben wir im Frontend noch nicht erstellt. Deshalb habe ich diese Funktion hier abgeändert.
components/com_foos/src/Controller/FooController.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t25/src/components/com_foos/src/Controller/FooController.php
<?php
/**
* @package Joomla.Site
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace FooNamespace\Component\Foos\Site\Controller;
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Controller\FormController;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Uri\Uri;
use Joomla\Utilities\ArrayHelper;
/**
* Controller for single foo view
*
* @since __DEPLOY_VERSION__
*/
class FooController extends FormController
{
/**
* The URL view item variable.
*
* @var string
* @since __DEPLOY_VERSION__
*/
protected $view_item = 'form';
/**
* Method to get a model object, loading it if required.
*
* @param string $name The model name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config Configuration array for model. Optional.
*
* @return \Joomla\CMS\MVC\Model\BaseDatabaseModel The model.
*
* @since __DEPLOY_VERSION__
*/
public function getModel($name = 'form', $prefix = '', $config = ['ignore_request' => true])
{
return parent::getModel($name, $prefix, ['ignore_request' => false]);
}
/**
* Method override to check if you can add a new record.
*
* @param array $data An array of input data.
*
* @return boolean
*
* @since __DEPLOY_VERSION__
*/
protected function allowAdd($data = [])
{
if ($categoryId = ArrayHelper::getValue($data, 'catid', $this->input->getInt('catid'), 'int')) {
$user = Factory::getUser();
// If the category has been passed in the data or URL check it.
return $user->authorise('core.create', 'com_foos.category.' . $categoryId);
}
// In the absence of better information, revert to the component permissions.
return parent::allowAdd();
}
/**
* Method override to check if you can edit an existing record.
*
* @param array $data An array of input data.
* @param string $key The name of the key for the primary key; default is id.
*
* @return boolean
*
* @since __DEPLOY_VERSION__
*/
protected function allowEdit($data = [], $key = 'id')
{
$recordId = (int) isset($data[$key]) ? $data[$key] : 0;
if (!$recordId) {
return false;
}
// Need to do a lookup from the model.
$record = $this->getModel()->getItem($recordId);
$categoryId = (int) $record->catid;
if ($categoryId) {
$user = Factory::getUser();
// The category has been set. Check the category permissions.
if ($user->authorise('core.edit', $this->option . '.category.' . $categoryId)) {
return true;
}
// Fallback on edit.own.
if ($user->authorise('core.edit.own', $this->option . '.category.' . $categoryId)) {
return ($record->created_by == $user->id);
}
return false;
}
// Since there is no asset tracking, revert to the component permissions.
return parent::allowEdit($data, $key);
}
/**
* Method to save a record.
*
* @param string $key The name of the primary key of the URL variable.
* @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions).
*
* @return boolean True if successful, false otherwise.
*
* @since __DEPLOY_VERSION__
*/
public function save($key = null, $urlVar = null)
{
$result = parent::save($key, $urlVar = null);
$this->setRedirect(Route::_($this->getReturnPage(), false));
return $result;
}
/**
* Method to cancel an edit.
*
* @param string $key The name of the primary key of the URL variable.
*
* @return boolean True if access level checks pass, false otherwise.
*
* @since __DEPLOY_VERSION__
*/
public function cancel($key = null)
{
$result = parent::cancel($key);
$this->setRedirect(Route::_($this->getReturnPage(), false));
return $result;
}
/**
* Gets the URL arguments to append to an item redirect.
*
* @param integer $recordId The primary key id for the item.
* @param string $urlVar The name of the URL variable for the id.
*
* @return string The arguments to append to the redirect URL.
*
* @since __DEPLOY_VERSION__
*/
protected function getRedirectToItemAppend($recordId = 0, $urlVar = 'id')
{
// Need to override the parent method completely.
$tmpl = $this->input->get('tmpl');
$append = '';
// Setup redirect info.
if ($tmpl) {
$append .= '&tmpl=' . $tmpl;
}
$append .= '&layout=edit';
$append .= '&' . $urlVar . '=' . (int) $recordId;
$itemId = $this->input->getInt('Itemid');
$return = $this->getReturnPage();
$catId = $this->input->getInt('catid');
if ($itemId) {
$append .= '&Itemid=' . $itemId;
}
if ($catId) {
$append .= '&catid=' . $catId;
}
if ($return) {
$append .= '&return=' . base64_encode($return);
}
return $append;
}
/**
* Get the return URL.
*
* If a "return" variable has been passed in the request
*
* @return string The return URL.
*
* @since __DEPLOY_VERSION__
*/
protected function getReturnPage()
{
$return = $this->input->get('return', null, 'base64');
if (empty($return) || !Uri::isInternal(base64_decode($return))) {
return Uri::base();
}
return base64_decode($return);
}
}
components/com_foos/src/Model/FormModel.php
Die Datei components/com_foos/src/Model/FormModel.php
organisiert alle notwendigen Daten für die Bearbeitung im Formular.
components/com_foos/src/Model/FormModel.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t25/src/components/com_foos/src/Model/FormModel.php
<?php
/**
* @package Joomla.Site
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace FooNamespace\Component\Foos\Site\Model;
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Language\Multilanguage;
use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;
/**
* Foo Component Foo Model
*
* @since __DEPLOY_VERSION__
*/
class FormModel extends \FooNamespace\Component\Foos\Administrator\Model\FooModel
{
/**
* Model typeAlias string. Used for version history.
*
* @var string
* @since __DEPLOY_VERSION__
*/
public $typeAlias = 'com_foos.foo';
/**
* Name of the form
*
* @var string
* @since __DEPLOY_VERSION__
*/
protected $formName = 'form';
/**
* Method to get the row 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 \JForm|boolean A \JForm object on success, false on failure
*
* @since __DEPLOY_VERSION__
*/
public function getForm($data = [], $loadData = true)
{
$form = parent::getForm($data, $loadData);
// Prevent messing with article language and category when editing existing foo with associations
if ($id = $this->getState('foo.id') && Associations::isEnabled()) {
$associations = Associations::getAssociations('com_foos', '#__foos_details', 'com_foos.item', $id);
// Make fields read only
if (!empty($associations)) {
$form->setFieldAttribute('language', 'readonly', 'true');
$form->setFieldAttribute('language', 'filter', 'unset');
}
}
return $form;
}
/**
* Method to get foo data.
*
* @param integer $itemId The id of the foo.
*
* @return mixed Foo item data object on success, false on failure.
*
* @throws Exception
*
* @since __DEPLOY_VERSION__
*/
public function getItem($itemId = null)
{
$itemId = (int) (!empty($itemId)) ? $itemId : $this->getState('foo.id');
// Get a row instance.
$table = $this->getTable();
// Attempt to load the row.
try {
if (!$table->load($itemId)) {
return false;
}
} catch (Exception $e) {
Factory::getApplication()->enqueueMessage($e->getMessage());
return false;
}
$properties = $table->getProperties();
$value = ArrayHelper::toObject($properties, 'JObject');
// Convert field to Registry.
$value->params = new Registry($value->params);
return $value;
}
/**
* Get the return URL.
*
* @return string The return URL.
*
* @since __DEPLOY_VERSION__
*/
public function getReturnPage()
{
return base64_encode($this->getState('return_page'));
}
/**
* Method to save the form data.
*
* @param array $data The form data.
*
* @return boolean True on success.
*
* @throws Exception
* @since __DEPLOY_VERSION__
*/
public function save($data)
{
// Associations are not edited in frontend ATM so we have to inherit them
if (Associations::isEnabled() && !empty($data['id'])
&& $associations = Associations::getAssociations('com_foos', '#__foos_details', 'com_foos.item', $data['id'])) {
foreach ($associations as $tag => $associated) {
$associations[$tag] = (int) $associated->id;
}
$data['associations'] = $associations;
}
return parent::save($data);
}
/**
* Method to auto-populate the model state.
*
* Note. Calling getState in this method will result in recursion.
*
* @return void
*
* @throws Exception
*
* @since __DEPLOY_VERSION__
*/
protected function populateState()
{
$app = Factory::getApplication();
$input = $app->getInput();
$pk = $input->getInt('id');
$this->setState('foo.id', $pk);
$this->setState('foo.catid', $input->getInt('catid'));
$return = $input->get('return', '', 'base64');
$this->setState('return_page', base64_decode($return));
// Load the parameters.
$params = $app->getParams();
$this->setState('params', $params);
$this->setState('layout', $input->getString('layout'));
}
/**
* Allows preprocessing of the JForm object.
*
* @param Form $form The form object
* @param array $data The data to be merged into the form object
* @param string $group The plugin group to be executed
*
* @return Form
*
* @since __DEPLOY_VERSION__
*/
protected function preprocessForm(Form $form, $data, $group = 'foo')
{
if (!Multilanguage::isEnabled()) {
$form->setFieldAttribute('language', 'type', 'hidden');
$form->setFieldAttribute('language', 'default', '*');
}
return parent::preprocessForm($form, $data, $group);
}
/**
* Method to get a table object, load it if necessary.
*
* @param string $name The table name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $options Configuration array for model. Optional.
*
* @return Table A Table object
*
* @since __DEPLOY_VERSION__
* @throws \Exception
*/
public function getTable($name = 'Foo', $prefix = 'Administrator', $options = [])
{
return parent::getTable($name, $prefix, $options);
}
}
components/com_foos/src/View/Form/HtmlView.php
Die Datei components/com_foos/src/View/Form/HtmlView.php
holt alle notwendigen Daten und gibt diese an die Templatedatei edit.php
weiter.
components/com_foos/src/View/Form/HtmlView.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t25/src/components/com_foos/src/View/Form/HtmlView.php
<?php
/**
* @package Joomla.Site
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace FooNamespace\Component\Foos\Site\View\Form;
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use FooNamespace\Component\Foos\Administrator\Helper\FooHelper;
/**
* HTML Foo View class for the Foo component
*
* @since __DEPLOY_VERSION__
*/
class HtmlView extends BaseHtmlView
{
/**
* @var \Joomla\CMS\Form\Form
* @since __DEPLOY_VERSION__
*/
protected $form;
/**
* @var object
* @since __DEPLOY_VERSION__
*/
protected $item;
/**
* @var string
* @since __DEPLOY_VERSION__
*/
protected $return_page;
/**
* @var string
* @since __DEPLOY_VERSION__
*/
protected $pageclass_sfx;
/**
* @var \Joomla\Registry\Registry
* @since __DEPLOY_VERSION__
*/
protected $state;
/**
* @var \Joomla\Registry\Registry
* @since __DEPLOY_VERSION__
*/
protected $params;
/**
* Execute and display a template script.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return mixed A string if successful, otherwise an Error object.
*
* @throws Exception
* @since __DEPLOY_VERSION__
*/
public function display($tpl = null)
{
$user = Factory::getUser();
$app = Factory::getApplication();
// Get model data.
$this->state = $this->get('State');
$this->item = $this->get('Item');
$this->form = $this->get('Form');
$this->return_page = $this->get('ReturnPage');
if (empty($this->item->id)) {
$authorised = $user->authorise('core.create', 'com_foos') || count($user->getAuthorisedCategories('com_foos', 'core.create'));
} else {
// Since we don't track these assets at the item level, use the category id.
$canDo = FooHelper::getActions('com_foos', 'category', $this->item->catid);
$authorised = $canDo->get('core.edit') || ($canDo->get('core.edit.own') && $this->item->created_by == $user->id);
}
if ($authorised !== true) {
$app->enqueueMessage(Text::_('JERROR_ALERTNOAUTHOR'), 'error');
$app->setHeader('status', 403, true);
return false;
}
// Check for errors.
if (count($errors = $this->get('Errors'))) {
$app->enqueueMessage(implode("\n", $errors), 'error');
return false;
}
// Create a shortcut to the parameters.
$this->params = $this->state->params;
// Escape strings for HTML output
$this->pageclass_sfx = htmlspecialchars($this->params->get('pageclass_sfx', ''));
// Override global params with foo specific params
$this->params->merge($this->item->params);
// Propose current language as default when creating new foo
if (empty($this->item->id) && Multilanguage::isEnabled()) {
$lang = Factory::getLanguage()->getTag();
$this->form->setFieldAttribute('language', 'default', $lang);
}
$this->_prepareDocument();
parent::display($tpl);
}
/**
* Prepares the document
*
* @return void
*
* @throws Exception
*
* @since __DEPLOY_VERSION__
*/
protected function _prepareDocument()
{
$app = Factory::getApplication();
$menus = $app->getMenu();
$title = null;
// Because the application sets a default page title,
// we need to get it from the menu item itself
$menu = $menus->getActive();
if ($menu) {
$this->params->def('page_heading', $this->params->get('page_title', $menu->title));
} else {
$this->params->def('page_heading', Text::_('COM_FOOS_FORM_EDIT_FOO'));
}
$title = $this->params->def('page_title', Text::_('COM_FOOS_FORM_EDIT_FOO'));
if ($app->get('sitename_pagetitles', 0) == 1) {
$title = Text::sprintf('JPAGETITLE', $app->get('sitename'), $title);
} else if ($app->get('sitename_pagetitles', 0) == 2) {
$title = Text::sprintf('JPAGETITLE', $title, $app->get('sitename'));
}
$this->document->setTitle($title);
$pathway = $app->getPathWay();
$pathway->addItem($title, '');
}
}
Im obigen Codebeispiel habe mich beim Prüfen der Berechtigungen an Code in Joomla orientiert. Ist jemand nicht berechtigt, wird ihm einen Nachricht angezeigt. Je nachdem in welchem Umfeld man die Erweiterung programmiert ist es benutzerfreundlicher sofort eine Anmeldemöglichkeit zu bieten. Wenn du in der Datei components/com_foos/src/View/Form/HtmlView.php
den nachfolgenden Codeauszug
if ($authorised !== true) {
$app->redirect('index.php?option=com_users&view=login');
}
anstelle dieses
if ($authorised !== true)
{
$app->enqueueMessage(Text::_('JERROR_ALERTNOAUTHOR'), 'error');
$app->setHeader('status', 403, true);
return false;
}
einfügst, erfolgt bei einer fehlgeschlagenen Berechtigungsprüfung unmittelbar eine Weiterleitung zum Anmeldeformular.
components/com_foos/tmpl/form/edit.php
components/com_foos/tmpl/form/edit.php
sorgt als Template dafür, dass das Formular schon im Frontend angezeigt wird.
components/com_foos/tmpl/form/edit.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t25/src/components/com_foos/tmpl/form/edit.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Associations;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
HTMLHelper::_('behavior.keepalive');
HTMLHelper::_('behavior.formvalidator');
HTMLHelper::_('script', 'com_foos/admin-foos-letter.js', ['version' => 'auto', 'relative' => true]);
$this->tab_name = 'com-foos-form';
$this->ignore_fieldsets = ['details', 'item_associations', 'language'];
$this->useCoreUI = true;
?>
<form action="<?php echo Route::_('index.php?option=com_foos&id=' . (int) $this->item->id); ?>" method="post" name="adminForm" id="adminForm" class="form-validate form-vertical">
<fieldset>
<?php echo HTMLHelper::_('uitab.startTabSet', $this->tab_name, ['active' => 'details']); ?>
<?php echo HTMLHelper::_('uitab.addTab', $this->tab_name, 'details', empty($this->item->id) ? Text::_('COM_FOOS_NEW_FOO') : Text::_('COM_FOOS_EDIT_FOO')); ?>
<?php echo $this->form->renderField('name'); ?>
<?php if (is_null($this->item->id)) : ?>
<?php echo $this->form->renderField('alias'); ?>
<?php endif; ?>
<?php echo $this->form->renderFieldset('details'); ?>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php if (Multilanguage::isEnabled()) : ?>
<?php echo HTMLHelper::_('uitab.addTab', $this->tab_name, 'language', Text::_('JFIELD_LANGUAGE_LABEL')); ?>
<?php echo $this->form->renderField('language'); ?>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php else : ?>
<?php echo $this->form->renderField('language'); ?>
<?php endif; ?>
<?php echo LayoutHelper::render('joomla.edit.params', $this); ?>
<?php echo HTMLHelper::_('uitab.endTabSet'); ?>
<input type="hidden" name="task" value=""/>
<input type="hidden" name="return" value="<?php echo $this->return_page; ?>"/>
<?php echo HTMLHelper::_('form.token'); ?>
</fieldset>
<div class="mb-2">
<button type="button" class="btn btn-primary" onclick="Joomla.submitbutton('foo.save')">
<span class="fas fa-check" aria-hidden="true"></span>
<?php echo Text::_('JSAVE'); ?>
</button>
<button type="button" class="btn btn-danger" onclick="Joomla.submitbutton('foo.cancel')">
<span class="fas fa-times-cancel" aria-hidden="true"></span>
<?php echo Text::_('JCANCEL'); ?>
</button>
</div>
</form>
components/com_foos/tmpl/form/edit.xml
Zu guter Letzt benötigen wir die Datei components/com_foos/tmpl/form/edit.xml
, um den Menüpunkt zu erstellen.
components/com_foos/tmpl/form/edit.xml
<!-- https://codeberg.org/astrid/j4examplecode/raw/branch/t25/src/components/com_foos/tmpl/form/edit.xml -->
<?xml version="1.0" encoding="utf-8"?>
<metadata>
<layout title="COM_FOOS_FORM_VIEW_DEFAULT_TITLE">
<help
key="JHELP_MENUS_MENU_ITEM_FOO_CREATE"
/>
<message>
<![CDATA[COM_FOOS_FORM_VIEW_DEFAULT_DESC]]>
</message>
</layout>
<fields name="params">
</fields>
</metadata>
Geänderte Dateien
administrator/components/com_foos/src/Extension/FoosComponent.php
In der Datei administrator/components/com_foos/src/Extension/FoosComponent.php
registrieren wir das Icon. Anders ausgedruckt: Wir machen Icon mit Joomla bekannt.
administrator/components/com_foos/src/Extension/FoosComponent.php
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Application\UserFactoryInterface;
use Joomla\CMS\Association\AssociationServiceInterface;
use Joomla\CMS\Association\AssociationServiceTrait;
use Joomla\CMS\Categories\CategoryServiceInterface;
use Joomla\CMS\Extension\MVCComponent;
use Joomla\CMS\HTML\HTMLRegistryAwareTrait;
use FooNamespace\Component\Foos\Administrator\Service\HTML\AdministratorService;
use FooNamespace\Component\Foos\Administrator\Service\HTML\Icon;
use Psr\Container\ContainerInterface;
use Joomla\CMS\Helper\ContentHelper;
public function boot(ContainerInterface $container)
{
$this->getRegistry()->register('foosadministrator', new AdministratorService);
$this->getRegistry()->register('fooicon', new Icon($container->get(UserFactoryInterface::class)));
}
/**
components/com_foos/tmpl/foo/default.php
Wir erweitern das Template für die Ansicht: Wenn man das Element bearbeiten darf if ($canEdit)
, dann sieht man das Icon zum Öffnen des Formulares.
components/com_foos/tmpl/foo/default.php
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\Language\Text;
if ($this->item->params->get('show_name')) {
$canDo = ContentHelper::getActions('com_foos', 'category', $this->item->catid);
$canEdit = $canDo->get('core.edit') || ($canDo->get('core.edit.own') && $this->item->created_by == Factory::getUser()->id);
$tparams = $this->item->params;
if ($tparams->get('show_name')) {
if ($this->params->get('show_foo_name_label')) {
echo Text::_('COM_FOOS_NAME');
}
echo $this->item->name;
}
?>
<?php if ($canEdit) : ?>
<div class="icons">
<div class="btn-group float-right">
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton-<?php echo $this->item->id; ?>"
aria-label="<?php echo JText::_('JUSER_TOOLS'); ?>"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="fa fa-cog" aria-hidden="true"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton-<?php echo $this->item->id; ?>">
<li class="edit-icon"> <?php echo JHtml::_('fooicon.edit', $this->item, $tparams); ?> </li>
</ul>
</div>
</div>
<?php endif; ?>
<?php
echo $this->item->event->afterDisplayTitle;
echo $this->item->event->beforeDisplayContent;
echo $this->item->event->afterDisplayContent;
Tipp: Möchtest du, dass ein Benutzer nach dem Anlegen eines Items zur fertigen Ansicht von diesem weiterleitet wird. Dies ist nur über Umwege möglich. Weil man beim Erstellen die ID nicht kennt, muss man diese erfragen. Da wir die Model-Klassen von Joomla-Core erweitern, können wir auf die ID über das Model in der
postSaveHook()
Methode des Controllers zugreifen. Konkrete könnte in der Dateisrc/components/com_foos/src/Controller/FooController.php
der nachfolgende Code verwendet werden, um die Weiterleitung einzurichten:
...
protected function postSaveHook(\Joomla\CMS\MVC\Model\BaseDatabaseModel $model, $validData = [])
{
$id = $model->getState($model->getName() . '.id');
$this->setRedirect(Route::_('index.php?option=com_agosms&view=foo&id=' . $id, false));
return $id;
}
...
Teste deine Joomla-Komponente
- Installiere deine Komponente in Joomla Version 4, um sie zu testen:
Kopiere die Dateien im administrator
Ordner in den administrator
Ordner deiner Joomla 4 Installation.
Kopiere die Dateien im components
Ordner in den components
Ordner deiner Joomla 4 Installation.
Installiere deine Komponente wie in Teil eins beschrieben, nachdem du alle Dateien kopiert hast. Joomla aktualisiert bei der Installation die Namespaces für dich. Da eine neue Datei hinzugekommen ist, ist dies erforderlich.
- Erstelle einen Menüpunkt zum Ändern eines Foo-Elementes und einen der ein Foo Element anzeigt.
- Öffne den Menüpunkt zum Erstellen eines Foo-Elementes im Frontend. Stelle sicher, dass du die notwendigen Rechte hast. Falls du die Standardrechte belassen hast, musst du dich mit einem Benutzer anmelden, der mindestens Autor ist. Überzeuge dich davon, dass du ein Element erstellen kannst.
- Stelle sicher, dass du das Icon zum Editieren bei der Detailanzeige eines Elements siehst und eine Element editierbar ist.
Links
Frontend-Edit für com_contact[^github.com/joomla/joomla-cms/pull/24311]
Webmentions