Commit fda529fe authored by Hendrik Rombach's avatar Hendrik Rombach
Browse files

Merge branch 'release/2.1.0'

Showing with 371 additions and 107 deletions
+371 -107
preset: psr2
risky: false
enabled:
- align_double_arrow # Align double arrow symbols (=>) in consecutive lines
- align_equals # Align equals symbols in consecutive lines
- alpha_ordered_imports # alternatively: length_ordered_imports
- blank_line_before_return # An empty line feed should precede a return statement.
- combine_consecutive_unsets # Calling unset on multiple items should be done in one call
- concat_with_spaces # Concatenation should be used with at least one whitespace around
- function_typehint_space # Add missing space between function's argument and its typehint
- hash_to_slash_comment # Single line comments should use double slashes // and not hash #
- linebreak_after_opening_tag # Ensure there is no code on the same line as the PHP open tag
- lowercase_cast # Cast should be written in lower case
- native_function_casing # Function defined by PHP should be called using the correct casing
- no_blank_lines_after_phpdoc # There should not be blank lines between docblock and the documented element
- no_blank_lines_after_return # Removes empty lines following a return statement
- no_blank_lines_after_throw # Removes empty lines following a throw statement
- no_blank_lines_between_imports # Removes empty lines inbetween import use statements
- no_empty_comment # There should not be any empty comments
- no_empty_phpdoc # There should not be empty PHPDoc blocks
- no_extra_consecutive_blank_lines # Removes extra consecutive empty lines
- no_short_bool_cast # Short cast bool using double exclamation mark should not be used
#- no_unreachable_default_argument_value # In method arguments there must not be arguments with default values before non-default ones
- no_unused_imports # Unused use statements must be removed
- no_useless_else # There should not be useless else cases
- no_useless_return # There should not be an empty return statement at the end of a function
- no_whitespace_in_blank_line # Remove trailing whitespace at the end of blank lines
- object_operator_without_whitespace # There should not be space before or after object T_OBJECT_OPERATOR
- ordered_class_elements # Orders the elements of classes/interfaces/traits
- phpdoc_add_missing_param_annotation # phpdoc_add_missing_param_annotation
- phpdoc_align # All items of the @param, @throws, @return, @var, and @type phpdoc tags must be aligned vertically
- phpdoc_indent # Docblocks should have the same indentation as the documented subject
- phpdoc_order # Annotations in phpdocs should be ordered so that param annotations come first, then throws annotations, then return annotations
- phpdoc_scalar # Scalar types should always be written in the same form. int not integer, bool not boolean, float not real or double
- phpdoc_separation # Annotations of the same type should immediately follow each other, and annotations of different types should be separated
- phpdoc_single_line_var_spacing # Single line @var PHPDoc should have proper spacing
- phpdoc_trim # Phpdocs should start and end with content, excluding the very first and last line of the docblocks
- pre_increment # Pre incrementation/decrementation should be used if possible
- short_array_syntax # Arrays should use the short syntax
- short_scalar_cast # Cast (boolean) and (integer) should be written as (bool) and (int), (double) and (real) as (float)
- single_blank_line_before_namespace # There should be exactly one blank line before a namespace declaration
- single_quote # Convert double quotes to single quotes for simple strings
- standardize_not_equals # Replace all <> with !=
- ternary_operator_spaces # Standardize spaces around ternary operator
- trailing_comma_in_multiline_array # PHP multi-line arrays should have a trailing comma
- unary_operator_spaces # Unary operators should be placed adjacent to their operands
- whitespace_after_comma_in_array # In array declaration, there MUST be a whitespace after each comma
finder:
name:
- "*.php"
not-path:
- "Views"
<?php
/*
* @copyright Copyright (c) 2016, Net Inventors GmbH
* @category Shopware
* @author Net Inventors GmbH
*
*/
namespace NetiToolKit\CompilerPasses;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class EmotionComponentPass implements CompilerPassInterface
{
/**
* You can modify the container here before it is dumped to PHP code.
*
* @param ContainerBuilder $container
*/
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('shopware.emotion_component_installer')) {
$container->removeDefinition('neti_tool_kit.emotion_view_subscriber');
}
}
}
<?php
/**
/*
* @copyright Copyright (c) 2016, Net Inventors GmbH
* @category Shopware
* @author hrombach
* @author Net Inventors GmbH
*
*/
namespace NetiToolKit;
use NetiToolKit\CompilerPasses\EmotionComponentPass;
use Shopware\Components\Emotion\ComponentInstaller;
use Shopware\Components\Plugin;
use Shopware\Components\Plugin\Context\InstallContext;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
class NetiToolKit extends Plugin
{
}
\ No newline at end of file
/**
* @param InstallContext $context
*/
public function install(InstallContext $context)
{
$emotionInstaller = $this->container->get(
'shopware.emotion_component_installer',
ContainerInterface::NULL_ON_INVALID_REFERENCE
);
if ($emotionInstaller instanceof ComponentInstaller) {
$this->createEmotionComponent($emotionInstaller);
}
}
/**
* @param ContainerBuilder $container
*/
public function build(ContainerBuilder $container)
{
// avoid DI errors in Shopware < 5.2.10
$container->addCompilerPass(new EmotionComponentPass());
parent::build($container);
}
/**
* @param ComponentInstaller $emotionInstaller
*/
private function createEmotionComponent(ComponentInstaller $emotionInstaller)
{
$customCodeElement = $emotionInstaller->createOrUpdate(
$this->getName(),
'Custom HTML/JS',
[
'name' => 'ToolKit Custom Code',
'template' => 'neti_tool_kit_emotion_custom',
'cls' => 'emotion-tool_kit-element',
'description' => 'Custom HTML/JS Code that will be output as-is in the emotion element.',
]
);
$customCodeElement->createTextAreaField(
[
'name' => 'html_code',
'fieldLabel' => 'Code:',
'supportText' => 'Enter HTML/JS Code here, it will be not be altered in any way.',
'allowBlank' => false,
]
);
}
}
# Shopware ToolKit
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
[![Join the chat at https://gitter.im/NetInventors/sw.ext.neti_tool_kit](https://badges.gitter.im/NetInventors/sw.ext.neti_tool_kit.svg)](https://gitter.im/NetInventors/sw.ext.neti_tool_kit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/NetInventors/sw.ext.neti_tool_kit/badges/quality-score.png?b=develop)](https://scrutinizer-ci.com/g/NetInventors/sw.ext.neti_tool_kit/?branch=develop)
[![Build Status](https://scrutinizer-ci.com/g/NetInventors/sw.ext.neti_tool_kit/badges/build.png?b=develop)](https://scrutinizer-ci.com/g/NetInventors/sw.ext.neti_tool_kit/build-status/develop)
## About ToolKit
This plugin for Shopware provides some usefull functions and tools wich every developer needs sometimes.
......@@ -14,7 +14,7 @@ This plugin for Shopware provides some usefull functions and tools wich every d
## Install:
1. If you haven't already, download and install our free plugin "[NetiFoundation](http://store.shopware.com/detail/index/sArticle/162025)" from the Shopware Community Store
2. Download this plugin via [Shopware Store](http://store.shopware.com/detail/index/sArticle/163077) or clone this repository to the
*"engine/Shopware/Plugins/Core"* folder, **remember to name the plugin folder "NetiToolKit"** and install through the Plugin Manager
*"engine/Shopware/custom/plugins/"* folder, **remember to name the plugin folder "NetiToolKit"** and install through the Plugin Manager
## Configuration:
> Because some of these Features were moved from the Net Inventors Foundation Plugin the features **UserData**
......@@ -35,6 +35,12 @@ If the user is logged in you can access the user infos on every page
Here you can check globally if the user is logged in
* Accessed in Smarty via `{$sUserLoggedIn}`
### Custom HTML / JS Code Emotion component (SW >= 5.2.10)
An emotion (Shopping worlds) component that enables you to add arbitrary HTML / JS code to shop pages with no
filtering or alteration. This differs from the default custom code emotion component in that you can mix JS and HTML code.
As this code is relayed to the page with no alteration, we take no responsibility for any problems resulting from this.
Use at your own risk!
## VCS
https://github.com/NetInventors/sw.ext.neti_tool_kit/
......
......@@ -9,6 +9,7 @@
<service id="neti_tool_kit.subscriber.global_data" class="NetiToolKit\Subscriber\GlobalData">
<argument type="service" id="neti_foundation.plugin_manager_config"/>
<argument type="service" id="session"/>
<argument type="service" id="models" />
<tag name="shopware.event_subscriber"/>
</service>
......@@ -19,5 +20,12 @@
<argument type="service" id="neti_foundation.plugin_manager_config"/>
<tag name="shopware.event_subscriber"/>
</service>
<!-- Emotion event subscriber -->
<service id="neti_tool_kit.emotion_view_subscriber"
class="Shopware\Components\Emotion\EmotionComponentViewSubscriber">
<argument>%neti_tool_kit.plugin_dir%</argument>
<tag name="shopware.event_subscriber"/>
</service>
</services>
</container>
{block name="widgets_emotion_components_toolkit_custom"}
{$Data.html_code}
{/block}
<?php
/**
/*
* @copyright Copyright (c) 2016, Net Inventors GmbH
* @category Shopware
* @author hrombach
* @author Net Inventors GmbH
*
*/
namespace NetiToolKit\Struct;
......@@ -17,17 +19,17 @@ class PluginConfig extends AbstractClass
protected $listingProperties = false;
/**
* @var bool - provide $sUserLoggedIn globally
* @var bool - provide $netiUserData globally
*/
protected $globalLoginState = true;
protected $globalUserData = true;
/**
* @var bool - provide $netiUserData globally
* @var bool - provide $netiUserData with attributes globally
*/
protected $globalUserData = true;
protected $globalUserAttributeData;
/**
* @return boolean
* @return bool
*/
public function isListingProperties()
{
......@@ -35,18 +37,18 @@ class PluginConfig extends AbstractClass
}
/**
* @return boolean
* @return bool
*/
public function isGlobalLoginState()
public function isGlobalUserData()
{
return $this->globalLoginState;
return $this->globalUserData;
}
/**
* @return boolean
* @return string
*/
public function isGlobalUserData()
public function isGlobalUserAttributeData()
{
return $this->globalUserData;
return $this->globalUserAttributeData;
}
}
\ No newline at end of file
}
<?php
/**
/*
* @copyright Copyright (c) 2016, Net Inventors GmbH
* @category Shopware
* @author hrombach
* @author Net Inventors GmbH
*
*/
namespace NetiToolKit\Subscriber;
use Doctrine\ORM\AbstractQuery;
use Enlight\Event\SubscriberInterface;
use NetiFoundation\Service\PluginManager\Config;
use NetiFoundation\Service\PluginManager\ConfigInterface;
use NetiToolKit\Struct\PluginConfig;
use Shopware\Components\Model\ModelManager;
use Shopware\Models\Customer\Customer;
class GlobalData implements SubscriberInterface
{
/** @var bool */
/**
* @var bool
*/
private $userLoggedIn;
/** @var Config */
private $configService;
/**
* @var PluginConfig
*/
......@@ -29,17 +33,26 @@ class GlobalData implements SubscriberInterface
*/
private $session;
/**
* @var ModelManager
*/
private $em;
/**
* GlobalData constructor.
*
* @param Config $configService
* @param ConfigInterface $configService
* @param \Enlight_Components_Session_Namespace $session
* @param ModelManager $em
*/
public function __construct(Config $configService, \Enlight_Components_Session_Namespace $session)
{
$this->configService = $configService;
$this->session = $session;
$this->pluginConfig = $configService->getPluginConfig('NetiToolKit');
public function __construct(
ConfigInterface $configService,
\Enlight_Components_Session_Namespace $session,
ModelManager $em
) {
$this->session = $session;
$this->em = $em;
$this->pluginConfig = $configService->getPluginConfig('NetiToolKit');
}
/**
......@@ -48,8 +61,8 @@ class GlobalData implements SubscriberInterface
public static function getSubscribedEvents()
{
return [
'Enlight_Controller_Action_PostDispatch_Frontend' => 'addSmartyGlobals',
'Enlight_Controller_Action_PostDispatch_Widgets' => 'addSmartyGlobals',
'Enlight_Controller_Action_PostDispatchSecure_Frontend' => 'addSmartyGlobals',
'Enlight_Controller_Action_PostDispatchSecure_Widgets' => 'addSmartyGlobals',
];
}
......@@ -63,48 +76,91 @@ class GlobalData implements SubscriberInterface
$view = $args->getSubject()->View();
if (null === $this->userLoggedIn) {
$this->userLoggedIn = (bool)$this->session->sUserId;
$this->userLoggedIn = (bool)$this->session->get('sUserId');
}
// assign customer login state to smarty
if ($this->pluginConfig->isGlobalLoginState()) {
if ($view->hasTemplate()) {
$view->assign('sUserLoggedIn', $this->userLoggedIn);
}
}
$netiUserData = [];
// assign userData array to smarty
if ($this->pluginConfig->isGlobalUserData() && $this->userLoggedIn) {
$userData = Shopware()->Modules()->Admin()->sGetUserData();
$netiUserData = array(
'sUserID' => $userData['additional']['user']['id'],
'sUserEmail' => $userData['additional']['user']['email'],
'sUserAccountmode' => $userData['additional']['user']['accountmode'],
'sUserPaymentID' => $userData['additional']['user']['paymentID'],
'sUserFirstlogin' => $userData['additional']['user']['firstlogin'],
'sUserLastlogin' => $userData['additional']['user']['lastlogin'],
'sUserNewsletter' => (bool)$userData['additional']['user']['newsletter'],
'sUserAffiliate' => (bool)$userData['additional']['user']['affiliate'],
'sUserCustomergroup' => $userData['additional']['user']['customergroup'],
'sUserPaymentpreset' => $userData['additional']['user']['paymentpreset'],
'sUserLanguage' => $userData['additional']['user']['language'],
'sUserSubshopID' => $userData['additional']['user']['subshopID'],
'sUserPricegroupID' => $userData['additional']['user']['pricegroupID'],
'sUserInternalcomment' => $userData['additional']['user']['internalcomment'],
'sUserBillingaddressSalutation' => $userData['billingaddress']['salutation'],
'sUserBillingaddressFirstname' => $userData['billingaddress']['firstname'],
'sUserBillingaddressLastname' => $userData['billingaddress']['lastname'],
'sUserBillingaddressCustomernumber' => $userData['billingaddress']['customernumber'],
'sUserBillingaddressStreet' => $userData['billingaddress']['street'],
'sUserBillingaddressZipcode' => $userData['billingaddress']['zipcode'],
'sUserBillingaddressCity' => $userData['billingaddress']['city'],
'sUserBillingaddressPhone' => $userData['billingaddress']['phone'],
'sUserBillingaddressFax' => $userData['billingaddress']['fax'],
'sUserBillingaddressCountryID' => $userData['billingaddress']['countryID'],
'sUserBillingaddressStateID' => $userData['billingaddress']['stateID'],
'sUserBillingaddressBirthday' => $userData['billingaddress']['birthday'],
);
$view->assign('netiUserData', $netiUserData);
$this->addUserData($netiUserData);
}
if ($this->pluginConfig->isGlobalUserAttributeData() && $this->userLoggedIn) {
$this->addUserAttributes($netiUserData);
}
$view->assign('sUserLoggedIn', $this->userLoggedIn);
$view->assign('netiUserData', $netiUserData);
}
/**
* @param $netiUserData
*
* @return void
*/
private function addUserData(&$netiUserData)
{
$userData = Shopware()->Modules()->Admin()->sGetUserData();
$netiUserData = [
'sUserID' => $userData['additional']['user']['id'],
'sUserCompany' => $userData['additional']['user']['company'],
'sUserEmail' => $userData['additional']['user']['email'],
'sUserAccountmode' => $userData['additional']['user']['accountmode'],
'sUserPaymentID' => $userData['additional']['user']['paymentID'],
'sUserFirstlogin' => $userData['additional']['user']['firstlogin'],
'sUserLastlogin' => $userData['additional']['user']['lastlogin'],
'sUserNewsletter' => (bool)$userData['additional']['user']['newsletter'],
'sUserAffiliate' => (bool)$userData['additional']['user']['affiliate'],
'sUserCustomergroup' => $userData['additional']['user']['customergroup'],
'sUserPaymentpreset' => $userData['additional']['user']['paymentpreset'],
'sUserLanguage' => $userData['additional']['user']['language'],
'sUserSubshopID' => $userData['additional']['user']['subshopID'],
'sUserPricegroupID' => $userData['additional']['user']['pricegroupID'],
'sUserInternalcomment' => $userData['additional']['user']['internalcomment'],
'sUserBillingaddressSalutation' => $userData['billingaddress']['salutation'],
'sUserBillingaddressFirstname' => $userData['billingaddress']['firstname'],
'sUserBillingaddressLastname' => $userData['billingaddress']['lastname'],
'sUserBillingaddressCustomernumber' => $userData['billingaddress']['customernumber'],
'sUserBillingaddressStreet' => $userData['billingaddress']['street'],
'sUserBillingaddressZipcode' => $userData['billingaddress']['zipcode'],
'sUserBillingaddressCity' => $userData['billingaddress']['city'],
'sUserBillingaddressPhone' => $userData['billingaddress']['phone'],
'sUserBillingaddressFax' => $userData['billingaddress']['fax'],
'sUserBillingaddressCountryID' => $userData['billingaddress']['countryID'],
'sUserBillingaddressStateID' => $userData['billingaddress']['stateID'],
'sUserBillingaddressBirthday' => $userData['billingaddress']['birthday'],
];
}
/**
* @param $netiUserData
*
* @return void
*/
private function addUserAttributes(&$netiUserData)
{
$userId = $this->session->offsetGet('sUserId');
$qb = $this->em->createQueryBuilder();
$qb->from(Customer::class, 'c')
->select(['c', 'ca', 'cdb', 'cdba', 'cb', 'cba', 'cs', 'csa'])
->leftJoin('c.attribute', 'ca')
->leftJoin('c.defaultBillingAddress', 'cdb')
->leftJoin('cdb.attribute', 'cdba')
->leftJoin('c.billing', 'cb')
->leftJoin('cb.attribute', 'cba')
->leftJoin('c.shipping', 'cs')
->leftJoin('cs.attribute', 'csa')
->where($qb->expr()->eq('c.id', ':customerId'))
->setMaxResults(1);
$attributeData = array_shift($qb->getQuery()->execute(['customerId' => $userId], AbstractQuery::HYDRATE_ARRAY));
$netiUserData += [
'sUserAttribute' => $attributeData['attribute'],
'sUserShippingAttribute' => $attributeData['shipping']['attribute'],
'sUserBillingAttribute' => $attributeData['billing']['attribute'],
'sUserAddressAttribute' => $attributeData['defaultBillingAddress']['attribute'],
];
}
}
<?php
/**
/*
* @copyright Copyright (c) 2016, Net Inventors GmbH
* @category Shopware
* @author hrombach
* @author Net Inventors GmbH
*
*/
namespace NetiToolKit\Subscriber;
......@@ -69,7 +71,7 @@ class ListingProperties implements SubscriberInterface
public static function getSubscribedEvents()
{
return [
'sArticles::sGetArticlesByCategory::after' => 'afterGetArticlesByCategory'
'sArticles::sGetArticlesByCategory::after' => 'afterGetArticlesByCategory',
];
}
......@@ -78,15 +80,36 @@ class ListingProperties implements SubscriberInterface
*/
public function afterGetArticlesByCategory(\Enlight_Hook_HookArgs $args)
{
if (! $this->pluginConfig->isListingProperties()) {
if (!$this->pluginConfig->isListingProperties()) {
return;
}
$return = $args->getReturn();
$return = $args->getReturn();
$products = $this->getProductStructsFromViewArticles($return['sArticles']);
// get property set Structs
$propertySets = $this->propertyService->getList($products, $this->contextService->getShopContext());
$legacyProps = $this->convertPropertyStructs($propertySets);
// add property arrays to sArticles array
foreach ($return['sArticles'] as &$sArticle) {
$sArticle['sProperties'] = $legacyProps[$sArticle['ordernumber']];
}
unset($sArticle);
$args->setReturn($return);
}
/**
* @param array $sArticles
*
* @return BaseProduct[]
*/
private function getProductStructsFromViewArticles(array $sArticles)
{
//turn sArticles array into BaseProduct Structs
$products = [];
foreach ($return['sArticles'] as $sArticle) {
foreach ($sArticles as $sArticle) {
$products[$sArticle['ordernumber']] = new BaseProduct(
$sArticle['articleID'],
$sArticle['articleDetailsID'],
......@@ -94,21 +117,22 @@ class ListingProperties implements SubscriberInterface
);
}
// get property set Structs
$propertySets = $this->propertyService->getList($products, $this->contextService->getContext());
return $products;
}
/**
* @param $propertySets
*
* @return array
*/
private function convertPropertyStructs($propertySets)
{
// convert property set Structs to legacy Array format
$legacyProps = [];
foreach ($propertySets as $ordernumber => $propertySet) {
$legacyProps[$ordernumber] = $this->structConverter->convertPropertySetStruct($propertySet);
}
// add property arrays to sArticles array
foreach ($return['sArticles'] as &$sArticle) {
$sArticle['sProperties'] = $legacyProps[$sArticle['ordernumber']];
}
unset($sArticle);
$args->setReturn($return);
return $legacyProps;
}
}
{
"name": "netinventors/sw.ext.neti_tool_kit",
"type": "project",
"require": {
"shopware/shopware": "^5.2.6"
},
"license": "MIT",
"minimum-stability": "stable"
}
<?php
/**
/*
* @copyright Copyright (c) 2016, Net Inventors GmbH
* @category Shopware
* @author Net Inventors GmbH
*
*/
return [
'redmine' => [
'redmine' => [
'projectID' => '000000-012-447',
'contact' => 'hr@netinventors.de'
'contact' => 'hr@netinventors.de',
],
'form' => [
'form' => [
[
'boolean',
'listingProperties',
[
'de_DE' => 'Artikeleigenschaften im Listing zu Verfügung stellen',
'en_GB' => 'Add product properties to listing products'
'en_GB' => 'Add product properties to listing products',
],
[
'de_DE' => 'Fügt dem sArticle-Array die Property-Sets hinzu, wie sie auf der Detailseite verfügbar sind.',
'en_GB' => 'Adds the Property sets to the sArticle array in the frontend listing, the same way they are available on the Detail page.'
'en_GB' => 'Adds the Property sets to the sArticle array in the frontend listing, the same way they are available on the Detail page.',
],
false,
Shopware\Models\Config\Element::SCOPE_SHOP
Shopware\Models\Config\Element::SCOPE_SHOP,
],
[
'boolean',
'globalLoginState',
'globalUserData',
[
'de_DE' => '$sUserLoggedIn global bereitstellen',
'en_GB' => 'provide $sUserLoggedIn globally'
'de_DE' => '$netiUserData global bereitstellen',
'en_GB' => 'provide $netiUserData globally',
],
[],
true,
Shopware\Models\Config\Element::SCOPE_SHOP
Shopware\Models\Config\Element::SCOPE_SHOP,
],
[
'boolean',
'globalUserData',
'globalUserAttributeData',
[
'de_DE' => '$netiUserData global bereitstellen',
'en_GB' => 'provide $netiUserData globally'
'de_DE' => '$netiUserData global mit attributes bereitstellen',
'en_GB' => 'provide $netiUserData with attributes globally',
],
[],
true,
Shopware\Models\Config\Element::SCOPE_SHOP
]
]
false,
Shopware\Models\Config\Element::SCOPE_SHOP,
],
],
];
......@@ -5,11 +5,24 @@
<label lang="de">ToolKit</label>
<label lang="en">ToolKit</label>
<version>2.0.0</version>
<version>2.1.0</version>
<link>http://www.shopinventors.de</link>
<author>Net Inventors GmbH</author>
<compatibility minVersion="5.2.6"/>
<changelog version="2.1.0">
<changes lang="de"><![CDATA[
[#26336] Attribut-Daten und Firmenname zum UserData-Array hinzugefügt.
[#25802] Custom HTML/JS Code Einkaufsweltelement wurde reimplementiert.
Die Konfigurationsoption für die sUserLoggedIn-Smarty-Variable wurde aus Performance-Gründen entfernt, der Wert wird nun immer ans Template übergeben.
]]></changes>
<changes lang="en"><![CDATA[
[#26336] Added attribute data and company name to user data array.
[#25802] Reimplement custom HTML/JS Code emotion component.
The config option to disable the sUserLoggedIn smarty variable was removed for performance reasons. The variable is now always assigned.
]]></changes>
</changelog>
<changelog version="2.0.0">
<changes lang="de">Refaktorierung zur Nutzung des neuen Plugin-Systems</changes>
<changes lang="en">Refactoring for the usage of the new plugin system</changes>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment