J3.x

J3.x: Entwicklung einer MVC Komponente/Alias hinzufügen

From Joomla! Documentation

< J3.x:Developing an MVC Component
This page is a translated version of the page J3.x:Developing an MVC Component/Adding an alias and the translation is 98% complete.

Outdated translations are marked like this.
Other languages:
Deutsch • ‎English • ‎español • ‎français
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).



Info

Dieses Tutorial ist ein Teil der Entwicklung einer MVC Komponente für Joomla! 3.x Artikel-Serie. Es ist sinnvoll die vorherigen Teile des Tutorials zu lesen, bevor du hier fortfährst.

Der Hauptzweck in diesem Schritt ist es, eine Einführung in die Routerfunktionsweise von Joomla! zu liefern, die für die Übernahme der URL in der ankommenden HTTP-Anfrage verantwortlich ist, welche herausfinden soll, welche Komponenten für das Abfertigen der Anfrage bestimmt sind und welche die Anfrageparameter (Ansicht=abc, id=123, etc.) korrekt bildet, sodass dieses Teil auf sie über die Standard-API-Aufrufe zugreifen kann.

Wir werden ein Aliasfeld in jedem unserer hallo Welt Datensätze einrichten, und wir werden diesen Alias innerhalb der SEF (suchmaschinenfreundlichen) URLs benutzen, um auf unsere hallo Welt Nachrichten zugreifen zu können. Um dies zu erreichen werden wir unseren eigenen angepassten Komponentenrouter definieren. In diesem Anleitungsschritt wird der Router ziemlich simple aufgebaut sein, aber wir werden ihn im nächsten Schritt viel filigraner gestalten, wenn wir unsere Komponente vielsprachig gestalten. And we'll develop it further whenever we add associations.

Auf diesem Weg werden wir einige der Anwendungsprogrammierschnittstellen (APIs) betrachten, die mit Menüs und Menüeinträgen verknüpft sind.

Ein neuer Vermittlungsknoten wurde mit Joomla! 3.8 eingeführt, dieser Anleitungsschritt betrachtet allerdings nur den traditionellen.

Zwei Videos sind verfügbar, die mit diesem Schritt verknüpft sind, diese decken Joomla general routing principles und tutorial code description ab.

Hintergrund von SEF URLs

SEF URLs sind URLs, die sowohl für Menschen als auch Suchmaschinen Sinn ergeben. Sollten wir beispielsweise statt einer Datenbank von hallo Welt Datensätzen eine Datenbank von Bergen haben, dann würde eine SEF URL etwa so ausschauen:

www.domainname.net/mountains/himalayas/everest

statt irgenwas wie

www.domainname.net/mountains?catid=37&id=52

Man kann SEF URLs über die globale Konfiguration, Reiter "Seite", SEO Einstellungen, in Joomla! an- und abschalten.

Zurück auf "Adding a variable request in the menu type" fügten wir die Möglichkeit für einen Administrator, einen hallo Welt Menüeintrag zu erzeugen, hinzu. Außerdem konnte er den jeweiligen Gruß, der auf der Webseite angezeigt werden sollte, auswählen. So könnte ein Administrator einen Menüeintrag hinzufügen, der eine URL ähnlich wie

localhost/myjoomlasite/hello-world hat, und wenn man drauf klickte, zeigte es "hallo Welt", weil sie / er die Anzeige speziell dieser Begrüßung auf dieser Webseite gewählt hatte.

Im zugrunde legenden Code setzten wir allerdings nie einen speziellen hallo Welt Datensatz voraus - wir nahmen einfach nur eine ID und fanden die mit der Begrüßung verbundene ID. Und tatsächlich können wir eine URL

localhost/myjoomlasite/hello-world?id=2

eingeben und gelangen zur Anzeige einer verschiedenartigen Begrüßung.

Auf diese Weise wird die ID, die der Administrator in dem Menüeintrag wählte, übergangen von der ID, die du als URL-Parameter einträgst. Wenn der Joomla! Router eine URL auswertet, findet er zuerst den passenden Menüeintrag und jeden Parameter (z. B. ID, Kategorien-ID, Ansicht), der damit verbunden ist. Allerdings wird er diejenigen Standardparameter mit allen, die er in der Abfragezeichenkette der URL findet, übergehen. Das ist ein wichtiger Verständnispunkt!

Trotzdem ist unsere URL, eine alternative Nachricht zu erhalten, nicht gerade suchmaschinenfreundlich. Was wir stattdessen haben ist ein Menüeintrag mit einer URL wie

localhost/myjoomlasite/messages

zu der wir den spezifischen Gruß, den wir dargestellt haben wollten, hinzufügen könnten localhost/myjoomlasite/messages/hello-world

localhost/myjoomlasite/messages/goodbye-world

Dieser Schritt in der Anleitung ermöglicht uns genau das zu tun.

Funktionsweise

Wir werden unserem hallo Welt Datensatz ein Feld für eine Parallebezeichnung, ein Alias, und außerdem eine Funktionsweise innerhalb unserer Admineingabemaske zum Festsetzen und Pflegen der Aliasdaten, hinzufügen. Diese Funktionsweise ähnelt der Verwendung von Aliassen innerhalb anderer Joomla! Komponenten wie com_content, mit der Ausnahme dass wir unseren Alias zwingen, einzigartig zu sein (aber wir werden das überdenken, wenn wir einen Blick auf das Beinhalten zur Fähigkeit der Vielsprachigkeit werfen). Wir werden unsere Front-End Eingabemaske auch aktualisieren um es einen Nutzer zu ermöglichen, ein Alias einzugeben.

Den Alias zur Einzigartikeit zu zwingen wird tatsächlich ein Problem mit der Administratorfunktion "Speichere als Kopie" verursachen, und wir werden uns dem zuwenden müssen.

Wir stellen ein Äquivalent des Artikels Kategorieliste zur Verfügung, in der eine Liste von hallo Welt-Datensätzen mit einer bestimmten Kategorie angezeigt wird. Auf diese Weise können wir das URL-Format von hallo Welt-Nachrichten anzeigen, denen kein bestimmter Menüeintrag zugewiesen ist. Wenn wir auf die URL klicken, können wir darauf zugreifen.

Im vorhergenden Schritt boten wir eine Liste von hallo Welt Nachrichten über Ajax an, wenn irgendwer auf das "Suche hier" Icon verknüpft mit der Abbildung drückte. Im folgenden Schritt werden wir für jeden dieser Nachrichten einen Link bieten, sodass ein Benutzer darauf klicken kann um sie zu öffnen.

Im letzten Schritt entwickeln wir unsere angepasste Routerfunktionsweise um die Erzeugung von SEF URLs zu aktivieren.

Vorgehensweise

Datenbank

Wir werden unser Aliasfeld auf die selbe Weise wie Joomla! Kernkomponenten definieren. Weil wir wollen, dass unser Aliasfeld besetzt (nicht leer) und eindeutig ist, besetzen wir die Aliasfelder der bereits bestehenden Datensätze in userem SQL Aktualisierungsskript, welches auf einem einmaligen ID-Feld basiert.

Adminmanagement

Die Verwaltung der Aliasdaten fügt sich naturgemäß mit minimalen Veränderungen in unser bestehendes Admingerüst zur Verwaltung von hallo Welt Datensätzen ein. Wir müssen nur sicher stellen, dass der Alias eindeutig ist und keine Zeichenfolgen enthält, die in URLs nicht erlaubt sind. Das bewerkstelligen wir, indem den JTable::check() Befehl umgehen.

Als Kopie speichern

Wenn wir einen hallo Welt Datensatz bearbeiten, kann der Administrator auf das Icon "Als Kopie speichern" klicken, was dazu führt, dass im gegenwärtigen Feld die Werte der Eingabemaske als neuer Datensatz gespeichert werden. Falls allerdings das Aliasfeld vom ursprünglichen Eintrag nicht verändert wurde, wird das Speichern als Kopie einen Fehler ausgeben wegen dem eindeutigen Index des Alias. Während das genaugenommen richtig ist, ist es doch nicht sehr benutzerfreundlich, also übernehmen wir dieselbe Herangehensweise wie bei com_content, welche aufsteigende Zahlen in den Artikeltitel und den Alias solange hinzufügt, bis es eine Zahl erwischt, die keine Probleme verursacht.

Kategorie Listenansicht

Dies wird ziemlich unkompliziert und wird sich mehr wie die Wiederholung von vorhergehenden Schritten in diesem Tutorial anfühlen.

Wenn erst diese neue Funktionsweise entwickelt und installiert wurde, wirst du auf die Joomla! Administratorebene gehen und einen Menüeintrag mit dem Alias "Nachrichten", der auf die hallo Welt Kategorien Listenansicht verweist, erstellen müssen. Bei der Erstellung des Menüeintrags kommt es nicht darauf an, welche Kategorie du wählst. An diesen Menüeintrag hängen wir die hallo Welt Begrüßungen, sodass falls die URL in dem Menüeintrag

localhost/myjoomlasite/messages

ist, werden die URLs unserer hallo Welt Begrüßungen beispielsweise sein:

localhost/myjoomlasite/messages/hello-world

localhost/myjoomlasite/messages/goodbye-world

Front-End Eingabemaske

Da der Alias benötigt wird und eindeutig sein muss, müssen wir unsere Eingabemaske aktualisieren, um es dem Nutzer zu erlauben, auch das Aliasfeld einzugeben. Das wiederum wird Änderungen an unserer xml Formulardefinition erfordern, aber unser bestehender Layout- und Modellkode passt dafür.

Ajax Änderungen

In unserem Joomla! Serverkode, namentlich unser hallo Welt Modell, wird mit jeder zurückgegebenen Nachricht eine URL dieser Nachricht hinzugefügt werden, erreicht mit dem Aufruf JRoute::_ und dem Weiterreichen der hallo Welt Nachrichten ID.

Allerdings erfahren wir hier ein kleines Problem, wir können zu unserem Abbild gelangen und auf diese Weise zu unserem Ajaxaufruf über unseren Menüeintrag, der eine bestimmte hallo Welt Nachricht anzeigt, zum Beispiel

localhost/myjoomlasite/hello-world

Falls wir nur die Nachrichten ID zu unserem JRoute Aufruf hinzugefügt haben, würden wir mit URLs enden wie

localhost/myjoomlasite/hello-world/goodbye-world

was nicht wirklich unsere Intention ist.

Das ist so, weil jedesmal wenn es eine SEF URL erzeugt, nimmt JRoute::_ den Standardmenüeintrag, auf den es momentan geschaltet ist (das aktive Menüelement), der in diesem Fall der Menüeintrag mit dem dem Alias hallo-Welt ist. Falls wir wollen, das eine davon verschiedene URL erzeugt wird, das mit einem anderen Menüeintrag verknüpft ist, müssen wir die Menü-ID diesen anderen Menüeintrags weiterleiten. Also werden wir durch unseren Code die Menüeinträge auf dem System mit dem Alias "Nachrichten" durchsuchen und an JRoute::_die Elemente-ID vom Menüeintrag, den wir finden, weiterleiten müssen (Die Elemente-ID ist die ID-Spalte ganz rechts von den Menüelementen auf der Joomla! Adminseite aus gesehen).

Um unseren "hallo-Welt" Menüeintrag an unseren "Nachrichten" Menüeintrag zu vermittlen haben wir zwei Möglichkeiten:

  1. In dem Code auf unserem Server, der Ajax behandelt, könnten wir, nachdem wir den hallo Welt Datensatz in unserem hallo Welt Modell gefunden haben, der "Nachrichten"-Menüelement Element-ID dem Aufruf von JRoute::_ hinzufügen, indem "&Itemid=xxx" dazugegeben wird, um die URL für jeden dieser Datensätze zu erzeugen.
  2. Außerdem könnten wir die URL für unseren Ajax-Aufruf so ändern, dass dieser im Kontext des "Nachrichten"-Menüelements läuft. Dann, wenn JRoute::_ aufgerufen wird, befindet er sich bereits im richtigen Kontext, und wir werden keine einzige Menüeintrag Eintrags-ID weiterreichen müssen.

Im folgenden Codebeispiel haben wir uns für die zweite Alternative entschieden.

Router

Wir werden unseren angepassten Routercode entwickeln - mehr dazu später.

Datenkonfiguration

Wenn erst diese neue Version installiert ist, muss das "Nachrichten" Menüelement wie oben beschrieben angelegt werden.

Datenbankaktualisierung

Das Aliasfeld dem Datenbankeintrag beimischen.

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',
	`greeting` VARCHAR(25) NOT NULL,
	`alias`  VARCHAR(40)  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`);

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

Neue SQL Aktualisierungsdatei: /admin/sql/updates/mysql/0.0.20.sql

ALTER TABLE`#__helloworld` ADD COLUMN `alias` VARCHAR(40) NOT NULL DEFAULT '' AFTER `greeting`;
UPDATE `#__helloworld` AS h1
SET alias = (SELECT CONCAT('id-', ID) FROM (SELECT * FROM `#__helloworld`) AS h2 WHERE h1.id = h2.id);
CREATE UNIQUE INDEX `aliasindex` ON `#__helloworld` (`alias`);

Jeder aktualisierte hallo Welt Datensatz endet mit einem Alias von "id-" und der ID des Datensatzes. Nachdem du diesen Schritt aus dem Tutorial erledigt hast, magst du möglicherweise einige davon verändern, damit sie einen aussagekräftigen Alias haben.

Adminverwaltung

