...
 
Commits (14)
......@@ -106,6 +106,7 @@
#Rewrite der JTL-Shop-URLs
#Aendern Sie an diesen Zeilen nichts!
RewriteRule ^cli$ - [R=404,NC,L]
RewriteRule ^templates_c/filecache/.*$ - [R=403,NC,L]
RewriteRule ^((urllist|sitemap_).*\.(xml|txt)(\.gz)?)$ includes/sitemap.php?datei=$1 [L]
RewriteRule ^export/((sitemap_).*\.(xml|txt)(\.gz)?)$ $1 [L]
......
......@@ -21,6 +21,11 @@ class VueInstaller
*/
private $post;
/**
* @var bool
*/
private $cli;
/**
* @var NiceDB
*/
......@@ -44,19 +49,21 @@ class VueInstaller
/**
* Installer constructor.
*
* @param string $task
* @param string $task
* @param array|null $post
* @param bool $cli
*/
public function __construct($task, $post = null)
public function __construct($task, $post = null, $cli = false)
{
$this->task = $task;
$this->post = $post;
$this->cli = $cli;
}
/**
*
*/
public function run(): void
public function run(): ?array
{
switch ($this->task) {
case 'installedcheck':
......@@ -83,7 +90,8 @@ class VueInstaller
default:
break;
}
$this->output();
return $this->output();
}
/**
......@@ -123,10 +131,15 @@ class VueInstaller
/**
*
*/
private function output(): void
private function output(): ?array
{
echo json_encode($this->payload);
exit(0);
if (!$this->cli) {
echo json_encode($this->payload);
exit(0);
} else {
return $this->payload;
}
}
/**
......@@ -194,7 +207,15 @@ class VueInstaller
$this->payload['secretKey'] = $blowfishKey;
$this->db->query('SET FOREIGN_KEY_CHECKS=1', ReturnType::DEFAULT);
}
$this->sendResponse();
if (!$this->cli) {
$this->sendResponse();
} else {
$this->payload['error'] = !$this->responseStatus;
$this->payload['msg'] = $this->responseStatus === true && empty($this->responseMessage)
? 'Erfolgreich ausgeführt'
: $this->responseMessage;
}
return $this;
}
......@@ -243,13 +264,13 @@ define('DB_PASS','" . $credentials['pass'] . "');
define('BLOWFISH_KEY', '" . $blowfishKey . "');
//enables printing of warnings/infos/errors for the shop frontend
define('SHOP_LOG_LEVEL', 0);
define('SHOP_LOG_LEVEL', E_ALL);
//enables printing of warnings/infos/errors for the dbeS sync
define('SYNC_LOG_LEVEL', 0);
define('SYNC_LOG_LEVEL', E_ALL ^ E_NOTICE ^ E_DEPRECATED ^ E_WARNING);
//enables printing of warnings/infos/errors for the admin backend
define('ADMIN_LOG_LEVEL', 0);
define('ADMIN_LOG_LEVEL', E_ALL);
//enables printing of warnings/infos/errors for the smarty templates
define('SMARTY_LOG_LEVEL', 0);
define('SMARTY_LOG_LEVEL', E_ALL);
//excplicitly show/hide errors
ini_set('display_errors', 0);" . "\n";
$file = fopen(PFAD_ROOT . PFAD_INCLUDES . 'config.JTL-Shop.ini.php', 'w');
......
<?php
/**
* @copyright (c) JTL-Software-GmbH
* @license http://jtl-url.de/jtlshoplicense
*/
set_error_handler(function($code, $description, $file = null, $line = null, $context = null) {
if ($code === E_ERROR) {
echo $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']' . PHP_EOL;
exit;
}
});
require_once __DIR__.'/includes/globalinclude.php';
if (file_exists(__DIR__.'/install/vendor/autoload.php')) {
require_once __DIR__.'/install/vendor/autoload.php';
}
$application = new \JTL\Console\Application();
$application->initPluginCommands();
$application->run();
\ No newline at end of file
......@@ -9,6 +9,7 @@
"ext-bcmath": "*",
"ext-curl": "*",
"ext-dom": "*",
"ext-ftp": "*",
"ext-gd": "*",
"ext-iconv": "*",
"ext-intl": "*",
......@@ -16,6 +17,7 @@
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-pdo": "*",
"ext-posix": "*",
"ext-simplexml": "*",
"ext-soap": "*",
"ext-xml": "*",
......@@ -24,7 +26,7 @@
"intervention/image": "^2.4",
"ifsnop/mysqldump-php": "^2.1",
"symfony/var-dumper": "^4.2",
"jtlshop/shop4-systemcheck": "^1.1",
"jtlshop/shop4-systemcheck": "^1.2",
"jtlshop/sccbs3": "dev-master",
"jtlshop/scc": "dev-master",
"smarty/smarty": "~3.1",
......@@ -47,12 +49,13 @@
"gettext/gettext": "^4.6",
"jtl/oauth2-client": "^1.0",
"squizlabs/php_codesniffer": "^3.4",
"illuminate/container": "^5.7"
"illuminate/container": "^5.7",
"symfony/console": "^4.2",
"symfony/finder": "^4.2"
},
"require-dev": {
"phpunit/php-token-stream": "^3.0",
"nikic/php-parser": "^2.1",
"symfony/finder": "^4.0",
"phpmd/phpmd": "@stable",
"phpunit/phpunit": "^7.3",
"sensiolabs/security-checker": "^5.0",
......@@ -68,11 +71,11 @@
"type": "package",
"package": {
"name": "jtlshop/shop4-systemcheck",
"version": "1.1.2",
"version": "1.2.0",
"source": {
"url": "https://gitlab.com/jtl-software/jtl-shop/tools/systemcheck.git",
"type": "git",
"reference": "1.1.2"
"reference": "1.2.0"
}
}
},
......
This diff is collapsed.
<?php
/**
* @copyright (c) JTL-Software-GmbH
* @license http://jtl-url.de/jtlshoplicense
*/
namespace JTL\Console;
use JTL\Console\Command\Backup\DatabaseCommand;
use JTL\Console\Command\Backup\FilesCommand;
use JTL\Console\Command\Cache\DbesTmpCommand;
use JTL\Console\Command\Cache\DeleteFileCacheCommand;
use JTL\Console\Command\Cache\DeleteTemplateCacheCommand;
use JTL\Console\Command\InstallCommand;
use JTL\Console\Command\Migration\CreateCommand;
use JTL\Console\Command\Migration\MigrateCommand;
use JTL\Console\Command\Migration\StatusCommand;
use JTL\Console\Command\Plugin\CreateCommandCommand;
use JTL\Console\Command\Plugin\CreateMigrationCommand;
use JTL\Plugin\Admin\Listing;
use JTL\Plugin\Admin\ListingItem;
use JTL\Plugin\Admin\Validation\LegacyPluginValidator;
use JTL\Plugin\Admin\Validation\PluginValidator;
use JTL\Plugin\PluginLoader;
use JTL\Shop;
use JTL\XMLParser;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;
/**
* Class Application
* @property ConsoleIO io
* @property bool devMode
* @property bool isInstalled
* @package JTL\Console
*/
class Application extends BaseApplication
{
/**
* @var ConsoleIO
*/
protected $io;
/**
* @var bool
*/
protected $devMode = false;
/**
* @var bool
*/
protected $isInstalled = false;
public function __construct()
{
$this->devMode = !empty(APPLICATION_BUILD_SHA) && APPLICATION_BUILD_SHA === '#DEV#' ?? false;
$this->isInstalled = defined('DB_HOST') && Shop::Container()->getDB()->isConnected();
parent::__construct('JTL-Shop', APPLICATION_VERSION.' - '.($this->devMode ? 'develop' : 'production'));
}
public function initPluginCommands()
{
if ($this->isInstalled) {
$db = Shop::Container()->getDB();
$cache = Shop::Container()->getCache();
$parser = new XMLParser();
$validator = new LegacyPluginValidator($db, $parser);
$modernValidator = new PluginValidator($db, $parser);
$listing = new Listing($db, $cache, $validator, $modernValidator);
$installed = $listing->getInstalled();
$sorted = $listing->getAll($installed);
$filteredPlugins = $sorted->filter(function (ListingItem $i) {
return $i->isShop5Compatible();
});
foreach ($filteredPlugins as $plugin) {
if (is_dir($plugin->getPath().'/Commands')) {
$finder = Finder::create()
->ignoreVCS(false)
->ignoreDotFiles(false)
->in($plugin->getPath().'/Commands');
foreach ($finder->files() as $file) {
$class = sprintf(
'Plugin\\%s\\Commands\\%s',
$plugin->getDir(),
str_replace('.'.$file->getExtension(), '', $file->getBasename())
);
if (!class_exists($class)) {
throw new \RuntimeException("Class '".$class."' does not exist");
}
$command = new $class();
$command->setName($plugin->getId().':'.$command->getName());
$this->add($command);
}
}
}
}
}
/**
* {@inheritdoc}
*/
public function doRun(InputInterface $input, OutputInterface $output)
{
$this->io = new ConsoleIO($input, $output, $this->getHelperSet());
$exitCode = parent::doRun($input, $output);
return $exitCode;
}
/**
* @return ConsoleIO
*/
public function getIO()
{
return $this->io;
}
protected function getDefaultCommands()
{
$cmds = parent::getDefaultCommands();
if ($this->isInstalled) {
$cmds[] = new MigrateCommand();
$cmds[] = new StatusCommand();
$cmds[] = new DatabaseCommand();
$cmds[] = new FilesCommand();
$cmds[] = new DeleteTemplateCacheCommand();
$cmds[] = new DeleteFileCacheCommand();
$cmds[] = new DbesTmpCommand();
if ($this->devMode) {
$cmds[] = new CreateCommand();
}
if (PLUGIN_DEV_MODE) {
$cmds[] = new CreateMigrationCommand();
$cmds[] = new CreateCommandCommand();
}
} else {
$cmds[] = new InstallCommand();
}
return $cmds;
}
/**
* {@inheritdoc}
*/
protected function createAdditionalStyles()
{
return [
'plain' => new OutputFormatterStyle(),
'highlight' => new OutputFormatterStyle('red'),
'warning' => new OutputFormatterStyle('black', 'yellow'),
'verbose' => new OutputFormatterStyle('white', 'magenta'),
'info_inverse' => new OutputFormatterStyle('white', 'blue'),
'comment_inverse' => new OutputFormatterStyle('black', 'yellow'),
'success_inverse' => new OutputFormatterStyle('black', 'green'),
'white_invert' => new OutputFormatterStyle('black', 'white'),
];
}
}
<?php
/**
* @copyright (c) JTL-Software-GmbH
* @license http://jtl-url.de/jtlshoplicense
*/
namespace JTL\Console\Command\Backup;
use JTL\Console\Command\Command;
use JTL\Update\Updater;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class DatabaseCommand extends Command
{
protected function configure()
{
$this
->setName('backup:db')
->setAliases(['database:backup'])
->setDescription('Backup shop database')
->addOption('compress', 'c', InputOption::VALUE_NONE, 'Enable (gzip) compression');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int|void|null
* @throws \Exception
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = $this->getIO();
$compress = $this->getOption('compress');
$updater = new Updater();
try {
$file = $updater->createSqlDumpFile($compress);
$updater->createSqlDump($file, $compress);
$io->success("SQL-Dump '{$file}' created.");
} catch (\Exception $e) {
$io->error($e->getMessage());
}
}
}
<?php
/**
* @copyright (c) JTL-Software-GmbH
* @license http://jtl-url.de/jtlshoplicense
*/
namespace JTL\Console\Command\Backup;
use JTL\Console\Command\Command;
use JTL\Filesystem\Filesystem;
use JTL\Filesystem\LocalFilesystem;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;
class FilesCommand extends Command
{
protected function configure()
{
$this
->setName('backup:files')
->setDescription('Backup shop content')
->addOption(
'exclude-dir',
'x',
InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Exclude directory'
);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = $this->getIO();
$archivePath = \PFAD_ROOT . \PFAD_EXPORT_BACKUP . \date('YmdHis') . '_file_backup.zip';
$excludeDirectories = array_merge(['export',
'templates_c',
'admin/templates_c',
'dbeS/tmp',
'dbeS/logs',
'jtllogs',
'install/logs'], $this->getOption('exclude-dir'));
$localFilesystem = new Filesystem(new LocalFilesystem([
'root' => PFAD_ROOT
]));
$finder = Finder::create()
->ignoreVCS(false)
->ignoreDotFiles(false)
->exclude($excludeDirectories)
->in(PFAD_ROOT);
$io
->progress(
function ($mycb) use ($localFilesystem, $archivePath, $finder) {
$localFilesystem->zip($finder, $archivePath, function ($count, $index) use (&$mycb) {
$mycb($count, $index);
});
},
'Creating archive [%bar%] %percent:3s%%'
)
->newLine()
->success("Archive '{$archivePath}' created.");
}
}
<?php
/**
* @copyright (c) JTL-Software-GmbH
* @license http://jtl-url.de/jtlshoplicense
*/
namespace JTL\Console\Command\Cache;
use JTL\Console\Command\Command;
use JTL\Filesystem\Filesystem;
use JTL\Filesystem\LocalFilesystem;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class DbesTmpCommand extends Command
{
protected function configure()
{
$this
->setName('cache:dbes:delete')
->setDescription('Delete dbeS cache');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = $this->getIO();
$localFileSystem = new Filesystem(new LocalFilesystem(['root' => PFAD_ROOT]));
$standardTplCacheResponse = $localFileSystem->deleteDirectory('dbeS/tmp/', true);
if ($standardTplCacheResponse) {
$io->success('DbeS tmp cache deleted.');
} else {
$io->warning('Nothind to delete.');
}
}
}
<?php
/**
* @copyright (c) JTL-Software-GmbH
* @license http://jtl-url.de/jtlshoplicense
*/
namespace JTL\Console\Command\Cache;
use JTL\Console\Command\Command;
use JTL\Filesystem\Filesystem;
use JTL\Filesystem\LocalFilesystem;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class DeleteFileCacheCommand extends Command
{
protected function configure()
{
$this
->setName('cache:file:delete')
->setDescription('Delete file cache');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int|void|null
* @throws \Exception
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = $this->getIO();
$localFileSystem = new Filesystem(new LocalFilesystem(['root' => PFAD_ROOT]));
$standardTplCacheResponse = $localFileSystem->deleteDirectory('/templates_c/filecache/', true);
if ($standardTplCacheResponse) {
$io->success('File cache deleted.');
} else {
$io->warning('Nothind to delete.');
}
}
}
<?php
/**
* @copyright (c) JTL-Software-GmbH
* @license http://jtl-url.de/jtlshoplicense
*/
namespace JTL\Console\Command\Cache;
use JTL\Console\Command\Command;
use JTL\Filesystem\Filesystem;
use JTL\Filesystem\LocalFilesystem;
use JTL\Shop;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class DeleteTemplateCacheCommand extends Command
{
protected function configure()
{
$this
->setName('cache:tpl:delete')
->setDescription('Delete template cache')
->addOption('admin', 'a', InputOption::VALUE_NONE, 'Delete admin template cache');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int|void|null
* @throws \Exception
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = $this->getIO();
$adminTpl = $this->getOption('admin');
$localFileSystem = new Filesystem(new LocalFilesystem(['root' => PFAD_ROOT]));
$activeTemplate = Shop::Container()->getDB()->select('ttemplate', 'eTyp', 'standard');
$standardTplCacheResponse = $localFileSystem->deleteDirectory('/templates_c/'.$activeTemplate->name);
if ($adminTpl) {
$localFileSystem->deleteDirectory('/admin/templates_c/', true);
}
if ($standardTplCacheResponse) {
$io->success('Template cache deleted.');
} else {
$io->warning('Nothind to delete.');
}
}
}
<?php
/**
* @copyright (c) JTL-Software-GmbH
* @license http://jtl-url.de/jtlshoplicense
*/
namespace JTL\Console\Command;
use Symfony\Component\Console\Command\Command as BaseCommand;
/**
* Class Command.
*/
class Command extends BaseCommand
{
/**
* Command constructor.
*
* @param null $name
*/
public function __construct($name = null)
{
parent::__construct($name);
}
/**
* @return \JTL\Console\Application|\Symfony\Component\Console\Application
*/
public function getApp()
{
return $this->getApplication();
}
/**
* @return \JTL\Console\ConsoleIO
*/
public function getIO()
{
return $this->getApp()->getIO();
}
public function getArgumentDefinition($name)
{
$argument = $this->getDefinition()->getArgument($name);
return $argument;
}
/**
* @param $name
*
* @return bool
*/
public function hasMissingOption($name)
{
$option = $this->getDefinition()->getOption($name);
$value = trim($this->getIO()->getInput()->getOption($name));
if ($option->isValueRequired() && $option->acceptValue()) {
if (empty($value)) {
return true;
}
}
return false;
}
/**
* @param $name
*
* @return \Symfony\Component\Console\Input\InputOption
*/
public function getOptionDefinition($name)
{
$def = $this->getDefinition()->getOption($name);
return $def;
}
/**
* @param $name
*
* @return string|array
*/
public function getOption($name)
{
$value = $this->getIO()->getInput()->getOption($name);
if (is_string($value)) {
$value = trim($value);
}
/*
$option = $this->getOptionDefinition($name);
if ($option->isValueRequired() && $option->acceptValue()) {
if (empty($value)) {
throw new \RuntimeException("Missing option '--{$name}' value");
}
}
*/
return $value;
}
/**
* Get options
*
* @return array
*/
public function getOptions()
{
return $this->getIO()->getInput()->getOptions();
}
/**
* @param $name
*
* @return mixed
*/
public function hasOption($name)
{
return $this->getIO()->getInput()->hasOption($name);
}
}
This diff is collapsed.
<?php
/**
* @copyright (c) JTL-Software-GmbH
* @license http://jtl-url.de/jtlshoplicense
*/
namespace JTL\Console\Command\Migration;
use JTL\Console\Command\Command;
use JTL\Update\MigrationHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class CreateCommand.
*/
class CreateCommand extends Command
{
protected function configure()
{
$this
->setName('migrate:create')
->setDescription('Create a new migration')
->addArgument('description', InputArgument::REQUIRED, 'Short migration description')
->addArgument('author', InputArgument::REQUIRED, 'Author');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
$description = trim($input->getArgument('description'));
$author = trim($input->getArgument('author'));
if (strlen($description) < 5) {
$description = $this->getIO()->ask('Short migration description');
$input->setArgument('description', $description);
}
if (strlen($author) < 2) {
$author = $this->getIO()->ask('Migration author');
$input->setArgument('author', $author);
}
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int|null|void
* @throws \Exception
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$description = trim($input->getArgument('description'));
$author = trim($input->getArgument('author'));
$migrationPath = MigrationHelper::create($description, $author);
$output->writeln("<info>Created Migration:</info> <comment>'".$migrationPath."'</comment>");
}
}
<?php
/**
* @copyright (c) JTL-Software-GmbH
* @license http://jtl-url.de/jtlshoplicense
*/
namespace JTL\Console\Command\Migration;
use Exception;
use JTL\Console\Command\Command;
use JTL\Update\IMigration;
use JTL\Update\MigrationManager;
use PDOException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class MigrateCommand.
*/
class MigrateCommand extends Command
{
protected function configure()
{
$this
->setName('migrate')
->setDescription('Run the database migrations');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @throws \Exception
*
* @return int|null|void
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = $this->getIO();
$manager = new MigrationManager();
$migrations = $manager->getMigrations();
$executedMigrations = $manager->getExecutedMigrations();
$identifier = \max(\array_merge($executedMigrations, \array_keys($migrations)));
if (empty($executedMigrations) && empty($migrations)) {
$io->writeln('<info>Nothing to migrate.</info>');
return;
}
try {
\ksort($migrations);
foreach ($migrations as $migration) {
if ($migration->getId() > $identifier) {
break;
}
if (!\in_array($migration->getId(), $executedMigrations)) {
$executedMigrations[] = $migration;
$manager->executeMigration($migration);
$io->writeln("<info>Migrated:</info> ".$migration->getName()." ".$migration->getDescription());
}
}
} catch (PDOException $e) {
[$code, , $message] = $e->errorInfo;
$manager->log($migration, IMigration::UP, $code, $message);
$io->error($e->getMessage());
} catch (Exception $e) {
$manager->log($migration, IMigration::UP, 'JTL01', $e->getMessage());
$io->error($e->getMessage());
}
}
}
<?php
/**
* @copyright (c) JTL-Software-GmbH
* @license http://jtl-url.de/jtlshoplicense
*/
namespace JTL\Console\Command\Migration;
use JTL\Console\Command\Command;
use JTL\Update\MigrationManager;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class StatusCommand.
*/
class StatusCommand extends Command
{
protected function configure()
{
$this
->setName('migrate:status')
->setDescription('Show the status of each migration');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @throws \Exception
*
* @return int|null|void
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$list = [];
$manager = new MigrationManager();
$executedMigrations = $manager->getExecutedMigrations();
foreach ($manager->getMigrations() as $key => $migration) {
$list[] = (object)[
'id' => $migration->getId(),
'name' => $migration->getName(),
'author' => $migration->getAuthor(),
'description' => $migration->getDescription(),
'executed' => in_array($key, $executedMigrations)
];
}
$this->printMigrationTable($list);
}
/**
* @param $list
*/
protected function printMigrationTable($list)
{
if (count($list) === 0) {
$this->getIO()->note('No migration found.');
return;
}
$rows = [];
$headers = ['Migration', 'Description', 'Author', ''];
foreach ($list as $item) {
$rows[] = [$item->id, $item->description, $item->author,
$item->executed ? '<info> ✔ </info>' : '<comment> • </comment>', ];
}
$this->getIO()->writeln('');
$this->getIO()->table($headers, $rows);
}
}
<?php
/**
* {$description}
*
* @author {$author}
* @created {$created}
*/
use JTL\Update\IMigration;
use JTL\Update\Migration;
/**
* Migration
*
* Available methods:
* execute - returns affected rows
* fetchOne - single fetched object
* fetchAll - array of fetched objects
* fetchArray - array of fetched assoc arrays
* dropColumn - drops a column if exists
* setLocalization - add localization
* removeLocalization - remove localization
* setConfig - add / update config property
* removeConfig - remove config property
*/
class Migration_{$timestamp} extends Migration implements IMigration
{
protected $author = '{$author}';
protected $description = '{$description}';
public function up()
{
}
public function down()
{
}
}
\ No newline at end of file
<?php
/**
* @copyright (c) JTL-Software-GmbH
* @license http://jtl-url.de/jtlshoplicense
*/
namespace JTL\Console\Command\Plugin;
use JTL\Console\Command\Command;
use JTL\Filesystem\Filesystem;
use JTL\Filesystem\LocalFilesystem;
use JTL\Plugin\Helper;
use JTL\Shop;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class CreateCommandCommand extends Command
{
protected function configure()
{
$this
->setName('plugin:command:create')
->setDescription('Create new plugin command')
->addArgument('plugin-id', InputArgument::REQUIRED, 'Plugin id')
->addArgument('command-name', InputArgument::REQUIRED, 'Command name, like \'CronCommand\'')
->addArgument('author', InputArgument::REQUIRED, 'Author');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int|null
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$pluginId = trim($input->getArgument('plugin-id'));
$commandName = trim($input->getArgument('command-name'));
$author = trim($input->getArgument('author'));
try {
$commandPath = $this->createFile($pluginId, $commandName, $author);
$output->writeln("<info>Created command:</info> <comment>'".$commandPath."'</comment>");
} catch (\Exception $e) {
$this->getIO()->error($e->getMessage());
return 1;
}
}
/**
* @param string $pluginId
* @param string $commandName
* @param string $author
* @return string
* @throws \SmartyException
* @throws \Exception
*/
protected function createFile(string $pluginId, string $commandName, string $author): string
{
if (empty(Helper::getIDByPluginID($pluginId))) {
throw new \Exception('There is no plugin for the given dir name.');
}
$datetime = new \DateTime('NOW');
$relPath = 'plugins/'.$pluginId.'/Commands';
$migrationPath = $relPath.'/'.$commandName.'.php';
$fileSystem = new Filesystem(new LocalFilesystem(['root' => PFAD_ROOT]));
if (!$fileSystem->exists($relPath)) {
throw new \Exception('Commands path doesn\'t exist!');
}
$content = Shop::Smarty()
->assign('commandName', $commandName)
->assign('author', $author)
->assign('created', $datetime->format(\DateTime::RSS))
->assign('pluginId', $pluginId)
->fetch(PFAD_ROOT.'includes/src/Console/Command/Plugin/Template/command.class.tpl');
$fileSystem->put($migrationPath, $content);
return $migrationPath;
}
}
<?php
/**
* @copyright (c) JTL-Software-GmbH
* @license http://jtl-url.de/jtlshoplicense
*/
namespace JTL\Console\Command\Plugin;
use JTL\Console\Command\Command;
use JTL\Plugin\MigrationHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class CreateMigrationCommand extends Command
{
protected function configure()
{
$this
->setName('plugin:migration:create')
->setDescription('Create new plugin migration')
->addArgument('plugin-dir', InputArgument::REQUIRED, 'Plugin dir name')
->addArgument('description', InputArgument::REQUIRED, 'Short migration description')
->addArgument('author', InputArgument::REQUIRED, 'Author');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int|null
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$pluginDir = trim($input->getArgument('plugin-dir'));
$description = trim($input->getArgument('description'));
$author = trim($input->getArgument('author'));
try {
$migrationPath = MigrationHelper::create($pluginDir, $description, $author);
$output->writeln("<info>Created Migration:</info> <comment>'".$migrationPath."'</comment>");
} catch (\Exception $e) {
$this->getIO()->error($e->getMessage());
return 1;
}
}
}
<?php
/**
* {$commandName}
*
* @author {$author}
* @created {$created}
*/
namespace Plugin\{$pluginId}\Commands;
use JTL\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class {$commandName} extends Command
{
protected function configure()
{
$this
->setName('plugin:test')
->setDescription('Test description')
->addArgument('arg1', InputArgument::REQUIRED, 'Argument one');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
}
}
\ No newline at end of file
<?php
/**
* {$description}
*
* @author {$author}
* @created {$created}
*/
namespace Plugin\{$pluginDir}\Migration;
use JTL\Plugin\Migration;
use JTL\Update\IMigration;
/**
* Migration
*
* Available methods:
* execute - returns affected rows
* fetchOne - single fetched object
* fetchAll - array of fetched objects
* fetchArray - array of fetched assoc arrays
* dropColumn - drops a column if exists
* setLocalization - add localization
* removeLocalization - remove localization
* setConfig - add / update config property
* removeConfig - remove config property
*/
class Migration{$timestamp} extends Migration implements IMigration
{
protected $author = '{$author}';
protected $description = '{$description}';
public function up()
{
}
public function down()
{
}
}
\ No newline at end of file
This diff is collapsed.
......@@ -74,7 +74,7 @@ abstract class AbstractFilesystem implements IFilesystem
*
* @return string prefixed path
*
* @throws Exception
* @throws \Exception
*/
public function applyPathPrefix(string $path) : string
{
......@@ -87,7 +87,7 @@ abstract class AbstractFilesystem implements IFilesystem
$rooted = Path::combine($this->getPathPrefix(), $path);
if (!$this->hasPathPrefix($rooted)) {
throw new Exception(sprintf("Path '%s' is not within defined root", $rooted));
throw new \Exception(sprintf("Path '%s' is not within defined root", $rooted));
}
return $rooted;
......@@ -119,7 +119,7 @@ abstract class AbstractFilesystem implements IFilesystem
*
* @return bool
*/
public function hasPathPrefix(string $path) : string
public function hasPathPrefix(string $path) : bool
{
$prefix = $this->getPathPrefix();
......
......@@ -6,6 +6,8 @@
namespace JTL\Filesystem;
use JTL\Path;
/**
* Class FileInfo
* @package JTL\Filesystem
......
......@@ -6,7 +6,11 @@
namespace JTL\Filesystem;
use Exception;
use Generator;
use JTL\Path;
use Symfony\Component\Finder\Finder;
use ZipArchive;
/**
* Class Filesystem
......@@ -313,6 +317,11 @@ class Filesystem implements IFilesystem
return true;
}
public function zip(Finder $finder, string $archivePath, callable $callback = null): bool
{
return $this->getAdapter()->zip($finder, $archivePath, $callback);
}
public function getOwner($identity)
{
if (is_numeric($identity)) {
......
......@@ -293,6 +293,14 @@ class FtpFilesystem extends AbstractFilesystem
}
}
/**
* {@inheritdoc}
*/
public function zip(Finder $finder, string $archivePath, callable $callback = null): bool
{
throw new RuntimeException();
}
protected function isDir($directory)
{
$location = $this->applyPathPrefix($directory);
......@@ -408,10 +416,11 @@ class FtpFilesystem extends AbstractFilesystem
}
/**
* @param array $listing
* @param array $listing
* @param string $prefix
*
* @return \Generator
* @throws Exception
*/
protected function normalizeListing(array $listing, $prefix = '')
{
......
......@@ -7,6 +7,7 @@
namespace JTL\Filesystem;
use Generator;
use Symfony\Component\Finder\Finder;
/**
* Interface IFilesystem
......@@ -62,4 +63,6 @@ interface IFilesystem
public function copyDirectory($from, $to, $mode = null) : bool;
public function deleteDirectory($directory, $preserve = false) : bool;
public function zip(Finder $finder, string $archivePath, callable $callback = null) : bool;
}
......@@ -6,11 +6,14 @@
namespace JTL\Filesystem;
use Exception;
use Generator;
use JTL\Path;
use SplFileInfo;
use FilesystemIterator;
use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;
use Symfony\Component\Finder\Finder;
/**
* Class LocalFilesystem
......@@ -288,7 +291,7 @@ class LocalFilesystem extends AbstractFilesystem
*/
protected function mapFileInfo(SplFileInfo $file) : FileInfo
{
$location = $this->removePathPrefix($file->getPath());
$location = $this->removePathPrefix($file->getPathname());
$options = [
'path' => (string)$location,
......@@ -321,7 +324,7 @@ class LocalFilesystem extends AbstractFilesystem
/**
* @param string $path
* @param int $mode
* @param int $mode
*
* @return RecursiveIteratorIterator
*/
......@@ -344,4 +347,37 @@ class LocalFilesystem extends AbstractFilesystem
{
return new FilesystemIterator($path, FilesystemIterator::SKIP_DOTS);
}
/**
* @param Finder $finder
* @param string $archivePath
* @param callable|null $callback
* @return bool
* @throws Exception
*/
public function zip(Finder $finder, string $archivePath, callable $callback = null): bool
{
$zipArchive = new \ZipArchive();
$count = $finder->count();
$index = 0;
$basePath = rtrim($finder->getIterator()->getPath(), '/').'/';
if (($code = $zipArchive->open($archivePath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE)) !== true) {
throw new Exception('Archive file could not be created.', $code);
}
foreach ($finder->files() as $file) {
if (!$file->isDir()) {
$zipArchive->addFile($file->getRealpath(), str_replace($basePath, '', $file->getRealpath()));
if (is_callable($callback)) {
$callback($count, $index);
++$index;
}
}
}
$zipArchive->close();
return true;
}
}
......@@ -9,6 +9,10 @@ namespace JTL\Plugin;
use DirectoryIterator;
use JTL\DB\DbInterface;
use JTL\DB\ReturnType;
use DirectoryIterator;
use JTL\Filesystem\Filesystem;
use JTL\Filesystem\LocalFilesystem;
use JTL\Shop;
/**
* Class MigrationHelper
......@@ -211,4 +215,46 @@ final class MigrationHelper
return null;
}
/**
* @param string $pluginDir
* @param string $description
* @param string $author
* @return string
* @throws \SmartyException
* @throws \Exception
*/
public static function create(string $pluginDir, string $description, string $author): string
{
$plugin = Shop::Container()->getDB()->select('tplugin', 'cVerzeichnis', $pluginDir);
if (empty($plugin)) {
throw new \Exception('There is no plugin for the given dir name.');
}
$datetime = new \DateTime('NOW');
$timestamp = $datetime->format('YmdHis');
$filePath = 'Migration'.$timestamp;
$relPath = 'plugins/'.$pluginDir.'/Migrations';
$migrationPath = $relPath.'/'.$filePath.'.php';
$fileSystem = new Filesystem(new LocalFilesystem(['root' => PFAD_ROOT]));
if (!$fileSystem->exists($relPath)) {
throw new \Exception('Migrations path doesn\'t exist!');
}
$content = Shop::Smarty()
->assign('description', $description)
->assign('author', $author)
->assign('created', $datetime->format(\DateTime::RSS))
->assign('pluginDir', $pluginDir)
->assign('timestamp', $timestamp)
->fetch(PFAD_ROOT.'includes/src/Console/Command/Plugin/Template/migration.class.tpl');
$fileSystem->put($migrationPath, $content);
return $migrationPath;
}
}
......@@ -7,7 +7,11 @@
namespace JTL\Update;
use JTL\DB\ReturnType;
use JTL\Filesystem\Filesystem;
use JTL\Filesystem\LocalFilesystem;
use JTL\Shop;
use JTL\Smarty\JTLSmarty;
use Smarty;
/**
* Class MigrationHelper
......@@ -221,4 +225,49 @@ class MigrationHelper
return true;
}
/**
* @param string $description
* @param string $author
* @return string
* @throws \SmartyException
* @throws \Exception
*/
public static function create(string $description, string $author)
{
$datetime = new \DateTime('NOW');
$timestamp = $datetime->format('YmdHis');
$asFilePath = function ($text) {
$text = preg_replace('/\W/', '_', $text);
$text = preg_replace('/_+/', '_', $text);
return strtolower($text);
};
$filePath = implode(
'_',
array_filter([$timestamp, $asFilePath($description)])
);
$relPath = 'update/migrations';
$migrationPath = $relPath.'/'.$filePath.'.php';
$fileSystem = new Filesystem(new LocalFilesystem(['root' => PFAD_ROOT]));
if (!$fileSystem->exists($relPath)) {
throw new \Exception('Migrations path doesn\'t exist!');
}
$smartyCli = Shop::Smarty(true, 'cli');
$smartyCli->setCaching(Smarty::CACHING_OFF);
$content = $smartyCli->assign('description', $description)
->assign('author', $author)
->assign('created', $datetime->format(\DateTime::RSS))
->assign('timestamp', $timestamp)
->fetch(PFAD_ROOT.'includes/src/Console/Command/Migration/Template/migration.class.tpl');
$fileSystem->put($migrationPath, $content);
return $migrationPath;
}
}
......@@ -11,6 +11,8 @@ use Exception;
use InvalidArgumentException;
use JTL\DB\DbInterface;
use JTL\DB\ReturnType;
use JTL\Filesystem\Filesystem;
use JTL\Filesystem\LocalFilesystem;
use JTL\Shop;
use JTLShop\SemVer\Version;
use PDOException;
......@@ -111,6 +113,7 @@ class MigrationManager
* @param int $id MigrationId
* @return IMigration
* @throws InvalidArgumentException
* @throws Exception
*/
public function getMigrationById($id): IMigration
{
......@@ -155,9 +158,11 @@ class MigrationManager
$this->migrated($migration, $direction, $start);
} catch (Exception $e) {
Shop::Container()->getDB()->rollback();
$migrationFile = new \ReflectionClass($migration->getName());
throw new \Exception(
$migration->getName() . ' ' . $migration->getDescription() . ' | ' . $e->getMessage(),
$e->getCode()
'"'.$e->getMessage().'" in: '.$migrationFile->getFileName(),
(int)$e->getCode()
);
}
}
......@@ -179,6 +184,7 @@ class MigrationManager
* Has valid migrations.
*
* @return bool
* @throws Exception
*/
public function hasMigrations(): bool
{
......@@ -189,6 +195,7 @@ class MigrationManager