Compare commits

...

31 Commits

Author SHA1 Message Date
Daniel Seifert 445a58a819 add ViewId tests 2024-05-13 16:24:34 +02:00
Daniel Seifert 957de3ac0f adjust changelog 2024-05-13 16:04:56 +02:00
Daniel Seifert 9f2a97c560 fix assignment in template 2024-05-13 15:45:25 +02:00
Daniel Seifert 76d72c8546 align test fixtures to strict types 2024-05-13 15:45:25 +02:00
Daniel Seifert 76fd879554 adjust tests 2024-05-13 15:45:25 +02:00
Daniel Seifert be873efb32 set argument types 2024-05-13 15:45:25 +02:00
Daniel Seifert 201b01db4c fix unicode char issues 2024-05-13 15:45:24 +02:00
Daniel Seifert 185caeeac3 refactor CSV format class
- add column consistency check
- can force enclosure optionally
2024-05-13 15:45:24 +02:00
Daniel Seifert 760b7b2509 assert right argument types 2024-05-13 15:45:24 +02:00
Daniel Seifert f730a380ec use current module setting service 2024-05-13 15:45:24 +02:00
Daniel Seifert 4cfcfb6d44 fix type in Twig template 2024-05-13 15:45:23 +02:00
Daniel Seifert 69b33d3538 fix tests 2024-05-13 15:45:23 +02:00
Daniel Seifert 36ee1a5957 fix tests 2024-05-13 15:45:23 +02:00
Daniel Seifert 3fd5f2dc98 fix test issues 2024-05-13 15:45:23 +02:00
Daniel Seifert bb7b9c2025 add Twig templates and translations, move Smarty templates to appropriate folder 2024-05-13 15:45:23 +02:00
Daniel Seifert 7ed3a7be0e make installable in OXID 7.1 2024-05-13 15:45:22 +02:00
Daniel Seifert baeb293048 add module dependency
available from OXID 7.1
2024-05-13 15:45:22 +02:00
Daniel Seifert 908068abfa improve code 2024-05-13 15:45:22 +02:00
Daniel Seifert 4548be2986 retrieve module settings from settings service instead from outdated configuration 2024-05-13 15:45:21 +02:00
Daniel Seifert 42fe5c8ca2 adjust translation file structure 2024-05-13 15:45:21 +02:00
Daniel Seifert 58dcf94255 adjust translation file structure 2024-05-13 15:45:21 +02:00
Daniel Seifert 34f04b8835 refactor Smarty template integration 2024-05-13 15:45:21 +02:00
Daniel Seifert b77642666b improve code for PHP 8 2024-05-13 15:44:15 +02:00
Daniel Seifert 220f8a1c7c adjust composer file 2024-05-13 15:44:15 +02:00
Daniel Seifert 4b726bb678
use vector module icon 2023-12-05 23:27:49 +01:00
Markus Gärtner c414eb3683 Tag version 2.1.1.3, make installable in OXID 6.5.3 2023-09-12 13:54:03 +02:00
Markus Gärtner 5ddd1a3923 make installable in OXID 6.5.3 2023-09-12 13:43:59 +02:00
Daniel Seifert aac67d8550
add product logo 2023-06-09 12:18:36 +02:00
Daniel Seifert 3debde96c9
apply roles and rights 2023-05-23 10:59:44 +02:00
Daniel Seifert 03f31f3e8b
adjust documentation 2023-03-30 13:33:39 +02:00
Daniel Seifert 3d83650f7c
fix JS for multiforms 2023-03-30 13:30:04 +02:00
52 changed files with 859 additions and 393 deletions

View File

@ -16,22 +16,26 @@ declare(strict_types=1);
namespace D3\DataWizard\Application\Controller\Admin;
use D3\DataWizard\Application\Model\Configuration;
use D3\DataWizard\Application\Model\Constants;
use D3\DataWizard\Application\Model\Exceptions\DataWizardException;
use D3\DataWizard\Application\Model\Exceptions\DebugException;
use D3\DataWizard\Application\Model\Exceptions\InputUnvalidException;
use D3\DataWizard\Application\Model\Exceptions\TaskException;
use D3\ModCfg\Application\Model\d3database;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Exception as DBALException;
use OxidEsales\Eshop\Application\Controller\Admin\AdminDetailsController;
use OxidEsales\Eshop\Core\Config;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Module\Facade\ModuleSettingService;
use OxidEsales\EshopCommunity\Internal\Framework\Module\Facade\ModuleSettingServiceInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class d3ActionWizard extends AdminDetailsController
{
protected $_sThisTemplate = 'd3ActionWizard.tpl';
protected $_sThisTemplate = '@'. Constants::OXID_MODULE_ID .'/admin/d3ActionWizard';
/** @var Configuration */
protected $configuration;
protected Configuration $configuration;
public function __construct()
{
@ -40,64 +44,78 @@ class d3ActionWizard extends AdminDetailsController
$this->configuration = oxNew(Configuration::class);
}
public function getViewId(): string
{
return 'd3mxDataWizard_Action';
}
public function getGroups(): array
{
return $this->configuration->getActionGroups();
}
public function getGroupTasks($group)
public function getGroupTasks($group): array
{
return $this->configuration->getActionsByGroup($group);
}
/**
* @throws DatabaseConnectionException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function runTask()
public function runTask(): void
{
try {
$this->execute();
} catch (DataWizardException|DBALException|DatabaseErrorException $e) {
} catch (DataWizardException|DBALException $e) {
Registry::getLogger()->error($e->getMessage());
Registry::getUtilsView()->addErrorToDisplay($e);
}
}
/**
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
* @throws ContainerExceptionInterface
* @throws DBALException
* @throws DebugException
* @throws InputUnvalidException
* @throws NotFoundExceptionInterface
* @throws TaskException
*/
protected function execute()
protected function execute(): void
{
$id = Registry::getRequest()->getRequestEscapedParameter('taskid');
$action = $this->configuration->getActionById($id);
[ $queryString, $parameters ] = $action->getQuery();
if ($this->d3GetConfig()->getConfigParam('d3datawizard_debug')) {
throw oxNew(
if ($this->getSettingsService()->getBoolean('d3datawizard_debug', Constants::OXID_MODULE_ID)) {
/** @var DebugException $debug */
$debug = oxNew(
DebugException::class,
d3database::getInstance()->getPreparedStatementQuery($queryString, $parameters)
);
throw $debug;
}
$action->run();
}
/**
* @return Config
* @return ModuleSettingService
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function d3GetConfig(): Config
public function getSettingsService(): ModuleSettingServiceInterface
{
return Registry::getConfig();
return ContainerFactory::getInstance()->getContainer()->get(ModuleSettingServiceInterface::class);
}
public function getUserMessages()
public function getUserMessages(): ?string
{
return null;
}
public function getHelpURL()
public function getHelpURL(): ?string
{
return null;
}

View File

@ -16,27 +16,31 @@ declare(strict_types=1);
namespace D3\DataWizard\Application\Controller\Admin;
use D3\DataWizard\Application\Model\Configuration;
use D3\DataWizard\Application\Model\Constants;
use D3\DataWizard\Application\Model\Exceptions\DataWizardException;
use D3\DataWizard\Application\Model\Exceptions\DebugException;
use D3\DataWizard\Application\Model\Exceptions\NoSuitableRendererException;
use D3\DataWizard\Application\Model\Exceptions\TaskException;
use D3\ModCfg\Application\Model\d3database;
use D3\ModCfg\Application\Model\Exception\d3_cfg_mod_exception;
use D3\ModCfg\Application\Model\Exception\d3ShopCompatibilityAdapterException;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception as DBALException;
use OxidEsales\Eshop\Application\Controller\Admin\AdminDetailsController;
use OxidEsales\Eshop\Core\Config;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Exception\StandardException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Module\Facade\ModuleSettingService;
use OxidEsales\EshopCommunity\Internal\Framework\Module\Facade\ModuleSettingServiceInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class d3ExportWizard extends AdminDetailsController
{
protected $_sThisTemplate = 'd3ExportWizard.tpl';
protected $_sThisTemplate = '@'. Constants::OXID_MODULE_ID .'/admin/d3ExportWizard';
/** @var Configuration */
protected $configuration;
protected Configuration $configuration;
public function __construct()
{
@ -45,23 +49,31 @@ class d3ExportWizard extends AdminDetailsController
$this->configuration = oxNew(Configuration::class);
}
public function getViewId(): string
{
return 'd3mxDataWizard_Export';
}
public function getGroups(): array
{
return $this->configuration->getExportGroups();
}
public function getGroupTasks($group)
public function getGroupTasks($group): array
{
return $this->configuration->getExportsByGroup($group);
}
/**
* @throws ContainerExceptionInterface
* @throws DatabaseConnectionException
* @throws Exception
* @throws NotFoundExceptionInterface
* @throws StandardException
* @throws d3ShopCompatibilityAdapterException
* @throws d3_cfg_mod_exception
*/
public function runTask()
public function runTask(): void
{
try {
$this->execute();
@ -75,20 +87,22 @@ class d3ExportWizard extends AdminDetailsController
* @throws DBALException
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
* @throws StandardException
* @throws NoSuitableRendererException
* @throws TaskException
* @throws StandardException
* @throws Exception
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws d3ShopCompatibilityAdapterException
* @throws d3_cfg_mod_exception
*/
protected function execute()
protected function execute(): void
{
$id = Registry::getRequest()->getRequestEscapedParameter('taskid');
$export = $this->configuration->getExportById($id);
[ $queryString, $parameters ] = $export->getQuery();
if ($this->d3GetConfig()->getConfigParam('d3datawizard_debug')) {
if ($this->getSettingsService()->getBoolean('d3datawizard_debug', Constants::OXID_MODULE_ID)) {
throw oxNew(
DebugException::class,
d3database::getInstance()->getPreparedStatementQuery($queryString, $parameters)
@ -99,19 +113,21 @@ class d3ExportWizard extends AdminDetailsController
}
/**
* @return Config
* @return ModuleSettingService
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function d3GetConfig(): Config
public function getSettingsService(): ModuleSettingServiceInterface
{
return Registry::getConfig();
return ContainerFactory::getInstance()->getContainer()->get(ModuleSettingServiceInterface::class);
}
public function getUserMessages()
public function getUserMessages(): ?string
{
return null;
}
public function getHelpURL()
public function getHelpURL(): ?string
{
return null;
}

View File

@ -16,18 +16,21 @@ declare(strict_types=1);
namespace D3\DataWizard\Application\Model;
use D3\DataWizard\Application\Model\Exceptions\InputUnvalidException;
use D3\DataWizard\Application\Model\Exceptions\TaskException;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception as DBALException;
use FormManager\Inputs\Checkbox;
use FormManager\Inputs\Input;
use FormManager\Inputs\Radio;
use OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface;
use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Database\ConnectionProviderInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
abstract class ActionBase implements QueryBase
{
protected $formElements = [];
protected array $formElements = [];
/**
* Ensure that the translations are equally available in the frontend and the backend
@ -39,16 +42,21 @@ abstract class ActionBase implements QueryBase
}
/**
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
* @throws ContainerExceptionInterface
* @throws DBALException
* @throws InputUnvalidException
* @throws NotFoundExceptionInterface
* @throws TaskException
*/
public function run()
public function run(): void
{
if ($this->hasFormElements()) {
/** @var Input $element */
foreach ($this->getFormElements() as $element) {
if (false === $element->isValid()) {
throw oxNew(InputUnvalidException::class, $this, $element);
/** @var InputUnvalidException $exception */
$exception = oxNew(InputUnvalidException::class, $this, $element);
throw $exception;
}
}
}
@ -59,28 +67,33 @@ abstract class ActionBase implements QueryBase
/**
* @param array $query
*
* @return int
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
* @return void
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws TaskException
* @throws DBALException
*/
public function executeAction(array $query): int
public function executeAction(array $query): void
{
[ $queryString, $parameters ] = $query;
$queryString = trim($queryString);
if (strtolower(substr($queryString, 0, 6)) === 'select') {
throw oxNew(
Exceptions\TaskException::class,
/** @var TaskException $exception */
$exception = oxNew(
TaskException::class,
$this,
Registry::getLang()->translateString('D3_DATAWIZARD_ERR_ACTIONSELECT')
);
throw $exception;
}
$affected = $this->d3GetDb()->execute($queryString, $parameters);
$affected = (int) $this->getConnection()->executeStatement($queryString, $parameters);
throw oxNew(
Exceptions\TaskException::class,
/** @var TaskException $exception */
$exception = oxNew(
TaskException::class,
$this,
sprintf(
Registry::getLang()->translateString(
@ -89,15 +102,12 @@ abstract class ActionBase implements QueryBase
$affected
)
);
throw $exception;
}
/**
* @return DatabaseInterface|null
* @throws DatabaseConnectionException
*/
public function d3GetDb(): ?DatabaseInterface
protected function getConnection(): Connection
{
return DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC);
return ContainerFactory::getInstance()->getContainer()->get(ConnectionProviderInterface::class)->get();
}
/**
@ -111,7 +121,7 @@ abstract class ActionBase implements QueryBase
/**
* @param Input $input
*/
public function registerFormElement(Input $input)
public function registerFormElement(Input $input): void
{
if ($input instanceof Radio || $input instanceof Checkbox) {
$input->setTemplate('<p class="form-check">{{ input }} {{ label }}</p>');

View File

@ -27,8 +27,8 @@ class Configuration
public const GROUP_REMARKS = 'D3_DATAWIZARD_GROUP_REMARKS';
public const GROUP_CMS = 'D3_DATAWIZARD_GROUP_CMS';
protected $actions = [];
protected $exports = [];
protected array $actions = [];
protected array $exports = [];
public function __construct()
{
@ -44,7 +44,7 @@ class Configuration
* @param $group
* @param ActionBase $action
*/
public function registerAction($group, ActionBase $action)
public function registerAction($group, ActionBase $action): void
{
$this->actions[$group][md5(get_class($action))] = $action;
}
@ -53,7 +53,7 @@ class Configuration
* @param $group
* @param ExportBase $export
*/
public function registerExport($group, ExportBase $export)
public function registerExport($group, ExportBase $export): void
{
$this->exports[$group][md5(get_class($export))] = $export;
}
@ -93,9 +93,9 @@ class Configuration
/**
* @param $group
*
* @return mixed
* @return array
*/
public function getActionsByGroup($group)
public function getActionsByGroup($group): array
{
return $this->actions[$group];
}
@ -103,9 +103,9 @@ class Configuration
/**
* @param $group
*
* @return mixed
* @return array
*/
public function getExportsByGroup($group)
public function getExportsByGroup($group): array
{
return $this->exports[$group];
}
@ -147,7 +147,7 @@ class Configuration
{
$allActions = $this->getAllActions();
if (false == $allActions[$id]) {
if ( ! $allActions[ $id ] ) {
throw oxNew(DataWizardException::class, 'no action with id '.$id);
}
@ -163,7 +163,7 @@ class Configuration
{
$allExports = $this->getAllExports();
if (false == $allExports[$id]) {
if ( ! $allExports[ $id ] ) {
throw oxNew(DataWizardException::class, 'no export with id '.$id);
}

View File

@ -0,0 +1,21 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <support@shopmodule.com>
* @link https://www.oxidmodule.com
*/
declare(strict_types=1);
namespace D3\DataWizard\Application\Model;
class Constants
{
public const OXID_MODULE_ID = 'd3datawizard';
}

View File

@ -21,8 +21,7 @@ use FormManager\Inputs\Input;
class InputUnvalidException extends DataWizardException
{
/** @var QueryBase */
public $task;
public QueryBase $task;
/**
* InputUnvalidException constructor.

View File

@ -20,8 +20,7 @@ use Exception;
class TaskException extends DataWizardException
{
/** @var QueryBase */
public $task;
public QueryBase $task;
public function __construct(QueryBase $task, $sMessage = "not set", $iCode = 0, Exception $previous = null)
{

View File

@ -15,26 +15,33 @@ declare(strict_types=1);
namespace D3\DataWizard\Application\Model;
use Assert\Assert;
use D3\DataWizard\Application\Model\Exceptions\ExportFileException;
use D3\DataWizard\Application\Model\Exceptions\InputUnvalidException;
use D3\DataWizard\Application\Model\Exceptions\NoSuitableRendererException;
use D3\DataWizard\Application\Model\ExportRenderer\RendererBridge;
use D3\DataWizard\Application\Model\ExportRenderer\RendererInterface;
use D3\ModCfg\Application\Model\d3filesystem;
use D3\ModCfg\Application\Model\Exception\d3_cfg_mod_exception;
use D3\ModCfg\Application\Model\Exception\d3ShopCompatibilityAdapterException;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Exception as DBALException;
use FormManager\Inputs\Checkbox;
use FormManager\Inputs\Input;
use FormManager\Inputs\Radio;
use OxidEsales\Eshop\Core\Database\Adapter\DatabaseInterface;
use OxidEsales\Eshop\Core\DatabaseProvider;
use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Exception\StandardException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Database\ConnectionProviderInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
abstract class ExportBase implements QueryBase
{
protected $formElements = [];
protected array $formElements = [];
/**
* Ensure that the translations are equally available in the frontend and the backend
@ -47,17 +54,19 @@ abstract class ExportBase implements QueryBase
/**
* @param string $format
* @param $path
* @param null $path
*
* @return string
* @throws ContainerExceptionInterface
* @throws DBALException
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
* @throws Exceptions\NoSuitableRendererException
* @throws Exceptions\TaskException
* @throws Exception
* @throws NoSuitableRendererException
* @throws NotFoundExceptionInterface
* @throws StandardException
* @throws d3ShopCompatibilityAdapterException
* @throws d3_cfg_mod_exception
* @return string
*/
public function run(string $format = RendererBridge::FORMAT_CSV, $path = null): string
{
@ -82,31 +91,22 @@ abstract class ExportBase implements QueryBase
}
/**
* @param $format
*
* @return ExportRenderer\RendererInterface
* @throws Exceptions\NoSuitableRendererException
* @throws NoSuitableRendererException
*/
public function getRenderer($format): ExportRenderer\RendererInterface
public function getRenderer(string $format): RendererInterface
{
return $this->getRendererBridge()->getRenderer($format);
}
/**
* @return RendererBridge
*/
public function getRendererBridge(): RendererBridge
{
return oxNew(RendererBridge::class);
}
/**
* @param $format
*
* @return string
* @throws Exceptions\NoSuitableRendererException
* @throws NoSuitableRendererException
*/
public function getFileExtension($format): string
public function getFileExtension(string $format): string
{
return $this->getRenderer($format)->getFileExtension();
}
@ -148,13 +148,16 @@ abstract class ExportBase implements QueryBase
* @param array $query
*
* @return array
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
* @throws DBALException
* @throws Exception
*/
public function getExportData(array $query): array
{
[ $queryString, $parameters ] = $query;
Assert::that($queryString)->string();
Assert::that($parameters)->isArray();
$queryString = trim($queryString);
if (strtolower(substr($queryString, 0, 6)) !== 'select') {
@ -165,7 +168,7 @@ abstract class ExportBase implements QueryBase
);
}
$rows = $this->d3GetDb()->getAll($queryString, $parameters);
$rows = $this->getConnection()->executeQuery($queryString, $parameters)->fetchAllAssociative();
if (count($rows) <= 0) {
throw oxNew(
@ -180,10 +183,12 @@ abstract class ExportBase implements QueryBase
return [ $rows, $fieldNames ];
}
/**
* @param Input $input
*/
public function registerFormElement(Input $input)
protected function getConnection(): Connection
{
return ContainerFactory::getInstance()->getContainer()->get(ConnectionProviderInterface::class)->get();
}
public function registerFormElement(Input $input): void
{
if ($input instanceof Radio || $input instanceof Checkbox) {
$input->setTemplate('<p class="form-check">{{ input }} {{ label }}</p>');
@ -213,12 +218,16 @@ abstract class ExportBase implements QueryBase
/**
* @param string $format
* @param $path
* @param $path
*
* @return string
* @throws ContainerExceptionInterface
* @throws DBALException
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
* @throws Exceptions\NoSuitableRendererException
* @throws Exception
* @throws NoSuitableRendererException
* @throws NotFoundExceptionInterface
* @throws StandardException
* @throws d3ShopCompatibilityAdapterException
* @throws d3_cfg_mod_exception
@ -227,13 +236,12 @@ abstract class ExportBase implements QueryBase
{
$content = $this->getContent($format);
/** @var $oFS d3filesystem */
$oFS = $this->getFileSystem();
if (is_null($path)) {
$oFS->startDirectDownload($oFS->filterFilename($this->getExportFileName($format)), $content);
} else {
$filePath = $oFS->trailingslashit($path) . $oFS->filterFilename($this->getExportFileName($format));
if (false === $oFS->createFile($filePath, $content, true)) {
if (false === $oFS->createFile($filePath, $content)) {
throw oxNew(ExportFileException::class, $filePath);
}
return $filePath;
@ -242,19 +250,7 @@ abstract class ExportBase implements QueryBase
return '';
}
/**
* @return DatabaseInterface|null
* @throws DatabaseConnectionException
*/
protected function d3GetDb(): ?DatabaseInterface
{
return DatabaseProvider::getDb(DatabaseProvider::FETCH_MODE_ASSOC);
}
/**
* @return d3filesystem|mixed
*/
protected function getFileSystem()
protected function getFileSystem(): d3filesystem
{
return oxNew(d3filesystem::class);
}
@ -263,16 +259,16 @@ abstract class ExportBase implements QueryBase
* @param string $format
*
* @return string
* @throws DatabaseConnectionException
* @throws DatabaseErrorException
* @throws Exceptions\NoSuitableRendererException
* @throws ContainerExceptionInterface
* @throws DBALException
* @throws Exception
* @throws NoSuitableRendererException
* @throws NotFoundExceptionInterface
*/
public function getContent(string $format): string
{
[ $rows, $fieldNames ] = $this->getExportData($this->getQuery());
$content = $this->renderContent($rows, $fieldNames, $format);
return $content;
return $this->renderContent($rows, $fieldNames, $format);
}
}

View File

@ -16,7 +16,7 @@ declare(strict_types=1);
namespace D3\DataWizard\Application\Model\ExportRenderer;
use D3\DataWizard\Application\Model\Exceptions\RenderException;
use League\Csv\EncloseField;
use League\Csv\ColumnConsistency;
use League\Csv\Exception;
use League\Csv\Writer;
use OxidEsales\Eshop\Core\Config;
@ -24,18 +24,22 @@ use OxidEsales\Eshop\Core\Registry;
class Csv implements RendererInterface
{
public function __construct(protected bool $forceEnclose = false)
{}
/**
* @param $rows
* @param $fieldNames
* @param iterable $rows
* @param iterable $fieldNames
*
* @return string
* @throws RenderException
*/
public function getContent($rows, $fieldNames): string
public function getContent(iterable $rows, iterable $fieldNames): string
{
try {
$csv = $this->getCsv();
$csv->insertOne($fieldNames);
$this->forceEnclose ? $csv->forceEnclosure() : $csv->relaxEnclosure();
$csv->insertOne((array) $fieldNames);
$csv->insertAll($rows);
return (string) $csv;
} catch (Exception $e) {
@ -58,20 +62,14 @@ class Csv implements RendererInterface
{
$csv = Writer::createFromString();
EncloseField::addTo($csv, "\t\x1f");
$sEncloser = $this->d3GetConfig()->getConfigParam('sGiCsvFieldEncloser');
if (false == $sEncloser) {
$sEncloser = '"';
}
$sEncloser = $this->d3GetConfig()->getConfigParam('sGiCsvFieldEncloser') ?? '"';
$csv->setEnclosure($sEncloser);
$sDelimiter = $this->d3GetConfig()->getConfigParam('sCSVSign');
if (false == $sDelimiter) {
$sDelimiter = ';';
}
$sDelimiter = $this->d3GetConfig()->getConfigParam('sCSVSign') ?? ';';
$csv->setDelimiter($sDelimiter);
$csv->addValidator(new ColumnConsistency(), 'columns_consistency');
return $csv;
}

View File

@ -21,18 +21,17 @@ use JsonException;
class Json implements RendererInterface
{
/**
* @param $rows
* @param $fieldNames
* @param iterable $rows
* @param iterable $fieldNames
*
* @return string
* @throws RenderException
*/
public function getContent($rows, $fieldNames): string
public function getContent( iterable $rows, iterable $fieldNames): string
{
try {
$flags = JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR;
$json = json_encode($rows, $flags);
return $json;
$flags = JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
return json_encode($rows, $flags);
} catch (JsonException $e) {
/** @var RenderException $newException */
$newException = oxNew(RenderException::class, $e->getMessage(), $e->getCode(), $e);

View File

@ -20,12 +20,12 @@ use MathieuViossat\Util\ArrayToTextTable;
class Pretty implements RendererInterface
{
/**
* @param $rows
* @param $fieldNames
* @param iterable $rows
* @param iterable $fieldNames
*
* @return string
*/
public function getContent($rows, $fieldNames): string
public function getContent(iterable $rows, iterable $fieldNames): string
{
$renderer = $this->getArrayToTextTableInstance($rows);
return $renderer->getTable();

View File

@ -45,7 +45,7 @@ class RendererBridge
/**
* @param RendererInterface $instance
*/
protected function translateRendererId(RendererInterface &$instance)
protected function translateRendererId(RendererInterface &$instance): void
{
$instance = $instance->getTitleTranslationId();
}
@ -60,8 +60,8 @@ class RendererBridge
{
$format = strtolower($format);
$rendererList = array_change_key_case($this->getRendererList(), CASE_LOWER);
$rendererListTypes = array_keys(array_change_key_case($rendererList, CASE_LOWER));
$rendererList = array_change_key_case($this->getRendererList());
$rendererListTypes = array_keys(array_change_key_case($rendererList));
if (in_array($format, $rendererListTypes, true)) {
return $rendererList[$format];

View File

@ -18,12 +18,12 @@ namespace D3\DataWizard\Application\Model\ExportRenderer;
interface RendererInterface
{
/**
* @param $rows
* @param $fieldNames
* @param iterable $rows
* @param iterable $fieldNames
*
* @return string
*/
public function getContent($rows, $fieldNames): string;
public function getContent(iterable $rows, iterable $fieldNames): string;
/**
* @return string

View File

@ -0,0 +1,25 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
// @codeCoverageIgnoreStart
declare(strict_types=1);
$sLangName = 'Deutsch';
// -------------------------------
// RESOURCE IDENTITFIER = STRING
// -------------------------------
$aLang = include __DIR__."/../../de/d3DataWizard_translations.php";
// @codeCoverageIgnoreEnd

View File

@ -0,0 +1,25 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
// @codeCoverageIgnoreStart
declare(strict_types=1);
$sLangName = 'English';
// -------------------------------
// RESOURCE IDENTITFIER = STRING
// -------------------------------
$aLang = include __DIR__."/../../en/d3DataWizard_translations.php";
// @codeCoverageIgnoreEnd

View File

@ -0,0 +1,25 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
// @codeCoverageIgnoreStart
declare(strict_types=1);
$sLangName = 'Deutsch';
// -------------------------------
// RESOURCE IDENTITFIER = STRING
// -------------------------------
$aLang = include __DIR__."/../../de/d3DataWizard_translations.php";
// @codeCoverageIgnoreEnd

View File

@ -0,0 +1,25 @@
<?php
/**
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* https://www.d3data.de
*
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
* @author D3 Data Development - Daniel Seifert <info@shopmodule.com>
* @link https://www.oxidmodule.com
*/
// @codeCoverageIgnoreStart
declare(strict_types=1);
$sLangName = 'English';
// -------------------------------
// RESOURCE IDENTITFIER = STRING
// -------------------------------
$aLang = include __DIR__."/../../en/d3DataWizard_translations.php";
// @codeCoverageIgnoreEnd

View File

@ -14,12 +14,7 @@
// @codeCoverageIgnoreStart
declare(strict_types=1);
$sLangName = "Deutsch";
// -------------------------------
// RESOURCE IDENTITFIER = STRING
// -------------------------------
$aLang = [
return [
//Navigation
'charset' => 'UTF-8',
'd3mxDataWizard' => '<i class="fas fa-fw fa-hat-wizard"></i> Data Wizard',

View File

@ -14,11 +14,7 @@
// @codeCoverageIgnoreStart
declare(strict_types=1);
$sLangName = "English";
// -------------------------------
// RESOURCE IDENTITFIER = STRING
// -------------------------------
$aLang = [
return [
//Navigation
'charset' => 'UTF-8',

View File

@ -4,9 +4,25 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased](https://git.d3data.de/D3Public/DataWizard/compare/2.1.1.1...rel_2.x)
## [unreleased](https://git.d3data.de/D3Public/DataWizard/compare/3.0.0.0...rel_3.x) - 2023-09-12
## [2.1.1.0](https://git.d3data.de/D3Public/DataWizard/compare/2.1.1.0...2.1.1.1) - 2023-03-22
## [3.0.0.0](https://git.d3data.de/D3Public/DataWizard/compare/2.1.1.3...3.0.0.0) - 2024-05-13
### Added
- installable in OXID 7.0 + 7.1
- Twig support
### Removed
- support for OXID < 7
## [2.1.1.3](https://git.d3data.de/D3Public/DataWizard/compare/2.1.1.2...2.1.1.3) - 2023-09-12
### Added
- apply roles and rights
## [2.1.1.2](https://git.d3data.de/D3Public/DataWizard/compare/2.1.1.1...2.1.1.2) - 2023-03-30
### Fixed
- fix JavaScript for multiple forms
## [2.1.1.1](https://git.d3data.de/D3Public/DataWizard/compare/2.1.1.0...2.1.1.1) - 2023-03-22
### Fixed
- wrong cascaded HTML elements

View File

@ -72,7 +72,7 @@ If you have a suggestion that would make this better, please fork the repo and c
- Push to the Branch (git push origin feature/AmazingFeature)
- Open a Pull Request
## Licence
## License
(status: 2021-05-06)
Distributed under the GPLv3 license.

View File

@ -25,21 +25,17 @@
"GPL-3.0-or-later"
],
"require": {
"php": ">=7.3",
"oxid-esales/oxideshop-ce": "6.8 - 6.13",
"php": "^8.0",
"oxid-esales/oxideshop-ce": "7.0 - 7.1",
"league/csv": "^9.0",
"mathieuviossat/arraytotexttable": "^1.0",
"form-manager/form-manager": "^6.1",
"d3/modcfg": "^6.0"
"d3/modcfg": "^7.0",
"beberlei/assert": "^3.3.2"
},
"extra": {
"oxideshop": {
"blacklist-filter": [
"*.md",
"composer.json"
],
"target-directory": "d3/datawizard"
}
"require-dev": {
"d3/testingtools": "^1.2.0.0",
"phpunit/phpunit": "^9.1.1"
},
"suggest": {
"d3/datawizardtasks": "useful example tasks for Data Wizard",
@ -48,7 +44,11 @@
},
"autoload": {
"psr-4": {
"D3\\DataWizard\\": "../../../source/modules/d3/datawizard"
"D3\\DataWizard\\": "./"
}
},
"scripts": {
"phpunit": "XDEBUG_MODE=coverage ./vendor/bin/phpunit --config=vendor/d3/datawizard/ --no-coverage",
"phpunit-coverage": "XDEBUG_MODE=coverage ./vendor/bin/phpunit --config=vendor/d3/datawizard/ --coverage-html=vendor/d3/datawizard/tests/result/coverage"
}
}

2
dependencies.yaml Normal file
View File

@ -0,0 +1,2 @@
modules:
- d3modcfg_lib

View File

@ -13,12 +13,11 @@
declare(strict_types=1);
/**
* Metadata version
*/
use D3\DataWizard\Application\Model\Constants;
$sMetadataVersion = '2.1';
$sModuleId = 'd3datawizard';
$sModuleId = Constants::OXID_MODULE_ID;
$logo = '<img src="https://logos.oxidmodule.com/d3logo.svg" alt="(D3)" style="height:1em;width:1em">';
/**
@ -31,8 +30,8 @@ $aModule = [
'de' => '',
'en' => '',
],
'thumbnail' => '',
'version' => '2.1.1.1',
'thumbnail' => 'picture.svg',
'version' => '3.0.0.0',
'author' => 'D&sup3; Data Development (Inh.: Thomas Dartsch)',
'email' => 'support@shopmodule.com',
'url' => 'https://www.oxidmodule.com/',
@ -43,11 +42,11 @@ $aModule = [
'extend' => [],
'events' => [],
'templates' => [
'd3ExportWizard.tpl' => 'd3/datawizard/Application/views/admin/tpl/d3ExportWizard.tpl',
'd3ActionWizard.tpl' => 'd3/datawizard/Application/views/admin/tpl/d3ActionWizard.tpl',
'd3Wizards.tpl' => 'd3/datawizard/Application/views/admin/tpl/inc/Wizards.tpl',
'd3ExportSubmit.tpl' => 'd3/datawizard/Application/views/admin/tpl/inc/exportSubmit.tpl',
'd3ActionSubmit.tpl' => 'd3/datawizard/Application/views/admin/tpl/inc/actionSubmit.tpl',
'@' . Constants::OXID_MODULE_ID . '/admin/d3ExportWizard.tpl' => 'views/smarty/admin/d3ExportWizard.tpl',
'@' . Constants::OXID_MODULE_ID . '/admin/d3ActionWizard.tpl' => 'views/smarty/admin/d3ActionWizard.tpl',
'@' . Constants::OXID_MODULE_ID . '/admin/inc/d3Wizards.tpl' => 'views/smarty/admin/inc/Wizards.tpl',
'@' . Constants::OXID_MODULE_ID . '/admin/inc/d3ExportSubmit.tpl' => 'views/smarty/admin/inc/exportSubmit.tpl',
'@' . Constants::OXID_MODULE_ID . '/admin/inc/d3ActionSubmit.tpl' => 'views/smarty/admin/inc/actionSubmit.tpl',
],
'settings' => [
[

36
phpunit.xml Normal file
View File

@ -0,0 +1,36 @@
<?xml version="1.0"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
backupGlobals="true"
bootstrap="../../../source/bootstrap.php"
colors="false"
backupStaticAttributes="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="false"
convertWarningsToExceptions="true"
forceCoversAnnotation="false"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
beStrictAboutTestsThatDoNotTestAnything="false"
verbose="false"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<testsuites>
<testsuite name="Unit">
<directory>tests/unit</directory>
</testsuite>
</testsuites>
<coverage includeUncoveredFiles="true" processUncoveredFiles="true">
<include>
<directory suffix=".php">Application</directory>
</include>
<exclude>
<file>../.php-cs-fixer.php</file>
<file>../IntelliSenseHelper.php</file>
</exclude>
</coverage>
<php>
<const name="OXID_PHP_UNIT" value="true"/>
</php>
</phpunit>

84
picture.svg Normal file
View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="201px" height="124px" viewBox="0 0 201 124" enable-background="new 0 0 201 124" xml:space="preserve">
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="47.0591" y1="67.5117" x2="47.0591" y2="54.6143">
<stop offset="0.0056" style="stop-color:#3266A9"/>
<stop offset="1" style="stop-color:#0099FF"/>
</linearGradient>
<path fill="url(#SVGID_1_)" d="M50.282,55.502c-0.784-0.592-2.104-0.888-3.961-0.888h-1.376l-2.283,12.898h1.779
c3.76,0,6.032-2.245,6.815-6.733c0.134-0.871,0.202-1.642,0.202-2.313C51.457,57.081,51.064,56.093,50.282,55.502z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="65.9609" y1="49.104" x2="65.9609" y2="36.9434">
<stop offset="0.0056" style="stop-color:#3266A9"/>
<stop offset="1" style="stop-color:#0099FF"/>
</linearGradient>
<path fill="url(#SVGID_2_)" d="M65.72,40.482c1.074,0,1.611,0.381,1.611,1.143c0,0.701-0.321,1.201-0.962,1.5
c-0.209,0.119-0.366,0.194-0.471,0.224c-0.065,0.019-0.158,0.037-0.271,0.056c1.98,1.621,3.702,3.544,5.097,5.699
c0.117-0.321,0.21-0.658,0.277-1.013l0.09-1.008c0-1.223-0.568-2.081-1.701-2.574c0.776-0.402,1.376-0.94,1.801-1.611
c0.425-0.672,0.638-1.418,0.638-2.239c0-0.642-0.198-1.265-0.593-1.868c-0.396-0.605-0.98-1.049-1.757-1.333
c-0.433-0.193-0.876-0.328-1.332-0.402c-0.456-0.075-1.003-0.113-1.645-0.113c-0.82,0-1.663,0.124-2.529,0.37
c-0.865,0.246-1.6,0.563-2.204,0.952s-1.13,0.907-1.578,1.557c-0.036,0.052-0.066,0.109-0.101,0.163
c1.196,0.534,2.341,1.163,3.426,1.874C63.947,40.943,64.68,40.482,65.72,40.482z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="50.0576" y1="87.0566" x2="50.0576" y2="37.8525">
<stop offset="0.0056" style="stop-color:#3266A9"/>
<stop offset="1" style="stop-color:#0099FF"/>
</linearGradient>
<path fill="url(#SVGID_3_)" d="M70.725,49.104c-0.433,1.189-1.208,2.147-2.331,2.871c-1.425,0.918-3.182,1.377-5.271,1.377
c-1.179,0-2.175-0.176-2.988-0.525c-0.813-0.35-1.444-0.864-1.891-1.543c-0.448-0.678-0.671-1.481-0.671-2.405l0.022-0.694
l0.156-0.693h4.367l-0.028,0.179v0.179v0.246c0,1.164,0.628,1.746,1.884,1.746c0.635,0,1.201-0.217,1.696-0.649
c0.495-0.434,0.742-0.94,0.742-1.522c0-0.522-0.194-0.887-0.582-1.097c-0.329-0.208-1.007-0.313-2.036-0.313l0.47-2.754
l1.141-0.067c0.083-0.011,0.154-0.022,0.221-0.033c-0.674-0.551-1.378-1.067-2.11-1.546c-0.044,0.096-0.087,0.195-0.125,0.302
h-4.185c0.192-0.837,0.49-1.56,0.884-2.175c-3.064-1.372-6.46-2.133-10.034-2.133c-13.588,0-24.603,11.014-24.603,24.601
c0,13.59,11.015,24.604,24.603,24.604S74.66,76.043,74.66,62.453C74.66,57.532,73.214,52.949,70.725,49.104z M59.413,59.233
l-0.168,1.275c-0.538,2.953-1.511,5.404-2.921,7.35c-1.298,1.835-3.016,3.179-5.153,4.028c-2.138,0.851-4.494,1.274-7.067,1.274
H33.731l4.264-24.198h10.441c1.141,0,2.204,0.073,3.189,0.218c0.984,0.146,1.868,0.364,2.651,0.655
c1.611,0.537,2.887,1.471,3.827,2.802c0.94,1.332,1.41,2.992,1.41,4.984L59.413,59.233z"/>
</g>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="107.3027" y1="105.8555" x2="93.0727" y2="16.0106">
<stop offset="0" style="stop-color:#B2B2B2;stop-opacity:0"/>
<stop offset="0.2" style="stop-color:#B2B2B2"/>
<stop offset="0.8" style="stop-color:#B2B2B2"/>
<stop offset="1" style="stop-color:#B2B2B2;stop-opacity:0"/>
</linearGradient>
<rect x="99.875" y="14.933" fill="url(#SVGID_4_)" width="0.625" height="92"/>
<script xmlns=""></script>
<g>
<g>
<path fill="none" d="M153.855,40.748H140.76v9.354c0,1.034-0.839,1.872-1.871,1.872h-9.354v20.582
c0,1.031,0.838,1.869,1.871,1.869h22.45c0.083,0,0.159-0.014,0.239-0.023l-5.59-5.588h-13.358c-1.032,0-1.871-0.84-1.871-1.871
c0-1.033,0.839-1.871,1.871-1.871h9.617l-3.742-3.743h-5.875c-1.032,0-1.871-0.837-1.871-1.871c0-1.034,0.839-1.871,1.871-1.871
h3.215c-0.183-1.584,0.68-3.495,2.505-5.326c0.765-0.765,2.763-2.533,4.869-2.533c0.546,0,1.063,0.117,1.527,0.339
c0.346,0.133,0.668,0.338,0.945,0.615l7.52,7.517V42.618C155.728,41.585,154.889,40.748,153.855,40.748z"/>
<polygon fill="none" points="137.018,43.393 132.18,48.231 137.018,48.231 "/>
<path d="M133.275,66.941c0,1.031,0.839,1.871,1.871,1.871h13.358l-3.741-3.742h-9.617
C134.114,65.07,133.275,65.908,133.275,66.941z"/>
<path d="M133.275,59.457c0,1.033,0.839,1.871,1.871,1.871h5.875l-1.73-1.73c-0.272-0.272-0.476-0.587-0.613-0.923
c-0.169-0.339-0.272-0.705-0.316-1.089h-3.215C134.114,57.585,133.275,58.423,133.275,59.457z"/>
<path d="M153.855,74.424h-22.45c-1.033,0-1.871-0.838-1.871-1.869V51.973h9.354c1.032,0,1.871-0.838,1.871-1.872v-9.354h13.096
c1.033,0,1.872,0.837,1.872,1.871v15.579l1.419,1.42l1.301-1.736l-0.989-2.9c-0.358-1.065-0.215-2.155,0.394-3.001
c0.401-0.559,0.971-0.953,1.617-1.147v-8.215c0-3.1-2.514-5.613-5.613-5.613h-14.967c-0.496,0-0.973,0.197-1.322,0.548
L126.34,48.779c-0.351,0.352-0.548,0.827-0.548,1.323v22.453c0,3.1,2.514,5.611,5.613,5.611h22.45
c1.139,0,2.196-0.342,3.081-0.924l-2.842-2.842C154.015,74.41,153.938,74.424,153.855,74.424z M137.018,43.393v4.838h-4.838
L137.018,43.393z"/>
</g>
<g>
<g>
<g>
<path fill="#010002" d="M159.092,62.488l3.655-0.048c0.479-0.006,1.107,0.314,1.388,0.71l2.109,2.982
c0.281,0.393,0.624,0.34,0.769-0.125l1.089-3.486c0.14-0.466,0.636-0.96,1.098-1.103l3.487-1.084
c0.465-0.142,0.52-0.488,0.126-0.768l-2.985-2.112c-0.396-0.281-0.716-0.903-0.705-1.385l0.044-3.656
c0.006-0.479-0.305-0.644-0.694-0.352l-2.929,2.186c-0.385,0.289-1.076,0.397-1.536,0.246l-3.46-1.177
c-0.459-0.153-0.709,0.093-0.555,0.549l1.179,3.46c0.15,0.455,0.043,1.149-0.24,1.539l-2.19,2.926
C158.447,62.182,158.61,62.492,159.092,62.488z"/>
<path fill="#010002" d="M175.635,82.078l0.061-0.063l-29.578-29.571l-0.033,0.032c-0.005-0.002-0.005-0.006-0.009-0.007
c-0.489-0.496-1.99,0.201-3.343,1.552c-1.348,1.353-2.045,2.848-1.553,3.343c0.003,0.005,0.009,0.005,0.01,0.006l-0.033,0.036
l29.575,29.578l0.059-0.064c0.587,0.313,1.962-0.367,3.215-1.625C175.267,84.037,175.943,82.662,175.635,82.078z
M149.582,60.874c-0.715,0.715-1.469,1.239-2.096,1.51l-4.374-4.4c0.624-0.272,1.368-0.795,2.08-1.503
c0.705-0.708,1.222-1.446,1.497-2.066l4.385,4.41C150.801,59.443,150.28,60.174,149.582,60.874z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -2,51 +2,14 @@
## Requirements
Both unit and acceptance tests require OXID Testing Library installed.
See https://github.com/OXID-eSales/testing_library.
Please install the packages listed in the composer.json in "require-dev". Unfortunately Composer does not provide an automatic installation.
missing ext-sockets
### Configuration
Please install the packages listed in the composer.json in "require-dev". Unfortunately Composer does not provide an automatic installation.
Here is an example of Testing Library configuration file `oxideshop/test_config.yml`
```
# This file is auto-generated during the composer install
mandatory_parameters:
shop_path: /var/www/oxideshop/source
shop_tests_path: /var/www/oxideshop/tests
partial_module_paths: d3/datawizard
optional_parameters:
shop_url: null
shop_serial: ''
enable_varnish: false
is_subshop: false
install_shop: false
remote_server_dir: null
shop_setup_path: null
restore_shop_after_tests_suite: false
test_database_name: null
restore_after_acceptance_tests: false
restore_after_unit_tests: false
tmp_path: /tmp/oxid_test_library/
database_restoration_class: DatabaseRestorer
activate_all_modules: false
run_tests_for_shop: false
run_tests_for_modules: true
screen_shots_path: null
screen_shots_url: null
browser_name: firefox
selenium_server_ip: 127.0.0.1
selenium_server_port: '4444'
additional_test_paths: null
```
Make sure the module has been activated with a valid license key.
Configure the error reporting to `error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);` in the `config.inc.php` due some deprecation issues in original OXID code
## Unit Tests
To execute unit tests run the following:
```
cd /var/www/oxideshop/
vendor/bin/runtests
```
commands are described in composer.json scripts section

View File

@ -36,6 +36,6 @@ class additional extends additional_abstract
}
try {
d3GetModCfgDIC()->get(additional::class);
} catch (Exception $e) {
d3GetOxidDIC()->get(additional::class);
} catch (Exception) {
}

View File

@ -1,27 +0,0 @@
<phpunit backupGlobals="true"
backupStaticAttributes="false"
beStrictAboutTestsThatDoNotTestAnything="false"
cacheTokens="true"
colors="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="false"
convertWarningsToExceptions="true"
forceCoversAnnotation="false"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
verbose="false">
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">../Application</directory>
<directory suffix=".php">../Modules</directory>
<directory suffix=".php">../public</directory>
<directory suffix=".php">../Setup</directory>
</whitelist>
</filter>
<logging>
<log type="junit" target="reports/logfile.xml"/>
</logging>
</phpunit>

View File

@ -17,16 +17,19 @@ namespace D3\DataWizard\tests\unit\Application\Controller\Admin;
use D3\DataWizard\Application\Controller\Admin\d3ActionWizard;
use D3\DataWizard\Application\Model\Configuration;
use D3\DataWizard\Application\Model\Constants;
use D3\DataWizard\Application\Model\Exceptions\DataWizardException;
use D3\DataWizard\Application\Model\Exceptions\DebugException;
use D3\DataWizard\Application\Model\ExportRenderer\RendererBridge;
use D3\DataWizard\tests\tools\d3TestAction;
use OxidEsales\Eshop\Core\Config;
use Doctrine\DBAL\Exception as DBALException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Request;
use OxidEsales\EshopCommunity\Internal\Framework\Module\Facade\ModuleSettingService;
use PHPUnit\Framework\MockObject\MockObject;
use ReflectionException;
class d3ActionWizardTest extends d3AdminControllerTest
class d3ActionWizardTest extends d3AdminController
{
/** @var d3ActionWizard */
protected $_oController;
@ -118,26 +121,21 @@ class d3ActionWizardTest extends d3AdminControllerTest
$requestMock->expects($this->any())->method('getRequestEscapedParameter')->willReturnCallback([$this, 'executePassRequestCallback']);
Registry::set(Request::class, $requestMock);
/** @var Config|MockObject $configMock */
$configMock = $this->getMockBuilder(Config::class)
->onlyMethods(['getConfigParam'])
/** @var ModuleSettingService $settingsServiceMock */
$settingsServiceMock = $this->getMockBuilder(ModuleSettingService::class)
->disableOriginalConstructor()
->onlyMethods(['getBoolean'])
->getMock();
$configMock->expects($this->atLeastOnce())->method('getConfigParam')->willReturnCallback(
function ($argName) use ($blDebug) {
switch ($argName) {
case 'd3datawizard_debug':
return $blDebug;
default:
return Registry::getConfig()->getConfigParam($argName);
}
}
);
$settingsServiceMock->expects($this->once())->method('getBoolean')->with(
$this->identicalTo('d3datawizard_debug'),
$this->identicalTo(Constants::OXID_MODULE_ID)
)->willReturn($blDebug);
/** @var d3ActionWizard|MockObject $controllerMock */
$controllerMock = $this->getMockBuilder(d3ActionWizard::class)
->onlyMethods(['d3GetConfig'])
->onlyMethods(['getSettingsService'])
->getMock();
$controllerMock->method('d3GetConfig')->willReturn($configMock);
$controllerMock->method('getSettingsService')->willReturn($settingsServiceMock);
$this->_oController = $controllerMock;
/** @var d3TestAction|MockObject $actionMock */
@ -148,7 +146,7 @@ class d3ActionWizardTest extends d3AdminControllerTest
])
->getMock();
$actionMock->expects($this->atLeastOnce())->method('getQuery')->willReturn(['SELECT 1', ['1']]);
$actionMock->expects($this->exactly((int) !$blDebug))->method('run')->willReturn(true);
$actionMock->expects($this->exactly((int) !$blDebug))->method('run');
/** @var Configuration|MockObject $configurationMock */
$configurationMock = $this->getMockBuilder(Configuration::class)
@ -170,14 +168,11 @@ class d3ActionWizardTest extends d3AdminControllerTest
public function executePassRequestCallback($varName)
{
switch ($varName) {
case 'taskid':
return 'testTaskId';
case 'format':
return RendererBridge::FORMAT_CSV;
default:
return oxNew(Request::class)->getRequestEscapedParameter($varName);
}
return match ( $varName ) {
'taskid' => 'testTaskId',
'format' => RendererBridge::FORMAT_CSV,
default => oxNew( Request::class )->getRequestEscapedParameter( $varName ),
};
}
/**
@ -190,4 +185,15 @@ class d3ActionWizardTest extends d3AdminControllerTest
'debug' => [true],
];
}
/**
* @return string[][]
*/
public function runTaskFailedDataProvider(): array
{
return [
[DataWizardException::class],
[DBALException::class],
];
}
}

View File

@ -22,17 +22,19 @@ use D3\DataWizard\Application\Model\Exceptions\DataWizardException;
use D3\DataWizard\Application\Model\Exceptions\DebugException;
use D3\DataWizard\tests\tools\d3TestAction;
use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Exception as DBALException;
use Exception;
use OxidEsales\Eshop\Core\Config;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Request;
use OxidEsales\Eshop\Core\UtilsView;
use OxidEsales\EshopCommunity\Internal\Framework\Module\Facade\ModuleSettingService;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use ReflectionException;
abstract class d3AdminControllerTest extends d3ModCfgUnitTestCase
abstract class d3AdminController extends d3ModCfgUnitTestCase
{
/** @var d3ActionWizard|d3ExportWizard */
protected $_oController;
@ -54,7 +56,7 @@ abstract class d3AdminControllerTest extends d3ModCfgUnitTestCase
*/
public function testConstructor()
{
$this->setValue($this->_oController, 'configuration', null);
$this->setValue($this->_oController, 'configuration', oxNew(Configuration::class));
$this->callMethod(
$this->_oController,
@ -70,6 +72,21 @@ abstract class d3AdminControllerTest extends d3ModCfgUnitTestCase
);
}
/**
* @test
* @return void
* @throws ReflectionException
* @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::getViewId
* @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::getViewId
*/
public function canGetViewId(): void
{
$viewId = $this->callMethod($this->_oController, 'getViewId');
$this->assertIsString($viewId);
$this->assertStringStartsWith('d3mxDataWizard', $viewId);
}
/**
* @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::runTask()
* @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::runTask()
@ -82,7 +99,7 @@ abstract class d3AdminControllerTest extends d3ModCfgUnitTestCase
$controllerMock = $this->getMockBuilder($this->testClassName)
->onlyMethods(['execute'])
->getMock();
$controllerMock->expects($this->once())->method('execute')->willReturn(true);
$controllerMock->expects($this->once())->method('execute');
$this->_oController = $controllerMock;
@ -104,9 +121,8 @@ abstract class d3AdminControllerTest extends d3ModCfgUnitTestCase
{
/** @var DataWizardException|DBALException|DatabaseErrorException|MockObject $exceptionMock */
$exceptionMock = $this->getMockBuilder($exceptionClass)
->disableOriginalConstructor()
->setConstructorArgs(['exc_msg', 20, new Exception()])
->getMock();
$this->setValue($exceptionMock, 'message', 'exc_msg');
/** @var d3ActionWizard|d3ExportWizard|MockObject $controllerMock */
$controllerMock = $this->getMockBuilder($this->testClassName)
@ -137,18 +153,6 @@ abstract class d3AdminControllerTest extends d3ModCfgUnitTestCase
);
}
/**
* @return \string[][]
*/
public function runTaskFailedDataProvider(): array
{
return [
[DataWizardException::class],
[DBALException::class],
[DatabaseErrorException::class],
];
}
/**
* @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::execute()
* @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::execute()
@ -244,18 +248,19 @@ abstract class d3AdminControllerTest extends d3ModCfgUnitTestCase
}
/**
* @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::d3GetConfig
* @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::d3GetConfig
* @test
* @return void
* @throws ReflectionException
* @covers \D3\DataWizard\Application\Controller\Admin\d3ActionWizard::getSettingsService()
* @covers \D3\DataWizard\Application\Controller\Admin\d3ExportWizard::getSettingsService()
*/
public function canGetConfig()
public function canGetSettingsService(): void
{
$this->assertInstanceOf(
Config::class,
ModuleSettingService::class,
$this->callMethod(
$this->_oController,
'd3GetConfig'
'getSettingsService'
)
);
}

View File

@ -17,17 +17,21 @@ namespace D3\DataWizard\tests\unit\Application\Controller\Admin;
use D3\DataWizard\Application\Controller\Admin\d3ExportWizard;
use D3\DataWizard\Application\Model\Configuration;
use D3\DataWizard\Application\Model\Constants;
use D3\DataWizard\Application\Model\Exceptions\DataWizardException;
use D3\DataWizard\Application\Model\Exceptions\DebugException;
use D3\DataWizard\Application\Model\ExportRenderer\RendererBridge;
use D3\DataWizard\tests\tools\d3TestAction;
use D3\DataWizard\tests\tools\d3TestExport;
use OxidEsales\Eshop\Core\Config;
use Doctrine\DBAL\Exception as DBALException;
use OxidEsales\Eshop\Core\Exception\DatabaseErrorException;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\Eshop\Core\Request;
use OxidEsales\EshopCommunity\Internal\Framework\Module\Facade\ModuleSettingService;
use PHPUnit\Framework\MockObject\MockObject;
use ReflectionException;
class d3ExportWizardTest extends d3AdminControllerTest
class d3ExportWizardTest extends d3AdminController
{
/** @var d3ExportWizard */
protected $_oController;
@ -120,26 +124,21 @@ class d3ExportWizardTest extends d3AdminControllerTest
//OnConsecutiveCalls('testTaskId', 'CSV');
Registry::set(Request::class, $requestMock);
/** @var Config|MockObject $configMock */
$configMock = $this->getMockBuilder(Config::class)
->onlyMethods(['getConfigParam'])
/** @var ModuleSettingService $settingsServiceMock */
$settingsServiceMock = $this->getMockBuilder(ModuleSettingService::class)
->disableOriginalConstructor()
->onlyMethods(['getBoolean'])
->getMock();
$configMock->expects($this->atLeastOnce())->method('getConfigParam')->willReturnCallback(
function ($argName) use ($blDebug) {
switch ($argName) {
case 'd3datawizard_debug':
return $blDebug;
default:
return Registry::getConfig()->getConfigParam($argName);
}
}
);
$settingsServiceMock->expects($this->once())->method('getBoolean')->with(
$this->identicalTo('d3datawizard_debug'),
$this->identicalTo(Constants::OXID_MODULE_ID)
)->willReturn($blDebug);
/** @var d3ExportWizard|MockObject $controllerMock */
$controllerMock = $this->getMockBuilder(d3ExportWizard::class)
->onlyMethods(['d3GetConfig'])
->onlyMethods(['getSettingsService'])
->getMock();
$controllerMock->method('d3GetConfig')->willReturn($configMock);
$controllerMock->method('getSettingsService')->willReturn($settingsServiceMock);
$this->_oController = $controllerMock;
/** @var d3TestAction|MockObject $exportMock */
@ -192,4 +191,16 @@ class d3ExportWizardTest extends d3AdminControllerTest
'debug' => [true],
];
}
/**
* @return string[][]
*/
public function runTaskFailedDataProvider(): array
{
return [
[DataWizardException::class],
[DBALException::class],
[DatabaseErrorException::class],
];
}
}

View File

@ -18,6 +18,7 @@ namespace D3\DataWizard\tests\unit\Application\Model;
use D3\DataWizard\Application\Model\Exceptions\TaskException;
use D3\DataWizard\tests\tools\d3TestAction;
use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase;
use Doctrine\DBAL\Connection;
use FormManager\Inputs\Hidden;
use FormManager\Inputs\Number;
use FormManager\Inputs\Radio;
@ -179,7 +180,7 @@ class ActionBaseTest extends d3ModCfgUnitTestCase
])
->getMock();
$modelMock->expects($this->atLeastOnce())->method('hasFormElements')->willReturn(false);
$modelMock->expects($this->atLeastOnce())->method('executeAction')->willReturn(1);
$modelMock->expects($this->atLeastOnce())->method('executeAction');
$modelMock->expects($this->atLeastOnce())->method('getQuery')->willReturn([]);
$this->_oModel = $modelMock;
@ -208,7 +209,7 @@ class ActionBaseTest extends d3ModCfgUnitTestCase
])
->getMock();
$modelMock->expects($this->atLeastOnce())->method('hasFormElements')->willReturn(true);
$modelMock->expects($this->exactly((int) !$blThrowException))->method('executeAction')->willReturn(1);
$modelMock->expects($this->exactly((int) !$blThrowException))->method('executeAction');
$modelMock->expects($this->exactly((int) !$blThrowException))->method('getQuery')->willReturn([]);
$modelMock->expects($this->atLeastOnce())->method('getFormElements')->willReturn($elements);
$this->_oModel = $modelMock;
@ -258,17 +259,18 @@ class ActionBaseTest extends d3ModCfgUnitTestCase
*/
public function canExecuteAction($query, $throwsException)
{
/** @var Database|MockObject $dbMock */
$dbMock = $this->getMockBuilder(Database::class)
->onlyMethods(['execute'])
/** @var Database|MockObject $connectionMock */
$connectionMock = $this->getMockBuilder(Connection::class)
->disableOriginalConstructor()
->onlyMethods(['executeStatement'])
->getMock();
$dbMock->expects($this->exactly((int) !$throwsException))->method('execute')->willReturn(true);
$connectionMock->expects($this->exactly((int) !$throwsException))->method('executeStatement')->willReturn(1);
/** @var d3TestAction|MockObject $modelMock */
$modelMock = $this->getMockBuilder(d3TestAction::class)
->onlyMethods(['d3GetDb'])
->onlyMethods(['getConnection'])
->getMock();
$modelMock->expects($this->exactly((int) !$throwsException))->method('d3GetDb')->willReturn($dbMock);
$modelMock->expects($this->exactly((int) !$throwsException))->method('getConnection')->willReturn($connectionMock);
$this->_oModel = $modelMock;
@ -280,9 +282,9 @@ class ActionBaseTest extends d3ModCfgUnitTestCase
);
} catch (TaskException $e) {
if ($throwsException) {
$this->assertStringContainsString('ACTIONSELECT', $e->getMessage());
$this->assertStringContainsString('keine SELECTs exportieren', $e->getMessage());
} else {
$this->assertStringContainsString('ACTIONRESULT', $e->getMessage());
$this->assertStringContainsString('1 Eintrag verändert', $e->getMessage());
}
}
}
@ -299,17 +301,17 @@ class ActionBaseTest extends d3ModCfgUnitTestCase
}
/**
* @covers \D3\DataWizard\Application\Model\ActionBase::d3GetDb
* @covers \D3\DataWizard\Application\Model\ActionBase::getConnection
* @test
* @throws ReflectionException
*/
public function canGetDb()
public function canGetConnection()
{
$this->assertInstanceOf(
Database::class,
Connection::class,
$this->callMethod(
$this->_oModel,
'd3GetDb'
'getConnection'
)
);
}

View File

@ -267,7 +267,7 @@ class ConfigurationTest extends d3ModCfgUnitTestCase
*/
public function canGetActionsByGroup()
{
$actionList = ['abc' => '123', 'def' => '456'];
$actionList = ['abc' => ['123'], 'def' => ['456']];
$this->setValue(
$this->_oModel,
@ -276,7 +276,7 @@ class ConfigurationTest extends d3ModCfgUnitTestCase
);
$this->assertSame(
'456',
['456'],
$this->callMethod(
$this->_oModel,
'getActionsByGroup',
@ -292,7 +292,7 @@ class ConfigurationTest extends d3ModCfgUnitTestCase
*/
public function canGetExportsByGroup()
{
$exportList = ['abc' => '123', 'def' => '456'];
$exportList = ['abc' => ['123'], 'def' => ['456']];
$this->setValue(
$this->_oModel,
@ -301,7 +301,7 @@ class ConfigurationTest extends d3ModCfgUnitTestCase
);
$this->assertSame(
'456',
['456'],
$this->callMethod(
$this->_oModel,
'getExportsByGroup',

View File

@ -18,6 +18,7 @@ namespace D3\DataWizard\tests\unit\Application\Model\Exceptions;
use D3\DataWizard\Application\Model\Exceptions\DebugException;
use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase;
use Exception;
use OxidEsales\Eshop\Core\Registry;
use PHPUnit\Framework\MockObject\MockObject;
use ReflectionException;
@ -51,7 +52,7 @@ class DebugExceptionTest extends d3ModCfgUnitTestCase
);
$this->assertStringContainsString(
'DEBUG',
'Debug: testMessage',
$this->callMethod(
$this->_oModel,
'getMessage'

View File

@ -18,6 +18,7 @@ namespace D3\DataWizard\tests\unit\Application\Model\Exceptions;
use D3\DataWizard\Application\Model\Exceptions\ExportFileException;
use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase;
use Exception;
use OxidEsales\Eshop\Core\Registry;
use PHPUnit\Framework\MockObject\MockObject;
use ReflectionException;
@ -51,7 +52,7 @@ class ExportFileExceptionTest extends d3ModCfgUnitTestCase
);
$this->assertStringContainsString(
'EXPORTFILEERROR',
'kann nicht angelegt werden',
$this->callMethod(
$this->_oModel,
'getMessage'

View File

@ -69,7 +69,7 @@ class InputUnvalidExceptionTest extends d3ModCfgUnitTestCase
[$taskMock, $invalidField, $code, $exception]
);
$this->assertRegExp(
$this->assertMatchesRegularExpression(
'/^testTitle\s-\s*->\s.*\sless\s/m',
$this->callMethod(
$this->_oModel,

View File

@ -18,6 +18,7 @@ namespace D3\DataWizard\tests\unit\Application\Model\Exceptions;
use D3\DataWizard\Application\Model\Exceptions\NoSuitableRendererException;
use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase;
use Exception;
use OxidEsales\Eshop\Core\Registry;
use PHPUnit\Framework\MockObject\MockObject;
use ReflectionException;
@ -51,7 +52,7 @@ class NoSuitableRendererExceptionTest extends d3ModCfgUnitTestCase
);
$this->assertStringContainsString(
'NOSUITABLERENDERER',
'kein Renderer f',
$this->callMethod(
$this->_oModel,
'getMessage'

View File

@ -23,11 +23,14 @@ use D3\DataWizard\Application\Model\ExportRenderer\RendererInterface;
use D3\DataWizard\tests\tools\d3TestExport;
use D3\ModCfg\Application\Model\d3filesystem;
use D3\ModCfg\Tests\unit\d3ModCfgUnitTestCase;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\Result;
use FormManager\Inputs\Hidden;
use FormManager\Inputs\Number;
use FormManager\Inputs\Radio;
use OxidEsales\Eshop\Core\Database\Adapter\Doctrine\Database;
use OxidEsales\Eshop\Core\Exception\StandardException;
use OxidEsales\Eshop\Core\Registry;
use PHPUnit\Framework\MockObject\MockObject;
use ReflectionException;
@ -320,17 +323,17 @@ class ExportBaseTest extends d3ModCfgUnitTestCase
}
/**
* @covers \D3\DataWizard\Application\Model\ExportBase::d3GetDb
* @covers \D3\DataWizard\Application\Model\ExportBase::getConnection
* @test
* @throws ReflectionException
*/
public function canGetDb()
public function canGetConnection()
{
$this->assertInstanceOf(
Database::class,
Connection::class,
$this->callMethod(
$this->_oModel,
'd3GetDb'
'getConnection'
)
);
}
@ -524,7 +527,7 @@ class ExportBaseTest extends d3ModCfgUnitTestCase
$this->_oModel = $modelMock;
$this->assertRegExp(
$this->assertMatchesRegularExpression(
'/^base_(\d{4})-(\d{2})-(\d{2})_(\d{2})-(\d{2})-(\d{2})\.extension$/m',
$this->callMethod(
$this->_oModel,
@ -542,19 +545,26 @@ class ExportBaseTest extends d3ModCfgUnitTestCase
*/
public function canGetExportData($query, $throwsException, $dbResult)
{
/** @var Database|MockObject $dbMock */
$dbMock = $this->getMockBuilder(Database::class)
->onlyMethods(['getAll'])
/** @var Result|MockObject $resultMock */
$resultMock = $this->getMockBuilder(Result::class)
->onlyMethods(get_class_methods(Result::class))
->getMock();
$dbMock->expects($this->exactly((int) !$throwsException))->method('getAll')->willReturn($dbResult);
$resultMock->method('fetchAllAssociative')->willReturn($dbResult);
/** @var Database|MockObject $connectionMock */
$connectionMock = $this->getMockBuilder(Connection::class)
->disableOriginalConstructor()
->onlyMethods(['executeQuery'])
->getMock();
$connectionMock->expects($this->exactly((int) !$throwsException))->method('executeQuery')->willReturn($resultMock);
/** @var d3TestExport|MockObject $modelMock */
$modelMock = $this->getMockBuilder(d3TestExport::class)
->onlyMethods([
'd3GetDb',
'getConnection',
])
->getMock();
$modelMock->expects($this->exactly((int) !$throwsException))->method('d3GetDb')->willReturn($dbMock);
$modelMock->expects($this->exactly((int) !$throwsException))->method('getConnection')->willReturn($connectionMock);
$this->_oModel = $modelMock;
@ -562,7 +572,7 @@ class ExportBaseTest extends d3ModCfgUnitTestCase
$result = $this->callMethod(
$this->_oModel,
'getExportData',
[[$query], ['param1', 'param2']]
[[$query, ['param1', 'param2']]]
);
$this->assertSame(
@ -582,7 +592,7 @@ class ExportBaseTest extends d3ModCfgUnitTestCase
);
} catch (TaskException $e) {
if ($throwsException) {
$this->assertStringContainsString('NOEXPORTSELECT', $e->getMessage());
$this->assertStringContainsString('Export kann nicht ausgefĂĽhrt werden', $e->getMessage());
} elseif (!count($dbResult)) {
$this->assertStringContainsString('kein Inhalt', $e->getMessage());
}

View File

@ -17,6 +17,7 @@ namespace D3\DataWizard\tests\unit\Application\Model\ExportRenderer;
use D3\DataWizard\Application\Model\Exceptions\RenderException;
use D3\DataWizard\Application\Model\ExportRenderer\Csv;
use Generator;
use League\Csv\Exception;
use League\Csv\Writer;
use OxidEsales\Eshop\Core\Config;
@ -36,6 +37,34 @@ class CsvTest extends ExportRendererTest
$this->_oModel = oxNew(Csv::class);
}
/**
* @test
*
* @param bool $force
*
* @return void
* @throws ReflectionException
* @covers \D3\DataWizard\Application\Model\ExportRenderer\Csv::__construct
* @dataProvider canForceEncloseDataProvider
*/
public function canForceEnclose(bool $force): void
{
$noForce = oxNew(Csv::class, $force);
$this->assertSame(
$force,
$this->getValue(
$noForce,
'forceEnclose'
)
);
}
public function canForceEncloseDataProvider(): Generator
{
yield 'noForce' => [true];
yield 'force' => [false];
}
/**
* @covers \D3\DataWizard\Application\Model\ExportRenderer\Csv::getContent
* @test
@ -122,13 +151,10 @@ class CsvTest extends ExportRendererTest
->getMock();
$configMock->expects($this->atLeastOnce())->method('getConfigParam')->willReturnCallback(
function ($argName) {
switch ($argName) {
case 'sGiCsvFieldEncloser':
case 'sCSVSign':
return false;
default:
return Registry::getConfig()->getConfigParam($argName);
}
return match ( $argName ) {
'sGiCsvFieldEncloser', 'sCSVSign' => null,
default => Registry::getConfig()->getConfigParam( $argName ),
};
}
);

View File

@ -38,7 +38,7 @@ abstract class ExportRendererTest extends d3ModCfgUnitTestCase
*/
public function canGetFileExtension()
{
$this->assertRegExp(
$this->assertMatchesRegularExpression(
"/^[a-z0-9._-]*$/i",
$this->callMethod(
$this->_oModel,

View File

@ -55,12 +55,12 @@ class JsonTest extends ExportRendererTest
}
/**
* @return \string[][]
* @return string[][]
*/
public function canGetContentDataProvider(): array
{
return [
'valid' => [['value1', 'value2'], false],
'valid' => [['value1', "value2"], false],
'invalid' => [["text" => "\xB1\x31"], true], // malformed UTF8 chars
];
}

View File

@ -1,4 +1,4 @@
[{capture assign="d3dw_backgroundimage"}]linear-gradient(22.5deg, rgba(66, 66, 66, 0.02) 0%, rgba(66, 66, 66, 0.02) 11%,rgba(135, 135, 135, 0.02) 11%, rgba(135, 135, 135, 0.02) 24%,rgba(29, 29, 29, 0.02) 24%, rgba(29, 29, 29, 0.02) 38%,rgba(15, 15, 15, 0.02) 38%, rgba(15, 15, 15, 0.02) 50%,rgba(180, 180, 180, 0.02) 50%, rgba(180, 180, 180, 0.02) 77%,rgba(205, 205, 205, 0.02) 77%, rgba(205, 205, 205, 0.02) 100%),linear-gradient(67.5deg, rgba(10, 10, 10, 0.02) 0%, rgba(10, 10, 10, 0.02) 22%,rgba(52, 52, 52, 0.02) 22%, rgba(52, 52, 52, 0.02) 29%,rgba(203, 203, 203, 0.02) 29%, rgba(203, 203, 203, 0.02) 30%,rgba(69, 69, 69, 0.02) 30%, rgba(69, 69, 69, 0.02) 75%,rgba(231, 231, 231, 0.02) 75%, rgba(231, 231, 231, 0.02) 95%,rgba(138, 138, 138, 0.02) 95%, rgba(138, 138, 138, 0.02) 100%),linear-gradient(112.5deg, rgba(221, 221, 221, 0.02) 0%, rgba(221, 221, 221, 0.02) 17%,rgba(190, 190, 190, 0.02) 17%, rgba(190, 190, 190, 0.02) 39%,rgba(186, 186, 186, 0.02) 39%, rgba(186, 186, 186, 0.02) 66%,rgba(191, 191, 191, 0.02) 66%, rgba(191, 191, 191, 0.02) 68%,rgba(16, 16, 16, 0.02) 68%, rgba(16, 16, 16, 0.02) 70%,rgba(94, 94, 94, 0.02) 70%, rgba(94, 94, 94, 0.02) 100%),linear-gradient(90deg, #ffffff,#ffffff)[{/capture}]
[{capture assign="d3dw_noitemmessageid"}]D3_DATAWIZARD_ERR_NOACTION_INSTALLED[{/capture}]
[{include file="d3Wizards.tpl" submit="d3ActionSubmit.tpl"}]
[{include file="@d3datawizard/admin/inc/d3Wizards.tpl" submit="@d3datawizard/admin/inc/d3ActionSubmit.tpl"}]

View File

@ -1,4 +1,4 @@
[{capture assign="d3dw_backgroundimage"}]linear-gradient(339deg, rgba(47, 47, 47,0.02) 0%, rgba(47, 47, 47,0.02) 42%,transparent 42%, transparent 99%,rgba(17, 17, 17,0.02) 99%, rgba(17, 17, 17,0.02) 100%),linear-gradient(257deg, rgba(65, 65, 65,0.02) 0%, rgba(65, 65, 65,0.02) 11%,transparent 11%, transparent 92%,rgba(53, 53, 53,0.02) 92%, rgba(53, 53, 53,0.02) 100%),linear-gradient(191deg, rgba(5, 5, 5,0.02) 0%, rgba(5, 5, 5,0.02) 1%,transparent 1%, transparent 45%,rgba(19, 19, 19,0.02) 45%, rgba(19, 19, 19,0.02) 100%),linear-gradient(29deg, rgba(28, 28, 28,0.02) 0%, rgba(28, 28, 28,0.02) 33%,transparent 33%, transparent 40%,rgba(220, 220, 220,0.02) 40%, rgba(220, 220, 220,0.02) 100%),linear-gradient(90deg, rgb(255,255,255),rgb(255,255,255))[{/capture}]
[{capture assign="d3dw_noitemmessageid"}]D3_DATAWIZARD_ERR_NOEXPORT_INSTALLED[{/capture}]
[{include file="d3Wizards.tpl" submit="d3ExportSubmit.tpl"}]
[{include file="@d3datawizard/admin/inc/d3Wizards.tpl" submit="@d3datawizard/admin/inc/d3ExportSubmit.tpl"}]

View File

@ -6,6 +6,12 @@
[{oxscript include="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"}]
[{oxstyle include="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/solid.min.css"}]
[{if $readonly}]
[{assign var="readonly" value="readonly disabled"}]
[{else}]
[{assign var="readonly" value=""}]
[{/if}]
<style>
button {
margin: 1em 1em 1em 5em;
@ -41,9 +47,9 @@
}, 3000);
document.getElementById('mask').className='on';
document.getElementById('popup2').className='d3loader-2 on';
document.getElementById('taskid').value = id;
document.getElementById('format').value = format;
document.getElementById('myedit').submit();
form = document.getElementById('form_' + id);
form.format.value = format;
form.submit();
}
[{/strip}][{/capture}]
[{oxscript add=$smarty.capture.d3script}]
@ -72,12 +78,12 @@
</h5>
<div class="card-body">
<form name="myedit" id="myedit" action="[{$oViewConf->getSelfLink()}]" method="post">
<form name="myedit" id="form_[{$id}]" action="[{$oViewConf->getSelfLink()}]" method="post">
[{$oViewConf->getHiddenSid()}]
<input type="hidden" name="cl" value="[{$oViewConf->getActiveClassName()}]">
<input type="hidden" name="fnc" value="runTask">
<input type="hidden" name="taskid" id="taskid" value="">
<input type="hidden" name="format" id="format" value="CSV">
<input type="hidden" name="taskid" value="[{$id}]">
<input type="hidden" name="format" value="CSV">
[{if $item->getDescription()}]
@ -91,7 +97,7 @@
<p class="card-text" data-toggle="collapse" data-target="#collapseExample" aria-expanded="false" aria-controls="collapseExample" style="cursor: pointer">
[{$shorttext}]...
</p>
<p class="card-text collapse" id="collapseExample">
<p class="card-text collapse" id="collapseExample_[{$id}]">
...[{$description|replace:$shorttext:''}]
</p>
[{/if}]
@ -137,4 +143,4 @@
</div>
</div>
[{include file="d3_cfg_mod_inc.tpl"}]
[{include file="@d3modcfg_lib/admin/inc/inc.tpl"}]

View File

@ -1,6 +1,6 @@
[{block name="submitElements"}]
<div class="btn-group">
<button type="button" class="btn btn-primary" onclick="if (confirm('[{oxmultilang ident="D3_DATAWIZARD_ACTION_SUBMIT_CONFIRM"}]') === true) {startTask('[{$id}]')}">
<div class="btn-group" [{$readonly}]>
<button type="button" class="btn btn-primary" onclick="if (confirm('[{oxmultilang ident="D3_DATAWIZARD_ACTION_SUBMIT_CONFIRM"}]') === true) {startTask('[{$id}]')}" [{$readonly}]>
<i class="fas fa-fw fa-magic"></i>
[{oxmultilang ident=$item->getButtonText()}]
</button>

View File

@ -1,10 +1,10 @@
[{block name="submitElements"}]
<div class="btn-group">
<button type="button" class="btn btn-primary" onclick="startTask('[{$id}]', 'CSV')">
<div class="btn-group" [{$readonly}]>
<button type="button" class="btn btn-primary" onclick="startTask('[{$id}]', 'CSV')" [{$readonly}]>
<i class="fas fa-fw fa-magic"></i>
[{oxmultilang ident=$item->getButtonText()}]
</button>
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" [{$readonly}]>
<span class="sr-only">
<i class="fas fa-fw fa-magic"></i>
[{oxmultilang ident=$item->getButtonText()}]
@ -14,7 +14,7 @@
[{block name="dataWizardFormat"}]
[{assign var="rendererBridge" value=$item->getRendererBridge()}]
[{foreach from=$rendererBridge->getTranslatedRendererIdList() key="key" item="translationId"}]
<button class="dropdown-item" onclick="startTask('[{$id}]', '[{$key}]')">
<button class="dropdown-item" onclick="startTask('[{$id}]', '[{$key}]')" [{$readonly}]>
[{oxmultilang ident=$translationId}]
</button>
[{/foreach}]

View File

@ -0,0 +1,4 @@
{% set d3dw_backgroundimage %}linear-gradient(22.5deg, rgba(66, 66, 66, 0.02) 0%, rgba(66, 66, 66, 0.02) 11%,rgba(135, 135, 135, 0.02) 11%, rgba(135, 135, 135, 0.02) 24%,rgba(29, 29, 29, 0.02) 24%, rgba(29, 29, 29, 0.02) 38%,rgba(15, 15, 15, 0.02) 38%, rgba(15, 15, 15, 0.02) 50%,rgba(180, 180, 180, 0.02) 50%, rgba(180, 180, 180, 0.02) 77%,rgba(205, 205, 205, 0.02) 77%, rgba(205, 205, 205, 0.02) 100%),linear-gradient(67.5deg, rgba(10, 10, 10, 0.02) 0%, rgba(10, 10, 10, 0.02) 22%,rgba(52, 52, 52, 0.02) 22%, rgba(52, 52, 52, 0.02) 29%,rgba(203, 203, 203, 0.02) 29%, rgba(203, 203, 203, 0.02) 30%,rgba(69, 69, 69, 0.02) 30%, rgba(69, 69, 69, 0.02) 75%,rgba(231, 231, 231, 0.02) 75%, rgba(231, 231, 231, 0.02) 95%,rgba(138, 138, 138, 0.02) 95%, rgba(138, 138, 138, 0.02) 100%),linear-gradient(112.5deg, rgba(221, 221, 221, 0.02) 0%, rgba(221, 221, 221, 0.02) 17%,rgba(190, 190, 190, 0.02) 17%, rgba(190, 190, 190, 0.02) 39%,rgba(186, 186, 186, 0.02) 39%, rgba(186, 186, 186, 0.02) 66%,rgba(191, 191, 191, 0.02) 66%, rgba(191, 191, 191, 0.02) 68%,rgba(16, 16, 16, 0.02) 68%, rgba(16, 16, 16, 0.02) 70%,rgba(94, 94, 94, 0.02) 70%, rgba(94, 94, 94, 0.02) 100%),linear-gradient(90deg, #ffffff,#ffffff){% endset %}
{% set d3dw_noitemmessageid %}D3_DATAWIZARD_ERR_NOACTION_INSTALLED{% endset %}
{% include "@d3datawizard/admin/inc/Wizards.html.twig" with {submit: "@d3datawizard/admin/inc/actionSubmit.html.twig"} %}

View File

@ -0,0 +1,4 @@
{% set d3dw_backgroundimage %}linear-gradient(339deg, rgba(47, 47, 47,0.02) 0%, rgba(47, 47, 47,0.02) 42%,transparent 42%, transparent 99%,rgba(17, 17, 17,0.02) 99%, rgba(17, 17, 17,0.02) 100%),linear-gradient(257deg, rgba(65, 65, 65,0.02) 0%, rgba(65, 65, 65,0.02) 11%,transparent 11%, transparent 92%,rgba(53, 53, 53,0.02) 92%, rgba(53, 53, 53,0.02) 100%),linear-gradient(191deg, rgba(5, 5, 5,0.02) 0%, rgba(5, 5, 5,0.02) 1%,transparent 1%, transparent 45%,rgba(19, 19, 19,0.02) 45%, rgba(19, 19, 19,0.02) 100%),linear-gradient(29deg, rgba(28, 28, 28,0.02) 0%, rgba(28, 28, 28,0.02) 33%,transparent 33%, transparent 40%,rgba(220, 220, 220,0.02) 40%, rgba(220, 220, 220,0.02) 100%),linear-gradient(90deg, rgb(255,255,255),rgb(255,255,255)){% endset %}
{% set d3dw_noitemmessageid %}D3_DATAWIZARD_ERR_NOEXPORT_INSTALLED{% endset %}
{% include "@d3datawizard/admin/inc/Wizards.html.twig" with {submit: "@d3datawizard/admin/inc/exportSubmit.html.twig"} %}

View File

@ -0,0 +1,137 @@
{% include "headitem.html.twig" with {title: "GENERAL_ADMIN_TITLE"|translate} %}
{{ style({ include: "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" }) }}
{{ script({ include: "https://code.jquery.com/jquery-3.2.1.slim.min.js", dynamic: __oxid_include_dynamic }) }}
{{ script({ include: "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js", dynamic: __oxid_include_dynamic }) }}
{{ script({ include: "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js", dynamic: __oxid_include_dynamic }) }}
{{ style({ include: "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/solid.min.css" }) }}
<style>
button {
margin: 1em 1em 1em 5em;
}
html {
font-size: 0.8em;
}
/* Image courtesy of gradientmagic.com */
body {
background-image: {{ d3dw_backgroundimage }};
}
h4 .btn {
font-size: 1.3rem;
}
h5.card-header {
font-size: 1.1rem;
}
.formElements label {
display: inline-block;
margin: .5rem 0;
}
</style>
{% capture assign = "d3script" %}{% apply spaceless %}
function startTask(id, format = '') {
let elements = document.getElementsByClassName('errorbox');
for (var i = 0; i < elements.length; i++){
elements[i].style.display = 'none';
}
setTimeout(function(){
document.getElementById('mask').className='';
document.getElementById('popup2').className='d3loader-2';
}, 3000);
document.getElementById('mask').className='on';
document.getElementById('popup2').className='d3loader-2 on';
document.getElementById('taskid').value = id;
document.getElementById('format').value = format;
document.getElementById('myedit').submit();
}
{% endapply %}{% endcapture %}
{{ script({ add: d3script, dynamic: __oxid_include_dynamic }) }}
<form name="myedit" id="myedit" action="{{ oViewConf.getSelfLink() }}" method="post" style="padding: 0;margin: 0;height:0;">
{{ oViewConf.getHiddenSid()|raw }}
<input type="hidden" name="cl" value="{{ oViewConf.getActiveClassName() }}">
<input type="hidden" name="fnc" value="runTask">
<input type="hidden" name="taskid" id="taskid" value="">
<input type="hidden" name="format" id="format" value="CSV">
{% set groups = oView.getGroups() %}
{% if groups|length %}
<div id="accordion">
{% for group in oView.getGroups() %}
<div class="card mb-2">
<div class="card-header p-1" id="heading{{ group }}">
<h4 class="mb-0">
<span class="btn p-1" data-toggle="collapse" data-target="#collapse{{ group }}" aria-expanded="false" aria-controls="collapse{{ group }}">
{{ translate({ ident: group }) }}
</span>
</h4>
</div>
<div id="collapse{{ group }}" class="collapse" aria-labelledby="heading{{ group }}" data-parent="#accordion">
<div class="card-body pb-0">
<div class="row">
{% for id, item in oView.getGroupTasks(group) %}
<div class="col-sm-6 col-md-4 col-lg-3 pb-4">
<div class="card">
<h5 class="card-header">
{{ item.getTitle() }}
</h5>
<div class="card-body">
{% if item.getDescription() %}
{% set description = item.getDescription() %}
{% set sectionlength = "100" %}
{% if description|length <= sectionlength %}
<p class="card-text">{{ description }}</p>
{% else %}
{% set shorttext = description|truncate(sectionlength, '') %}
<p class="card-text" data-toggle="collapse" data-target="#collapseExample" aria-expanded="false" aria-controls="collapseExample" style="cursor: pointer">
{{ shorttext }}...
</p>
<p class="card-text collapse" id="collapseExample">
...{{ description|replace({shorttext:''}) }}
</p>
{% endif %}
{% endif %}
{% if item.hasFormElements() %}
{% for formElement in item.getFormElements() %}
{{ formElement|raw }}
{% endfor %}
{% endif %}
{% block exportSubmit %}
{% include submit %}
{% endblock %}
</div>
</div>
</div>
{% endfor %}
</div>
<div class="clear"></div>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="alert alert-primary" role="alert">
{{ translate({ ident: d3dw_noitemmessageid }) }}
</div>
{% endif %}
</form>
<div id="mask" class=""></div>
<div id="popup2" class="d3loader-2">
<div class="d3loader-spinner">
<div class="d3loader-circle-1"></div>
<div class="d3loader-circle-2"></div>
<div class="d3loader-circle-3"></div>
</div>
</div>
{% include "@d3modcfg_lib/admin/inc/inc.html.twig" %}

View File

@ -0,0 +1,8 @@
{% block submitElements %}
<div class="btn-group">
<button type="button" class="btn btn-primary" onclick="if (confirm('{{ translate({ ident: "D3_DATAWIZARD_ACTION_SUBMIT_CONFIRM" }) }}') === true) {startTask('{{ id }}')}">
<i class="fas fa-fw fa-magic"></i>
{{ translate({ ident: item.getButtonText() }) }}
</button>
</div>
{% endblock %}

View File

@ -0,0 +1,24 @@
{% block submitElements %}
<div class="btn-group">
<button type="button" class="btn btn-primary" onclick="startTask('{{ id }}', 'CSV')">
<i class="fas fa-fw fa-magic"></i>
{{ translate({ ident: item.getButtonText() }) }}
</button>
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">
<i class="fas fa-fw fa-magic"></i>
{{ translate({ ident: item.getButtonText() }) }}
</span>
</button>
<div class="dropdown-menu">
{% block dataWizardFormat %}
{% set rendererBridge = item.getRendererBridge() %}
{% for key, translationId in rendererBridge.getTranslatedRendererIdList() %}
<button class="dropdown-item" onclick="startTask('{{ id }}', '{{ key }}')">
{{ translate({ ident: translationId }) }}
</button>
{% endfor %}
{% endblock %}
</div>
</div>
{% endblock %}