Unsere XML Definition des Formulars um hallo Welt Datensätze zu bearbeiten: admin/models/forms/helloworld.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"
				validate="greeting"
				required="true"
				default=""
				/>
		<field 
				name="alias" 
				type="text" 
				label="JFIELD_ALIAS_LABEL"
				description="JFIELD_ALIAS_DESC"
				hint="JFIELD_ALIAS_PLACEHOLDER"
				size="40" 
				/>
		<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="latitude"
				type="number"
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_LATITUDE_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_LATITUDE_DESC"
				min="-90.0"
				max="90.0"
				class="inputbox"
				required="true"
				default="0.0"
				/>
		<field
				name="longitude"
				type="number"
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_LONGITUDE_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_LONGITUDE_DESC"
				min="-180.0"
				max="180.0"
				class="inputbox"
				required="true"
				default="0.0"
				/>
	</fieldset>
	<fields name="imageinfo">
		<fieldset
			name="image-info"
			label="COM_HELLOWORLD_IMAGE_FIELDS"
		>
			<field
				name="image"
				type="media"
				preview="tooltip"
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_IMAGE_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_IMAGE_DESC" />
			<field name="alt"
				type="text"
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_ALT_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_ALT_DESC"
				size="30"/>
			<field name="caption"
				type="text"
				label="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTION_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTION_DESC"
				size="30"/>
		</fieldset>
	</fields>
	<fields name="params">
		<fieldset
				name="params"
				label="JGLOBAL_FIELDSET_DISPLAY_OPTIONS"
		>
			<field
					name="show_category"
					type="list"
					label="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL"
					description="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC"
					default=""
			>
				<option value="">JGLOBAL_USE_GLOBAL</option>
				<option value="0">JHIDE</option>
				<option value="1">JSHOW</option>
			</field>
		</fieldset>
	</fields>
	<fieldset
			name="accesscontrol"
			label="COM_HELLOWORLD_FIELDSET_RULES"
	>
    	<field
				name="asset_id"
				type="hidden"
				filter="unset"
				/>
    	<field
				name="rules"
				type="rules"
				label="COM_HELLOWORLD_FIELD_RULES_LABEL"
				filter="rules"
				validate="rules"
				class="inputbox"
				component="com_helloworld"
				section="message"
				/>
    </fieldset>
</form>

Unser hallo Welt Modell, um Daten für unsere Darstellung von hallo Welt Datensätzen bereitzustellen: 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',
				'published'
			);
		}

		parent::__construct($config);
	}

	/**
	 * 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.image as imageInfo, a.latitude as latitude, a.longitude as longitude, a.alias as alias')
			  ->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');
            
		// 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))');
		}

		// 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;
	}
}

Für die Ansicht von hallo Welt Datensätzen, werden wir den Alias auf die gleiche Weise zeigen, wie er in com_content dargestellt wird, und zwar in Kleinbuchstaben unter der Grußformel. 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'));
?>
<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="30%">
                    <?php echo JText::_('COM_HELLOWORLD_HELLOWORLDS_IMAGE'); ?>
                </th>
                <th width="15%">
                    <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_AUTHOR', 'author', $listDirn, $listOrder); ?>
                </th>
                <th width="15%">
                    <?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>
                                <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>
                            <td align="center">
                                <?php echo $row->author; ?>
                            </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>

Wenn wir irgendwas in die Datenbank unter Verwendung von JTable schreiben, wird die Funktion check() aufgerufen bevor der Datensatz gespeichert wird. Daher können wir das übergehen um sicherzustellen, dass unser Aliasfeld nicht leer ist und keine unerlaubten Zeichenfolgen hat, die durch den JFilterOutput::stringURLSafe() Befehl ausgefiltert werden. Dieser wird sowohl die Datensätze überprüfen, die über die Admineingabemaske eingegeben wurden, wie auch diejenigen, die über das Front-End-Formular eingetragen wurden. Unser eindeutiger Index der hallo Welt Tabelle stellt sicher, dass die Aliaswerte nicht doppelt verwendet werden.

admin/tables/helloworld.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
defined('_JEXEC') or die('Restricted access');

/**
 * Hello Table class
 *
 * @since  0.0.1
 */
class HelloWorldTableHelloWorld extends JTable
{
	/**
	 * Constructor
	 *
	 * @param   JDatabaseDriver  &$db  A database connector object
	 */
	function __construct(&$db)
	{
		parent::__construct('#__helloworld', 'id', $db);
	}
	/**
	 * Overloaded bind function
	 *
	 * @param       array           named array
	 * @return      null|string     null is operation was satisfactory, otherwise returns an error
	 * @see JTable:bind
	 * @since 1.5
	 */
	public function bind($array, $ignore = '')
	{
		if (isset($array['params']) && is_array($array['params']))
		{
			// Convert the params field to a string.
			$parameter = new JRegistry;
			$parameter->loadArray($array['params']);
			$array['params'] = (string)$parameter;
		}

		if (isset($array['imageinfo']) && is_array($array['imageinfo']))
		{
			// Convert the imageinfo array to a string.
			$parameter = new JRegistry;
			$parameter->loadArray($array['imageinfo']);
			$array['image'] = (string)$parameter;
		}

		// Bind the rules.
		if (isset($array['rules']) && is_array($array['rules']))
		{
			$rules = new JAccessRules($array['rules']);
			$this->setRules($rules);
		}

		return parent::bind($array, $ignore);
	}

	/**
	 * Method to compute the default name of the asset.
	 * The default name is in the form `table_name.id`
	 * where id is the value of the primary key of the table.
	 *
	 * @return	string
	 * @since	2.5
	 */
	protected function _getAssetName()
	{
		$k = $this->_tbl_key;
		return 'com_helloworld.helloworld.'.(int) $this->$k;
	}
	/**
	 * Method to return the title to use for the asset table.
	 *
	 * @return	string
	 * @since	2.5
	 */
	protected function _getAssetTitle()
	{
		return $this->greeting;
	}
	/**
	 * Method to get the asset-parent-id of the item
	 *
	 * @return	int
	 */
	protected function _getAssetParentId(JTable $table = NULL, $id = NULL)
	{
		// We will retrieve the parent-asset from the Asset-table
		$assetParent = JTable::getInstance('Asset');
		// Default: if no asset-parent can be found we take the global asset
		$assetParentId = $assetParent->getRootId();

		// Find the parent-asset
		if (($this->catid)&& !empty($this->catid))
		{
			// The item has a category as asset-parent
			$assetParent->loadByName('com_helloworld.category.' . (int) $this->catid);
		}
		else
		{
			// The item has the component as asset-parent
			$assetParent->loadByName('com_helloworld');
		}

		// Return the found asset-parent-id
		if ($assetParent->id)
		{
			$assetParentId=$assetParent->id;
		}
		return $assetParentId;
	}

	public function check()
	{
		$this->alias = trim($this->alias);
		if (empty($this->alias))
		{
			$this->alias = $this->greeting;
		}
		$this->alias = JFilterOutput::stringURLSafe($this->alias);
		return true;
	}
}

Als Kopie speichern

Um zu verstehen, was wir tun müssen, versuche einmal auf "Speichern als Kopie" beim Bearbeiten eines Artikels zu klicken. Joomla! erschafft einen neuen Beitrag, hängt (1) an den Titel und "-1" an den Alias.

Schließe den neuen Artikel, gehe zurück und bearbeite den Originalbeitrag, dann klicke erneut auf "Speichere als Kopie". Dieses Mal hängt Joomla (2) an den Titel und "-2" an den Alias und überspringt Datensatz (1), der bereits besteht. Um das nummerien des Titels und Aliases zu unterstützen, hat Joomla! das Dienstprogramm generateNewTitle(), welches solange die Zahlen erhöht, bis es eine Kombination von Alias plus Kategorie findet, die nicht schon in der Datenbank existiert. Diese Funktion werden wir in unserem aktualisierten hallo Welt Modell verwenden. Zur gleichen Zeit werden wir eine Gültigkeitsprüfung der Kategorien-ID, die aus dem Formular hochgeladen wurde, vorstellen (Das wird das Problem nicht zu 100 Prozent lösen, das wird erst passieren, wenn wir im nächsten Schritt unseren eindeutigen Index auf Alias+Kategorie, statt nur auf Alias, ändern).

We'll do this by overriding save() in our model. Why save() and not save2copy(), seeing as the task in this case is set to save2copy? If you look at the code of JControllerForm you'll see

$this->registerTask('save2copy', 'save');

was bedeutet, dass die Bearbeitungsformularvorlage den Befehl task=save2copy im save() Befehl, der innerhalb des Kontrollers ausgeführt wird und wiederum den Befehl save() im Modell ausführt, zur Folge hat. Verschiedenste Varianten der Speichernfunktion laufen innerhalb der save() Methode im Kontroller und Modell, wenn wir also irgeneinen Kode typisch für save2copy einfügen wollen, müssen wir überprüfen, auf was die ursprüngliche Aufgabenvariable gesetzt war.

admin/models/helloworld.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;

/**
 * HelloWorld Model
 *
 * @since  0.0.1
 */
class HelloWorldModelHelloWorld extends JModelAdmin
{
	/**
	 * Method to override getItem to allow us to convert the JSON-encoded image information
	 * in the database record into an array for subsequent prefilling of the edit form
	 */
	public function getItem($pk = null)
	{
		$item = parent::getItem($pk);
		if ($item AND property_exists($item, 'image'))
		{
			$registry = new Registry($item->image);
			$item->imageinfo = $registry->toArray();
		}
		return $item; 
	}
	/**
	 * 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.helloworld',
			'helloworld',
			array(
				'control' => 'jform',
				'load_data' => $loadData
			)
		);

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

		return $form;
	}
	/**
	 * Method to get the script that have to be included on the form
	 *
	 * @return string	Script files
	 */
	public function getScript() 
	{
		return 'administrator/components/com_helloworld/models/forms/helloworld.js';
	}
	/**
	 * Method to get the data that should be injected in the form.
	 *
	 * @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()
		);

		if (empty($data))
		{
			$data = $this->getItem();
		}

		return $data;
	}
	/**
	 * Method to override the JModelAdmin save() function to handle Save as Copy correctly
	 *
	 * @param   The helloworld record data submitted from the form.
	 *
	 * @return  parent::save() return value
	 */
	public function save($data)
	{
		$input = JFactory::getApplication()->input;

		JLoader::register('CategoriesHelper', JPATH_ADMINISTRATOR . '/components/com_categories/helpers/categories.php');

		// Validate the category id
		// validateCategoryId() returns 0 if the catid can't be found
		if ((int) $data['catid'] > 0)
		{
			$data['catid'] = CategoriesHelper::validateCategoryId($data['catid'], 'com_helloworld');
		}

		// Alter the greeting and alias for save as copy
		if ($input->get('task') == 'save2copy')
		{
			$origTable = clone $this->getTable();
			$origTable->load($input->getInt('id'));

			if ($data['greeting'] == $origTable->greeting)
			{
				list($greeting, $alias) = $this->generateNewTitle($data['catid'], $data['alias'], $data['greeting']);
				$data['greeting'] = $greeting;
				$data['alias'] = $alias;
			}
			else
			{
				if ($data['alias'] == $origTable->alias)
				{
					$data['alias'] = '';
				}
			}
			// standard Joomla practice is to set the new record as unpublished
			$data['published'] = 0;
		}

		return parent::save($data);
	}
	/**
	 * Method to check if it's OK to delete a message. Overrides JModelAdmin::canDelete
	 */
	protected function canDelete($record)
	{
		if( !empty( $record->id ) )
		{
			return JFactory::getUser()->authorise( "core.delete", "com_helloworld.helloworld." . $record->id );
		}
	}
}

Die Addition von Zahlen (1), (2), und so weiter zu unserer hallo Welt Grußformel verursacht uns ein Problem, weil wir festgelegt habe, dass die Grüße nur Buchstaben enthalten sollten. Also lockern wird diese Bedingung, und erlauben alle Zeichen außer ein Sternchen. Wir müssen beide Gültigkeitsprüfungen, sowohl die im Client-Javaskript als auch die im Server-PHP, von dem was wir in Adding Verifications angegeben haben, ändern.

admin/models/rules/greeting.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');
 
/**
 * Form Rule class for the Joomla Framework.
 */
class JFormRuleGreeting extends JFormRule
{
	/**
	 * The regular expression.
	 *
	 * @access	protected
	 * @var		string
	 * @since	2.5
	 */
	protected $regex = '^[^\*]+$';
}

admin/models/forms/helloworld.js

jQuery(function() {
    document.formvalidator.setHandler('greeting',
        function (value) {
            regex=/^[^\*]+$/;
            return regex.test(value);
        });
});

Kategorie Listenansicht

Die Kategorien Listenansicht ist ähnlich einer Front-End Version von unserer hallo Welt Adminansicht, welche die Liste der hallo Welt Nachrichten darstellt. Also ist unser Code ähnlich dem dort verwendeten. Vom Kontroller wird lediglich verlangt, die Ansicht darzustellen, also ist keine Änderung notwendig, aber wir werden:

  • eine neue Ansicht - welche wir in einen neuen Seitenkategorieordner legen
  • eine neue verknüpfte Layoutdatei
  • ein neu verbundenes Modell
  • eine XML Datei im Layoutverzeichnis - für eine Menüoption, sodass der Administrator ein Menüelement erzeugen kann, welches auf eine Kategorieliste deutet
  • eine XML Datei, die mit dem Filterformular zusammen hängt

brauchen. Beim Erzeugen neuer Verzeichnisse sollten wir daran denken, in jedes eine index.php Dummy-Datei zu schreiben.

Insgesamt gibt es hier nichts wirklich Neues, was nicht schon in vorhergehenden Anleitungsschritten abgedeckt worden ist.

Unsere neue Kategorielisten Ansichtsdatei: site/views/category/view.html.php

<?php
/*
 * View file for the view which displays a list of helloworld messages in a given category
 */
 
defined('_JEXEC') or die;

class HelloworldViewCategory extends JViewLegacy
{
	public function display($tpl = null)
	{
		$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');

		parent::display($tpl);
	}
}

Unsere neue Layoutdatei, in der wir am Bildschirm die Grußformel, den Alias, und was wir als URL von JRoute::_ erhalten, ausgeben, wenn wir die Parameter wie unten gezeigt weitergeben.

site/views/category/tmpl/default.php

<?php
/**
 * Layout file for displaying helloworld messages belonging to a given category
 */

defined('_JEXEC') or die;

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

$listOrder     = $this->escape($this->state->get('list.ordering'));
$listDirn      = $this->escape($this->state->get('list.direction'));
?>
<form action="#" method="post" id="adminForm" name="adminForm">

<div id="j-main-container" class="span10">
    <div class="row-fluid">
        <div class="span10">
            <?php
                echo JLayoutHelper::render(
                    'joomla.searchtools.default',
                    array('view' => $this, 'searchButton' => false)
                );
            ?>
        </div>
    </div>
<table class="table table-striped table-hover">
    <thead>
    <tr>
        <th width="5%"><?php echo JText::_('JGLOBAL_NUM'); ?></th>
        <th width="20%">
            <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_HELLOWORLD_GREETING_LABEL', 'greeting', $listDirn, $listOrder); ?>
        </th>
        <th width="20%">
            <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_HELLOWORLD_ALIAS_LABEL', 'alias', $listDirn, $listOrder); ?>
        </th>
        <th width="20%">
            <?php echo JText::_('COM_HELLOWORLD_HELLOWORLD_FIELD_URL_LABEL'); ?>
        </th>
        <th width="5%">
            <?php echo JHtml::_('searchtools.sort', 'JGLOBAL_FIELD_ID_LABEL', '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) : 
                $url = JRoute::_('index.php?option=com_helloworld&view=helloworld&id=' . $row->id);
                ?>
                <tr>
                    <td align="center"><?php echo $this->pagination->getRowOffset($i); ?></td>
                    <td align="center"><?php echo $row->greeting; ?></td>
                    <td align="center"><?php echo $row->alias; ?></td>
                    <td align="center"><a href="<?php echo $url; ?>"><?php echo $url; ?></a></td>
                    <td align="center"><?php echo $row->id; ?></td>
                </tr>
            <?php endforeach; ?>
        <?php endif; ?>
    </tbody>
</table>
</div>
</form>

Unser neues Modell

site/models/category.php

<?php
/**
 * Model for displaying the helloworld messages in a given category
 */

defined('_JEXEC') or die;

class HelloworldModelCategory extends JModelList
{
	public function __construct($config = array())
	{
		if (empty($config['filter_fields']))
		{
			$config['filter_fields'] = array(
				'id',
				'greeting',
				'alias',
			);
		}

		parent::__construct($config);
	}
    
	protected function populateState($ordering = null, $direction = null)
	{
		parent::populateState($ordering, $direction);
        
		$app = JFactory::getApplication('site');
		$catid = $app->input->getInt('id');

		$this->setState('category.id', $catid);
	}
    
	protected function getListQuery()
	{
		$db    = JFactory::getDbo();
		$query = $db->getQuery(true);

		$catid = $this->getState('category.id'); 
		$query->select('id, greeting, alias')
			->from($db->quoteName('#__helloworld'))
			->where('catid = ' . $catid);

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

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

		return $query;	
	}
}

Unsere neue XML Datei für den Kategorielisten Menüeintrag:

site/views/category/tmpl/default.xml

<?xml version="1.0" encoding="utf-8"?>
<metadata>
	<layout title="COM_HELLOWORLD_CATEGORY_VIEW_TITLE">
		<message>
			<![CDATA[COM_HELLOWORLD_CATEGORY_VIEW_DESC]]>
		</message>
	</layout>

	<!-- Add fields to the request variables for the layout. -->
	<fields name="request">
		<fieldset name="request"
			addfieldpath="/administrator/components/com_helloworld/models/fields"
		>
			<field 
				name="id"
				type="category"
				label="JGLOBAL_CHOOSE_CATEGORY_LABEL"
				description="JGLOBAL_CHOOSE_CATEGORY_DESC"
				extension="com_helloworld"
			/>
		</fieldset>
	</fields>
</metadata>

Unsere Definition der Filterfelder, die wir in dieser Ansicht sehen wollen:

site/models/forms/filter_category.xml

<?xml version="1.0" encoding="utf-8"?>
<form>
	<fields name="list">
        <field
			name="fullordering"
			type="list"
			onchange="this.form.submit();"
			default="greeting ASC"
			>
			<option value="">COM_HELLOWORLD_SORT_BY</option>
			<option value="greeting ASC">COM_HELLOWORLD_GREETING_ASC</option>
			<option value="greeting DESC">COM_HELLOWORLD_GREETING_DESC</option>
			<option value="id ASC">COM_HELLOWORLD_ID_ASC</option>
			<option value="id DESC">COM_HELLOWORLD_ID_DESC</option>
			<option value="alias ASC">COM_HELLOWORLD_ALIAS_ASC</option>
			<option value="alias DESC">COM_HELLOWORLD_ALIAS_DESC</option>
		</field>
		<field
			name="limit"
			type="limitbox"
			class="input-mini"
			default="10"
			onchange="this.form.submit();"
		/>
	</fields>
</form>

Front-End Formular

Unsere aktualisierte xml Formularddefinitionsdatei: 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="alias" 
				type="text" 
				label="JFIELD_ALIAS_LABEL"
				description="JFIELD_ALIAS_DESC"
				hint="JFIELD_ALIAS_PLACEHOLDER"
				size="40" 
				/>
		<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>
		<fields name="imageinfo" label="COM_HELLOWORLD_HELLOWORLD_IMAGE_LABEL">
			<field
				name="image"
				type="file"
				label="COM_HELLOWORLD_HELLOWORLD_PICTURE_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_PICTURE_DESC" 
				accept="image/*"
				>
			</field>
			<field
 				name="caption"
				type="text"
				label="COM_HELLOWORLD_HELLOWORLD_CAPTION_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_CAPTION_DESC"
				size="40"
				class="inputbox"
				>
			</field>
			<field
				name="alt"
				type="text"
				label="COM_HELLOWORLD_HELLOWORLD_ALTTEXT_LABEL"
				description="COM_HELLOWORLD_HELLOWORLD_ALTTEXT_DESC"
				size="40"
				class="inputbox"
				>
			</field>
		</fields>
		<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>

Ajax Veränderungen

In unserer Javaskriptdatei erwarten wir nun die Ajax URL, die vom Server abgeholt wird: media/js/openstreetmap.js

var map;
var ajaxurl;

jQuery(document).ready(function() {
    
    // get the data passed from Joomla PHP
    // params is a Javascript object with properties for the map display: 
    // centre latitude, centre longitude and zoom, and the helloworld greeting
    const params = Joomla.getOptions('params');
    ajaxurl = params.ajaxurl; 
    
    // We'll use OpenLayers to draw the map (http://openlayers.org/)
    
    // Openlayers uses an x,y coordinate system for positions
    // We need to convert our lat/long into an x,y pair which is relative
    // to the map projection we're using, viz Spherical Mercator WGS 84
    const x = parseFloat(params.longitude);
    const y = parseFloat(params.latitude);
    const mapCentre = ol.proj.fromLonLat([x, y]); // Spherical Mercator is assumed by default
    
    // To draw a map, Openlayers needs:
    // 1. a target HTML element into which the map is put
    // 2. a map layer, which can be eg a Vector layer with details of polygons for
    //    country boundaries, lines for roads, etc, or a Tile layer, with individual
    //    .png files for each map tile (256 by 256 pixel square).
    // 3. a view, specifying the 2D projection of the map (default Spherical Mercator),
    //    map centre coordinates and zoom level
    map = new ol.Map({
        target: 'map',
        layers: [
            new ol.layer.Tile({  // we'll get the tiles from the OSM server
                source: new ol.source.OSM()
            })
        ],
        view: new ol.View({  // default is Spherical Mercator projection
            center: mapCentre,
            zoom: params.zoom
        })
    });
    
    // Now we add a marker for our Helloworld position
    // To do that, we specify it as a Point Feature, and we add styling 
    // to define how that Feature is presented on the map
    var helloworldPoint = new ol.Feature({geometry: new ol.geom.Point(mapCentre)});
    // we'll define the style as a red 5 point star with blue edging
    const redFill = new ol.style.Fill({
        color: 'red'
    });
    const blueStroke = new ol.style.Stroke({
        color: 'blue',
        width: 3
    });
    const star = new ol.style.RegularShape({
        fill: redFill,
        stroke: blueStroke,
        points: 5,
        radius1: 20,   // outer radius of star
        radius2: 10,   // inner radius of star
    })
    helloworldPoint.setStyle(new ol.style.Style({
        image: star
    }));
    // now we add the feature to the map via a Vector source and Vector layer
    const vectorSource = new ol.source.Vector({});
    vectorSource.addFeature(helloworldPoint);
    const vector = new ol.layer.Vector({
        source: vectorSource
    });
    map.addLayer(vector);
    
    // If a user clicks on the star, then we'll show the helloworld greeting
    // The greeting will go into another HTML element, with id="greeting-container"
    // and this will be shown as an Overlay on the map
    var overlay = new ol.Overlay({
        element: document.getElementById('greeting-container'),
    });
    map.addOverlay(overlay);
        
    // Finally we add the onclick listener to display the greeting when the star is clicked
    // The way this works is that the onclick listener is attached to the map,
    // and then it works out which Feature or Features have been hit
    map.on('click', function(e) {
        let markup = '';
        let position;
        map.forEachFeatureAtPixel(e.pixel, function(feature) {  // for each Feature hit
            markup = params.greeting;
            position = feature.getGeometry().getCoordinates();
        }, {hitTolerance: 5});  // tolerance of 5 pixels
        if (markup) {
            document.getElementById('greeting-container').innerHTML = markup;
            overlay.setPosition(position);
        } else {
            overlay.setPosition();  // this hides it, if we click elsewhere
        }
    });    
});

function getMapBounds(){
    var mercatorMapbounds = map.getView().calculateExtent(map.getSize());
    var latlngMapbounds = ol.proj.transformExtent(mercatorMapbounds,'EPSG:3857','EPSG:4326');
    return { minlat: latlngMapbounds[1],
             maxlat: latlngMapbounds[3],
             minlng: latlngMapbounds[0],
             maxlng: latlngMapbounds[2] }
}
    
function searchHere() {
    var mapBounds = getMapBounds();
    var token = jQuery("#token").attr("name");
    jQuery.ajax({
        url: ajaxurl,
        data: { [token]: "1", task: "mapsearch", view: "helloworld", format: "json", mapBounds: mapBounds },
        success: function(result, status, xhr) { displaySearchResults(result); },
        error: function() { console.log('ajax call failed'); },
    });
}

function displaySearchResults(result) {
    if (result.success) {
        var html = "";
        for (var i=0; i<result.data.length; i++) {
            html += '<p><a href="' + result.data[i].url + '">' +
                result.data[i].greeting + '</a>' +
                " @ " + result.data[i].latitude + 
                ", " + result.data[i].longitude + "</p>";
        }
        jQuery("#searchresults").html(html);
    } else {
        var msg = result.message;
        if ((result.messages) && (result.messages.error)) {
            for (var j=0; j<result.messages.error.length; j++) {
                msg += "<br/>" + result.messages.error[j];
            }
        }
        jQuery("#searchresults").html(msg);
    }
}

Wir müssen diese Ajax URL aus dem Modell liefern: site/models/helloworld.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');

JLoader::register('HelloworldHelperRoute', JPATH_ROOT . '/components/com_helloworld/helpers/route.php');

/**
 * HelloWorld Model
 *
 * @since  0.0.1
 */
class HelloWorldModelHelloWorld extends JModelItem
{
	/**
	 * @var object item
	 */
	protected $item;

	/**
	 * Method to auto-populate the model state.
	 *
	 * This method should only be called once per instantiation and is designed
	 * to be called on the first call to the getState() method unless the model
	 * configuration flag to ignore the request is set.
	 *
	 * Note. Calling getState in this method will result in recursion.
	 *
	 * @return	void
	 * @since	2.5
	 */
	protected function populateState()
	{
		// Get the message id
		$jinput = JFactory::getApplication()->input;
		$id     = $jinput->get('id', 1, 'INT');
		$this->setState('message.id', $id);

		// Load the parameters.
		$this->setState('params', JFactory::getApplication()->getParams());
		parent::populateState();
	}

	/**
	 * 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);
	}

	/**
	 * Get the message
	 * @return object The message to be displayed to the user
	 */
	public function getItem()
	{
		if (!isset($this->item)) 
		{
			$id    = $this->getState('message.id');
			$db    = JFactory::getDbo();
			$query = $db->getQuery(true);
			$query->select('h.greeting, h.params, h.image as image, c.title as category, h.latitude as latitude, h.longitude as longitude')
				  ->from('#__helloworld as h')
				  ->leftJoin('#__categories as c ON h.catid=c.id')
				  ->where('h.id=' . (int)$id);
			$db->setQuery((string)$query);
		
			if ($this->item = $db->loadObject()) 
			{
				// Load the JSON string
				$params = new JRegistry;
				$params->loadString($this->item->params, 'JSON');
				$this->item->params = $params;

				// Merge global params with item params
				$params = clone $this->getState('params');
				$params->merge($this->item->params);
				$this->item->params = $params;

				// Convert the JSON-encoded image info into an array
				$image = new JRegistry;
				$image->loadString($this->item->image, 'JSON');
				$this->item->imageDetails = $image;
			}
		}
		return $this->item;
	}

	public function getMapParams()
	{
		if ($this->item) 
		{
			$url = HelloworldHelperRoute::getAjaxURL();
			$this->mapParams = array(
				'latitude' => $this->item->latitude,
				'longitude' => $this->item->longitude,
				'zoom' => 10,
				'greeting' => $this->item->greeting,
				'ajaxurl' => $url
			);
			return $this->mapParams; 
		}
		else
		{
			throw new Exception('No helloworld details available for map', 500);
		}
	}

	public function getMapSearchResults($mapbounds)
	{
		try 
		{
			$db    = JFactory::getDbo();
			$query = $db->getQuery(true);
			$query->select('h.id, h.greeting, h.latitude, h.longitude')
			   ->from('#__helloworld as h')
			   ->where('h.latitude > ' . $mapbounds['minlat'] . 
				' AND h.latitude < ' . $mapbounds['maxlat'] .
				' AND h.longitude > ' . $mapbounds['minlng'] .
				' AND h.longitude < ' . $mapbounds['maxlng']);
			$db->setQuery($query);
			$results = $db->loadObjectList(); 
		}
		catch (Exception $e)
		{
			$msg = $e->getMessage();
			JFactory::getApplication()->enqueueMessage($msg, 'error'); 
			$results = null;
		}

		for ($i = 0; $i < count($results); $i++) 
		{
			$results[$i]->url = JRoute::_('index.php?option=com_helloworld&view=helloworld&id=' . $results[$i]->id);
		}

		return $results; 
	}
}

Wir legen aber diese komplexe Funktionsweise, die Ajax URL aufzufinden, in eine separate Hilfedatei. Diese befindet sich in einem neuen Hilfeverzeichnis (in welche wir die übliche Dummy index.php legen müssen). Im Kode unten:

$sitemenu= $app->getMenu(); gibt die Menüstruktur für die gesamte Seite zurück

$thismenuitem = $sitemenu->getActive(); gibt die Menüeintragsstruktur für den Menüeintrag, auf den wir uns derzeit befinden, zurück, zum Beispiel der Menüeintrag, den der Joomla! Router auswählte, der uns zu dem Code hier führte.

$mainmenuitems = $sitemenu->getItems('menutype', 'mainmenu'); liefert die Untermenge von Menüeinträgen, die an das Hauptmenü angeheftet sind, zum Beispiel wo der menutype Eigenschaft = "mainmenu".

Befinden wir uns bereits im "Nachrichten" Menüeintrag, können wir eine Null-URL an unseren Javaskriptcode übergeben. Wenn wir in unserem Javaskriptcode eine Null-URL an den jQuery Ajax Aufruf weitergeben, wird jQuery die HTTP Anfrage einfach an die gegenwärtige URL weiterleiten.

site/helpers/route.php

<?php

defined('_JEXEC') or die;

/**
 * Helloworld Component Helper file for generating the URL Routes
 *
 */
class HelloworldHelperRoute
{
	/**
	 * When the Helloworld message is displayed then there is also shown a map with a Search Here button.
	 * This function generates the URL which the Ajax call will use to perform the search. 
	 * 
	 */
	public static function getAjaxURL()
	{
		$app  = JFactory::getApplication();
		$sitemenu= $app->getMenu();
		$thismenuitem = $sitemenu->getActive();

		// if we haven't got an active menuitem, or we're currently on the messages menuitem then just stay there
		if (!$thismenuitem || $thismenuitem->alias == "messages")
		{
			return null;
		}

		$mainmenuitems = $sitemenu->getItems('menutype', 'mainmenu');
		foreach ($mainmenuitems as $menuitem)
		{
			if ($menuitem->alias == "messages")
			{
				$itemid = $menuitem->id; 
				$url = JRoute::_("index.php?Itemid=$itemid&view=helloworld&format=json");
				return $url;
			}
		}
		return null; 
	}
}

Router

Schließlich sind wir nun in der Lage, uns unseren Routercode zu überlegen. Zuerst lasst uns ein wenig verstehen, wie Joomla!'s Routing funktioniert, indem wir einen Blick darauf werfen, wie es eine SEF URL parsen würde in der Form: http://www.domainname.net/en/menu1/submenu-a/segment1/segment2

Das Ziel des Routers ist es, diese Anfrage an die richtige Komponente weiterzuleiten, außerdem sollen definiert sein:

  • die Sprache
  • die Formatierungsoptionen, etc., die für diese Webseite gelten
  • die Parameter wie die ID, das Format (zum Beispiel html/json), Ansicht, etc.

Zunächst wird der Router den Domainnamenteil entfernen, weil dieser auf die ganze Seite verweist und beim Routing keine Rolle spielt. Also bleibt uns dieser Pfad übrig:

de/menu1/submenu-a/segment1/segment2

Jetzt wendet der Router diverse Regeln beim Versuch, die Segmente der URL zu zergliedern, an. Diese Zergliederung beginnt auf der linken Seite. Während die Segmente zugeordnet werden, baut der Router die Konfigurationsparameter auf und entfernt diejenigen bereits zugeordneten Segmente der URL, bevor die nächste Regel angewendet wird.

Falls die Webseite vielsprachig ist, wird der Router die Regel zum Zergliedern der Sprache anwenden. In diesem Fall wird er 'de' finden, die Sprache auf de-DE setzen, den Teil vom Pfad entfernen und damit

menu1/submenu-a/segment1/segment2

zurücklassen. Nun wird der Router eine Regel ausführen, um zu versuchen, einen passenden Menüeintrag zu finden. Er wird alle Menüeinträge auf der Seite erhalten und die Route eines jeden Menüeintrags mit dem Segment des Pfads vergleichen, der sich am weitesten links befindet. In diesem Beispiel könnte er zwei passende finden - einen, der auf "menu1" und einen anderen, der auf "menu1/submenu-a" zutrifft - in diesem Fall wird er den mit der längsten Übereinstimmung nehmen.

Jetzt, da er das das Menüobjekt gefunden hat, weiß er die Komponenten, die Komponentenparameter und auch neben den Formatierungs- weitere Optioonen, die mit diesem Menüobjekt verknüpft sind. Uns bleibt jetzt noch

segment1/segment2

übrig. Es ist an diesem Punkt, dass sich der Router der Komponente zuwendet, um die übrigen Segmente zu zergliedern.

Vorher wird der Router über eine Liste aller Komponenten der Webseite verfügen, und jedes Komponentenverzeichnis auf der Suche nach einer Datei namens router.php überprüft haben, welche Klassenfunktionen enthält, die die Routerschnittstelle ausführten, namentlich die Funktionen build(), parse() und preprocess(). Falls er eine fand, wird dies Klasse als Komponentenrouter gesetzt, andernfalls setzt er nur einen standardmäßig klassischen Komponentenrouter.

Um die verbleibenden Segmente zu zergliedern ruft es die Funktion parse() vom Komponentenrouter auf, und reicht ein Datenfeld der verbleibenden Segmente ein. Was es im Gegenzug will, ist eine Liste der Abfrageparameter (zum Beispiel ID=xxx, Ansicht=yyy), und diese verwendet er, um jedwede entsprechenden Parameter zu überschreiben, die gegen diesen Menüeintrag sind.

Schließlich setzt er die HTTP Anfrageparameter auf diejenigen, die er gefunden hat, sodass beim Aufruf der Komponente der Router $input->get() verwenden kann, um auf diese zugreifen zu können.

Beim Betrachten der entgegengesezten Seite, wenn wir JRoute::_ aufrufen, geben wir daran einen Abfragestring wie "id=52&view=hallo Welt" weiter. Falls nun SEF URLs auf der Seite aktiviert sind, dann ist es die Aufgabe des Skripts, die Teilbereiche der SEF URL zu erzeugen, und dies wird so durchgeführt:

  • Überprüfung, ob die Sprache multilingual ist, und falls das zutrifft, wird das entsprechende Sprachsegment hinzugefügt
  • den Pfad des Menüeintrags hinzufügen, der im Teil "Itemid=xxx" des Abfragestrings weitergegeben wurde, oder, falls keine ElementeID weitergegeben wurde, wird es das derzeitige ("aktive") Menüelement verwenden, auf dem es sich gerade befindet
  • falls immer noch Abfrageparameter existieren, welche nicht zu jenen des Menüelements passen, dann wird es die Bausteinfunktion build() des Routers ausführen, indem es ein Datenfeld jener Abfrageparameter weiterleitet und dadurch die Segmente der URL zurück erwartet, die es anhängen soll.

Die dritte Funktion preprocess() in unserem angepassten Router ist eine, die am Anfang ausgeführt wird, um es dem Programmcode zu ermöglichen, Erstdaten zu bilden, aber wir werden sie nicht verwenden.

Die Befehle build() und parse() sind so ziemlich einfach wie sie sein können: unser build() nimmt eine ID und gibt den verbunden Alias zurück aus, und unser parse() verwendet den Alias und sendet die zugehörige ID.

site/router.php

<?php

defined('_JEXEC') or die;

class HelloworldRouter implements JComponentRouterInterface
{

	public function build(&$query)
	{
		$segments = array();
		if (isset($query['id']))
		{
			$db = JFactory::getDbo();
			$qry = $db->getQuery(true);
			$qry->select('alias');
			$qry->from('#__helloworld');
			$qry->where('id = ' . $db->quote($query['id']));
			$db->setQuery($qry);
			$alias = $db->loadResult();
			$segments[] = $alias;
			unset($query['id']);
		}
		unset($query['view']);
		return $segments;
	}
  
	public function parse(&$segments)
	{
		$vars = array();
    
		$db = JFactory::getDbo();
		$qry = $db->getQuery(true);
		$qry->select('id');
		$qry->from('#__helloworld');
		$qry->where('alias = ' . $db->quote($segments[0]));
		$db->setQuery($qry);
		$id = $db->loadResult();
        
		if(!empty($id))
		{
			$vars['id'] = $id;
			$vars['view'] = 'helloworld';
		}

		return $vars;
	}
  
	public function preprocess($query)
	{
		return $query;
	}
}

Aktualisierte Sprachzeichenfolgen

admin/language/en-GB/en-GB.com_helloworld.sys.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="Hello World!"
COM_HELLOWORLD_DESCRIPTION="This is the Hello World description"
COM_HELLOWORLD_HELLOWORLD_VIEW_DEFAULT_TITLE="Hello World"
COM_HELLOWORLD_HELLOWORLD_VIEW_DEFAULT_DESC="This view displays a selected message"
COM_HELLOWORLD_ADD_VIEW_TITLE="Hello World form"
COM_HELLOWORLD_ADD_VIEW_DESC="This displays a form to allow the user to enter a new message"
COM_HELLOWORLD_CATEGORY_VIEW_TITLE="Category List"
COM_HELLOWORLD_CATEGORY_VIEW_DESC="This displays the messages in the selected category"
COM_HELLOWORLD_INSTALL_TEXT="HelloWorld Install script"
COM_HELLOWORLD_MENU="Hello World!"
COM_HELLOWORLD_POSTFLIGHT_DISCOVER_INSTALL_TEXT="HelloWorld postflight discover install script"
COM_HELLOWORLD_POSTFLIGHT_INSTALL_TEXT="HelloWorld postflight install script"
COM_HELLOWORLD_POSTFLIGHT_UNINSTALL_TEXT="HelloWorld postflight uninstall script"
COM_HELLOWORLD_POSTFLIGHT_UPDATE_TEXT="HelloWorld postflight update script"
COM_HELLOWORLD_PREFLIGHT_DISCOVER_INSTALL_TEXT="HelloWorld preflight discover install script"
COM_HELLOWORLD_PREFLIGHT_INSTALL_TEXT="HelloWorld preflight install script"
COM_HELLOWORLD_PREFLIGHT_UNINSTALL_TEXT="HelloWorld preflight uninstall script"
COM_HELLOWORLD_PREFLIGHT_UPDATE_TEXT="HelloWorld preflight update script"
COM_HELLOWORLD_UNINSTALL_TEXT="HelloWorld Uninstall script"
COM_HELLOWORLD_UPDATE_TEXT="HelloWorld Update script. HelloWorld now updated to version %s."


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

; add new message form
COM_HELLOWORLD_LEGEND_DETAILS="New Helloworld Message Details"
COM_HELLOWORLD_HELLOWORLD_CREATING="Add message"
COM_HELLOWORLD_HELLOWORLD_ERROR_UNACCEPTABLE="Sorry, you have an error"
COM_HELLOWORLD_HELLOWORLD_DETAILS="Message details"
COM_HELLOWORLD_HELLOWORLD_GREETING_LABEL="Greeting"
COM_HELLOWORLD_HELLOWORLD_GREETING_DESC="Please specify the greeting to add"
COM_HELLOWORLD_HELLOWORLD_GREETING_HINT="Any characters allowed"
COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_LABEL="Category"
COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_DESC="Please select the associated category"
COM_HELLOWORLD_HELLOWORLD_MESSAGE_LABEL="Reason"
COM_HELLOWORLD_HELLOWORLD_MESSAGE_DESC="Please say why you're adding this greeting"
COM_HELLOWORLD_HELLOWORLD_MESSAGE_HINT="No HTML tags!"
COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_LABEL="Spam protection"
COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_DESC="Prove you're a real person!"
COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL="Display category or not?"
COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC="Select if you want the category displayed too"
COM_HELLOWORLD_HELLOWORLD_IMAGE_LABEL="Image information"
COM_HELLOWORLD_HELLOWORLD_PICTURE_LABEL="Image file to upload"
COM_HELLOWORLD_HELLOWORLD_PICTURE_DESC="Select the file with the image to upload"
COM_HELLOWORLD_HELLOWORLD_CAPTION_LABEL="Caption"
COM_HELLOWORLD_HELLOWORLD_CAPTION_DESC="Text to use as a caption for the image"
COM_HELLOWORLD_HELLOWORLD_ALTTEXT_LABEL="Alt text"
COM_HELLOWORLD_HELLOWORLD_ALTTEXT_DESC="Text to display if image cannot be shown"
; save and cancel confirmation messages
COM_HELLOWORLD_ADD_SUCCESSFUL="New greeting successfully saved"
COM_HELLOWORLD_ADD_CANCELLED="New greeting cancelled ok"
; file upload error conditions
COM_HELLOWORLD_ERROR_FILEUPLOAD="PHP Error %s encountered when uploading file"
COM_HELLOWORLD_ERROR_FILETOOLARGE="Upload file exceeds max size configured in Joomla"
COM_HELLOWORLD_ERROR_BADFILENAME="Upload file has an invalid filename"
COM_HELLOWORLD_ERROR_FILE_EXISTS="Upload file already exists"
COM_HELLOWORLD_ERROR_UNABLE_TO_UPLOAD_FILE="Error creating uploaded file"
; helloworld greeting page
COM_HELLOWORLD_SEARCH_HERE_BUTTON="Search here"
; Ajax handling errors
COM_HELLOWORLD_ERROR_NO_RECORDS="Didn't get any records"
COM_HELLOWORLD_ERROR_NO_MAP_BOUNDS="Error: no map bounds"
; category view, search and ordering fields and headings
COM_HELLOWORLD_SORT_BY="Sort by ..."
COM_HELLOWORLD_GREETING_ASC="Greeting asc"
COM_HELLOWORLD_GREETING_DESC="Greeting desc"
COM_HELLOWORLD_ID_ASC="id asc"
COM_HELLOWORLD_ID_DESC="id desc"
COM_HELLOWORLD_ALIAS_ASC="alias asc"
COM_HELLOWORLD_ALIAS_DESC="alias desc"
COM_HELLOWORLD_HELLOWORLD_ALIAS_LABEL="Alias"
COM_HELLOWORLD_HELLOWORLD_FIELD_URL_LABEL="URL"

Die Komponente konfektionieren

Der Inhalt deines Codeverzeichnisses. Jeder folgende Dateilink führt dich zu dem Schritt im Tutorial, der die aktuellste Version dieser Quellcodedatei aufweist.

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.20</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>
        </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>
		</languages>
	</administration>

</extension>


Mitwirkende