fix labels and text editors for Quest creation

This commit is contained in:
oliver 2016-01-15 12:33:07 +01:00
commit 476c18b6a9
4278 changed files with 1196345 additions and 0 deletions

View file

@ -0,0 +1,90 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class CodeCoverage extends ConsoleCommand
{
protected function configure()
{
$this->setName('tests:coverage');
$this->setDescription('Run all phpunit tests and generate a combined code coverage');
$this->addArgument('group', InputArgument::OPTIONAL, 'Run only a specific test group. Separate multiple groups by comma, for instance core,integration', '');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$phpCovPath = trim(shell_exec('which phpcov'));
if (empty($phpCovPath)) {
$output->writeln('phpcov not installed. please install pear.phpunit.de/phpcov.');
return;
}
$command = $this->getApplication()->find('tests:run');
$arguments = array(
'command' => 'tests:run',
'--options' => sprintf('--coverage-php %s/tests/results/logs/%%group%%.cov', PIWIK_DOCUMENT_ROOT),
);
$groups = $input->getArgument('group');
if (!empty($groups)) {
$arguments['group'] = $groups;
} else {
shell_exec(sprintf('rm %s/tests/results/logs/*.cov', PIWIK_DOCUMENT_ROOT));
}
$inputObject = new ArrayInput($arguments);
$inputObject->setInteractive($input->isInteractive());
$command->run($inputObject, $output);
$command = 'phpcov';
// force xdebug usage for coverage options
if (!extension_loaded('xdebug')) {
$output->writeln('<info>xdebug extension required for code coverage.</info>');
$output->writeln('<info>searching for xdebug extension...</info>');
$extensionDir = shell_exec('php-config --extension-dir');
$xdebugFile = trim($extensionDir) . DIRECTORY_SEPARATOR . 'xdebug.so';
if (!file_exists($xdebugFile)) {
$dialog = $this->getHelperSet()->get('dialog');
$xdebugFile = $dialog->askAndValidate($output, 'xdebug not found. Please provide path to xdebug.so', function($xdebugFile) {
return file_exists($xdebugFile);
});
} else {
$output->writeln('<info>xdebug extension found in extension path.</info>');
}
$output->writeln("<info>using $xdebugFile as xdebug extension.</info>");
$command = sprintf('php -d zend_extension=%s %s', $xdebugFile, $phpCovPath);
}
shell_exec(sprintf('rm -rf %s/tests/results/coverage/*', PIWIK_DOCUMENT_ROOT));
passthru(sprintf('cd %1$s && %2$s --merge --html tests/results/coverage/ --whitelist ./core/ --whitelist ./plugins/ --add-uncovered %1$s/tests/results/logs/', PIWIK_DOCUMENT_ROOT, $command));
}
}

View file

@ -0,0 +1,55 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\CronArchive;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class CoreArchiver extends ConsoleCommand
{
protected function configure()
{
$this->configureArchiveCommand($this);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
if ($input->getOption('piwik-domain') && !$input->getOption('url')) {
$_SERVER['argv'][] = '--url=' . $input->getOption('piwik-domain');
}
include PIWIK_INCLUDE_PATH . '/misc/cron/archive.php';
}
// This is reused by another console command
static public function configureArchiveCommand(ConsoleCommand $command)
{
$command->setName('core:archive');
$command->setDescription("Runs the CLI archiver. It is an important tool for general maintenance and to keep Piwik very fast.");
$command->setHelp("* It is recommended to run the script with the option --piwik-domain=[piwik-server-url] only. Other options are not required.
* This script should be executed every hour via crontab, or as a daemon.
* You can also run it via http:// by specifying the Super User &token_auth=XYZ as a parameter ('Web Cron'),
but it is recommended to run it via command line/CLI instead.
* If you have any suggestion about this script, please let the team know at hello@piwik.org
* Enjoy!");
$command->addOption('url', null, InputOption::VALUE_REQUIRED, "Mandatory option as an alternative to '--piwik-domain'. Must be set to the Piwik base URL.\nFor example: --url=http://analytics.example.org/ or --url=https://example.org/piwik/");
$command->addOption('force-all-websites', null, InputOption::VALUE_NONE, "If specified, the script will trigger archiving on all websites and all past dates.\nYou may use --force-all-periods=[seconds] to trigger archiving on those websites\nthat had visits in the last [seconds] seconds.");
$command->addOption('force-all-periods', null, InputOption::VALUE_OPTIONAL, "Limits archiving to websites with some traffic in the last [seconds] seconds. \nFor example --force-all-periods=86400 will archive websites that had visits in the last 24 hours. \nIf [seconds] is not specified, all websites with visits in the last " . CronArchive::ARCHIVE_SITES_WITH_TRAFFIC_SINCE . "\n seconds (" . round(CronArchive::ARCHIVE_SITES_WITH_TRAFFIC_SINCE / 86400) . " days) will be archived.");
$command->addOption('force-timeout-for-periods', null, InputOption::VALUE_OPTIONAL, "The current week/ current month/ current year will be processed at most every [seconds].\nIf not specified, defaults to " . CronArchive::SECONDS_DELAY_BETWEEN_PERIOD_ARCHIVES . ".");
$command->addOption('force-date-last-n', null, InputOption::VALUE_REQUIRED, "This script calls the API with period=lastN. You can force the N in lastN by specifying this value.");
$command->addOption('force-idsites', null, InputOption::VALUE_OPTIONAL, 'If specified, archiving will be processed only for these Sites Ids (comma separated)');
$command->addOption('skip-idsites', null, InputOption::VALUE_OPTIONAL, 'If specified, archiving will be skipped for these websites (in case these website ids would have been archived).');
$command->addOption('disable-scheduled-tasks', null, InputOption::VALUE_NONE, "Skips executing Scheduled tasks (sending scheduled reports, db optimization, etc.).");
$command->addOption('xhprof', null, InputOption::VALUE_NONE, "Enables XHProf profiler for this archive.php run. Requires XHPRof (see tests/README.xhprof.md).");
$command->addOption('accept-invalid-ssl-certificate', null, InputOption::VALUE_NONE, "It is _NOT_ recommended to use this argument. Instead, you should use a valid SSL certificate!\nIt can be useful if you specified --url=https://... or if you are using Piwik with force_ssl=1");
}
}

View file

@ -0,0 +1,58 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class GenerateApi extends GeneratePluginBase
{
protected function configure()
{
$this->setName('generate:api')
->setDescription('Adds an API to an existing plugin')
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have an API yet');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$pluginName = $this->getPluginName($input, $output);
$exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin';
$replace = array('ExamplePlugin' => $pluginName);
$whitelistFiles = array('/API.php');
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
$this->writeSuccessMessage($output, array(
sprintf('API.php for %s generated.', $pluginName),
'You can now start adding API methods',
'Enjoy!'
));
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return array
* @throws \RunTimeException
*/
protected function getPluginName(InputInterface $input, OutputInterface $output)
{
$pluginNames = $this->getPluginNamesHavingNotSpecificFile('API.php');
$invalidName = 'You have to enter the name of an existing plugin which does not already have an API';
return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName);
}
}

View file

@ -0,0 +1,89 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Common;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class GenerateCommand extends GeneratePluginBase
{
protected function configure()
{
$this->setName('generate:command')
->setDescription('Adds a command to an existing plugin')
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin')
->addOption('command', null, InputOption::VALUE_REQUIRED, 'The name of the command you want to create');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$pluginName = $this->getPluginName($input, $output);
$commandName = $this->getCommandName($input, $output);
$exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExampleCommand';
$replace = array(
'ExampleCommand' => $pluginName,
'examplecommand' => strtolower($pluginName),
'HelloWorld' => $commandName,
'helloworld' => strtolower($commandName)
);
$whitelistFiles = array('/Commands', '/Commands/HelloWorld.php');
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
$this->writeSuccessMessage($output, array(
sprintf('Command %s for plugin %s generated', $commandName, $pluginName)
));
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return string
* @throws \RunTimeException
*/
private function getCommandName(InputInterface $input, OutputInterface $output)
{
$testname = $input->getOption('command');
$validate = function ($testname) {
if (empty($testname)) {
throw new \InvalidArgumentException('You have to enter a command name');
}
return $testname;
};
if (empty($testname)) {
$dialog = $this->getHelperSet()->get('dialog');
$testname = $dialog->askAndValidate($output, 'Enter the name of the command: ', $validate);
} else {
$validate($testname);
}
$testname = ucfirst($testname);
return $testname;
}
protected function getPluginName(InputInterface $input, OutputInterface $output)
{
$pluginNames = $this->getPluginNames();
$invalidName = 'You have to enter the name of an existing plugin';
return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName);
}
}

View file

@ -0,0 +1,58 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class GenerateController extends GeneratePluginBase
{
protected function configure()
{
$this->setName('generate:controller')
->setDescription('Adds a Controller to an existing plugin')
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have a Controller yet');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$pluginName = $this->getPluginName($input, $output);
$exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin';
$replace = array('ExamplePlugin' => $pluginName);
$whitelistFiles = array('/Controller.php', '/templates', '/templates/index.twig');
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
$this->writeSuccessMessage($output, array(
sprintf('Controller for %s generated.', $pluginName),
'You can now start adding Controller actions',
'Enjoy!'
));
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return array
* @throws \RunTimeException
*/
protected function getPluginName(InputInterface $input, OutputInterface $output)
{
$pluginNames = $this->getPluginNamesHavingNotSpecificFile('Controller.php');
$invalidName = 'You have to enter the name of an existing plugin which does not already have a Controller';
return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName);
}
}

View file

@ -0,0 +1,223 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Filesystem;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class GeneratePlugin extends GeneratePluginBase
{
protected function configure()
{
$this->setName('generate:plugin')
->setAliases(array('generate:theme'))
->setDescription('Generates a new plugin/theme including all needed files')
->addOption('name', null, InputOption::VALUE_REQUIRED, 'Plugin name ([a-Z0-9_-])')
->addOption('description', null, InputOption::VALUE_REQUIRED, 'Plugin description, max 150 characters')
->addOption('pluginversion', null, InputOption::VALUE_OPTIONAL, 'Plugin version')
->addOption('full', null, InputOption::VALUE_OPTIONAL, 'If a value is set, an API and a Controller will be created as well. Option is only available for creating plugins, not for creating themes.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$isTheme = $this->isTheme($input);
$pluginName = $this->getPluginName($input, $output);
$description = $this->getPluginDescription($input, $output);
$version = $this->getPluginVersion($input, $output);
$createFullPlugin = !$isTheme && $this->getCreateFullPlugin($input, $output);
$this->generatePluginFolder($pluginName);
if ($isTheme) {
$exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExampleTheme';
$replace = array(
'ExampleTheme' => $pluginName,
'ExampleDescription' => $description,
'0.1.0' => $version
);
$whitelistFiles = array();
} else {
$exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin';
$replace = array(
'ExamplePlugin' => $pluginName,
'ExampleDescription' => $description,
'0.1.0' => $version
);
$whitelistFiles = array(
'/ExamplePlugin.php',
'/plugin.json',
'/README.md',
'/.travis.yml',
'/screenshots',
'/screenshots/.gitkeep',
'/javascripts',
'/javascripts/plugin.js',
);
}
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
$this->writeSuccessMessage($output, array(
sprintf('%s %s %s generated.', $isTheme ? 'Theme' : 'Plugin', $pluginName, $version),
'Enjoy!'
));
if ($createFullPlugin) {
$this->executePluginCommand($output, 'generate:api', $pluginName);
$this->executePluginCommand($output, 'generate:controller', $pluginName);
}
}
private function executePluginCommand(OutputInterface $output, $commandName, $pluginName)
{
$command = $this->getApplication()->find($commandName);
$arguments = array(
'command' => $commandName,
'--pluginname' => $pluginName
);
$input = new ArrayInput($arguments);
$command->run($input, $output);
}
/**
* @param InputInterface $input
* @return bool
*/
private function isTheme(InputInterface $input)
{
$commandName = $input->getFirstArgument();
return false !== strpos($commandName, 'theme');
}
protected function generatePluginFolder($pluginName)
{
$pluginPath = $this->getPluginPath($pluginName);
Filesystem::mkdir($pluginPath, true);
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return array
* @throws \RunTimeException
*/
protected function getPluginName(InputInterface $input, OutputInterface $output)
{
$self = $this;
$validate = function ($pluginName) use ($self) {
if (empty($pluginName)) {
throw new \RunTimeException('You have to enter a plugin name');
}
if (!Filesystem::isValidFilename($pluginName)) {
throw new \RunTimeException(sprintf('The plugin name %s is not valid', $pluginName));
}
$pluginPath = $self->getPluginPath($pluginName);
if (file_exists($pluginPath)) {
throw new \RunTimeException('A plugin with this name already exists');
}
return $pluginName;
};
$pluginName = $input->getOption('name');
if (empty($pluginName)) {
$dialog = $this->getHelperSet()->get('dialog');
$pluginName = $dialog->askAndValidate($output, 'Enter a plugin name: ', $validate);
} else {
$validate($pluginName);
}
$pluginName = ucfirst($pluginName);
return $pluginName;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return mixed
* @throws \RunTimeException
*/
protected function getPluginDescription(InputInterface $input, OutputInterface $output)
{
$validate = function ($description) {
if (empty($description)) {
throw new \RunTimeException('You have to enter a description');
}
if (150 < strlen($description)) {
throw new \RunTimeException('Description is too long, max 150 characters allowed.');
}
return $description;
};
$description = $input->getOption('description');
if (empty($description)) {
$dialog = $this->getHelperSet()->get('dialog');
$description = $dialog->askAndValidate($output, 'Enter a plugin description: ', $validate);
} else {
$validate($description);
}
return $description;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return string
*/
protected function getPluginVersion(InputInterface $input, OutputInterface $output)
{
$version = $input->getOption('pluginversion');
if (is_null($version)) {
$dialog = $this->getHelperSet()->get('dialog');
$version = $dialog->ask($output, 'Enter a plugin version number (default to 0.1.0): ', '0.1.0');
}
return $version;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return mixed
*/
protected function getCreateFullPlugin(InputInterface $input, OutputInterface $output)
{
$full = $input->getOption('full');
if (is_null($full)) {
$dialog = $this->getHelperSet()->get('dialog');
$full = $dialog->askConfirmation($output, 'Shall we also create an API and a Controller? (y/N)', false);
}
return !empty($full);
}
}

View file

@ -0,0 +1,143 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Filesystem;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
abstract class GeneratePluginBase extends ConsoleCommand
{
public function getPluginPath($pluginName)
{
return PIWIK_INCLUDE_PATH . '/plugins/' . ucfirst($pluginName);
}
private function createFolderWithinPluginIfNotExists($pluginName, $folder)
{
$pluginPath = $this->getPluginPath($pluginName);
if (!file_exists($pluginName . $folder)) {
Filesystem::mkdir($pluginPath . $folder, true);
}
}
protected function createFileWithinPluginIfNotExists($pluginName, $fileName, $content)
{
$pluginPath = $this->getPluginPath($pluginName);
if (!file_exists($pluginPath . $fileName)) {
file_put_contents($pluginPath . $fileName, $content);
}
}
/**
* @param string $templateFolder full path like /home/...
* @param string $pluginName
* @param array $replace array(key => value) $key will be replaced by $value in all templates
* @param array $whitelistFiles If not empty, only given files/directories will be copied.
* For instance array('/Controller.php', '/templates', '/templates/index.twig')
*/
protected function copyTemplateToPlugin($templateFolder, $pluginName, array $replace = array(), $whitelistFiles = array())
{
$replace['PLUGINNAME'] = $pluginName;
$files = array_merge(
Filesystem::globr($templateFolder, '*'),
// Also copy files starting with . such as .gitignore
Filesystem::globr($templateFolder, '.*')
);
foreach ($files as $file) {
$fileNamePlugin = str_replace($templateFolder, '', $file);
if (!empty($whitelistFiles) && !in_array($fileNamePlugin, $whitelistFiles)) {
continue;
}
if (is_dir($file)) {
$this->createFolderWithinPluginIfNotExists($pluginName, $fileNamePlugin);
} else {
$template = file_get_contents($file);
foreach ($replace as $key => $value) {
$template = str_replace($key, $value, $template);
}
foreach ($replace as $key => $value) {
$fileNamePlugin = str_replace($key, $value, $fileNamePlugin);
}
$this->createFileWithinPluginIfNotExists($pluginName, $fileNamePlugin, $template);
}
}
}
protected function getPluginNames()
{
$pluginDirs = \_glob(PIWIK_INCLUDE_PATH . '/plugins/*', GLOB_ONLYDIR);
$pluginNames = array();
foreach ($pluginDirs as $pluginDir) {
$pluginNames[] = basename($pluginDir);
}
return $pluginNames;
}
protected function getPluginNamesHavingNotSpecificFile($filename)
{
$pluginDirs = \_glob(PIWIK_INCLUDE_PATH . '/plugins/*', GLOB_ONLYDIR);
$pluginNames = array();
foreach ($pluginDirs as $pluginDir) {
if (!file_exists($pluginDir . '/' . $filename)) {
$pluginNames[] = basename($pluginDir);
}
}
return $pluginNames;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return array
* @throws \RunTimeException
*/
protected function askPluginNameAndValidate(InputInterface $input, OutputInterface $output, $pluginNames, $invalidArgumentException)
{
$validate = function ($pluginName) use ($pluginNames, $invalidArgumentException) {
if (!in_array($pluginName, $pluginNames)) {
throw new \InvalidArgumentException($invalidArgumentException);
}
return $pluginName;
};
$pluginName = $input->getOption('pluginname');
if (empty($pluginName)) {
$dialog = $this->getHelperSet()->get('dialog');
$pluginName = $dialog->askAndValidate($output, 'Enter the name of your plugin: ', $validate, false, null, $pluginNames);
} else {
$validate($pluginName);
}
$pluginName = ucfirst($pluginName);
return $pluginName;
}
}

View file

@ -0,0 +1,58 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class GenerateSettings extends GeneratePluginBase
{
protected function configure()
{
$this->setName('generate:settings')
->setDescription('Adds a plugin setting class to an existing plugin')
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have settings yet');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$pluginName = $this->getPluginName($input, $output);
$exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExampleSettingsPlugin';
$replace = array('ExampleSettingsPlugin' => $pluginName);
$whitelistFiles = array('/Settings.php');
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
$this->writeSuccessMessage($output, array(
sprintf('Settings.php for %s generated.', $pluginName),
'You can now start defining your plugin settings',
'Enjoy!'
));
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return array
* @throws \RunTimeException
*/
protected function getPluginName(InputInterface $input, OutputInterface $output)
{
$pluginNames = $this->getPluginNamesHavingNotSpecificFile('Settings.php');
$invalidName = 'You have to enter the name of an existing plugin which does not already have settings';
return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName);
}
}

View file

@ -0,0 +1,189 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Common;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class GenerateTest extends GeneratePluginBase
{
protected function configure()
{
$this->setName('generate:test')
->setDescription('Adds a test to an existing plugin')
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin')
->addOption('testname', null, InputOption::VALUE_REQUIRED, 'The name of the test to create')
->addOption('testtype', null, InputOption::VALUE_REQUIRED, 'Whether you want to create a "unit", "integration" or "database" test');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$pluginName = $this->getPluginName($input, $output);
$testName = $this->getTestName($input, $output);
$testType = $this->getTestType($input, $output);
$exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin';
$replace = array(
'ExamplePlugin' => $pluginName,
'SimpleTest' => $testName,
'SimpleIntegrationTest' => $testName,
'@group Plugins' => '@group ' . $testType
);
$testClass = $this->getTestClass($testType);
if(!empty($testClass)) {
$replace['\PHPUnit_Framework_TestCase'] = $testClass;
}
$whitelistFiles = $this->getTestFilesWhitelist($testType);
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
$this->writeSuccessMessage($output, array(
sprintf('Test %s for plugin %s generated.', $testName, $pluginName),
'You can now start writing beautiful tests!',
));
$this->writeSuccessMessage($output, array(
'To run all your plugin tests, execute the command: ',
sprintf('./console tests:run %s', $pluginName),
'To run only this test: ',
sprintf('./console tests:run %s', $testName),
'Enjoy!'
));
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return string
* @throws \RunTimeException
*/
private function getTestName(InputInterface $input, OutputInterface $output)
{
$testname = $input->getOption('testname');
$validate = function ($testname) {
if (empty($testname)) {
throw new \InvalidArgumentException('You have to enter a valid test name ');
}
return $testname;
};
if (empty($testname)) {
$dialog = $this->getHelperSet()->get('dialog');
$testname = $dialog->askAndValidate($output, 'Enter the name of the test: ', $validate);
} else {
$validate($testname);
}
if (!Common::stringEndsWith(strtolower($testname), 'test')) {
$testname = $testname . 'Test';
}
$testname = ucfirst($testname);
return $testname;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return array
* @throws \RunTimeException
*/
protected function getPluginName(InputInterface $input, OutputInterface $output)
{
$pluginNames = $this->getPluginNames();
$invalidName = 'You have to enter the name of an existing plugin';
return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName);
}
/**
* @param InputInterface $input
* @return string
*/
private function getTestClass($testType)
{
if ('Database' == $testType) {
return '\DatabaseTestCase';
}
if ('Unit' == $testType) {
return '\PHPUnit_Framework_TestCase';
}
return false;
}
public function getValidTypes()
{
return array('unit', 'integration', 'database');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return string Unit, Integration, Database
*/
private function getTestType(InputInterface $input, OutputInterface $output)
{
$testtype = $input->getOption('testtype');
$self = $this;
$validate = function ($testtype) use ($self) {
if (empty($testtype) || !in_array($testtype, $self->getValidTypes())) {
throw new \InvalidArgumentException('You have to enter a valid test type: ' . implode(" or ", $self->getValidTypes()));
}
return $testtype;
};
if (empty($testtype)) {
$dialog = $this->getHelperSet()->get('dialog');
$testtype = $dialog->askAndValidate($output, 'Enter the type of the test to generate ('. implode(", ", $this->getValidTypes()).'): ', $validate, false, null, $this->getValidTypes());
} else {
$validate($testtype);
}
$testtype = ucfirst($testtype);
return $testtype;
}
/**
* @return array
*/
protected function getTestFilesWhitelist($testType)
{
if('Integration' == $testType) {
return array(
'/.gitignore',
'/tests',
'/tests/SimpleIntegrationTest.php',
'/tests/expected',
'/tests/expected/test___API.get_day.xml',
'/tests/expected/test___Goals.getItemsSku_day.xml',
'/tests/processed',
'/tests/processed/.gitignore',
'/tests/fixtures',
'/tests/fixtures/SimpleFixtureTrackFewVisits.php'
);
}
return array(
'/tests',
'/tests/SimpleTest.php'
);
}
}

View file

@ -0,0 +1,96 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class GenerateVisualizationPlugin extends GeneratePlugin
{
protected function configure()
{
$this->setName('generate:visualizationplugin')
->setDescription('Generates a new visualization plugin including all needed files')
->addOption('name', null, InputOption::VALUE_REQUIRED, 'Plugin name ([a-Z0-9_-])')
->addOption('visualizationname', null, InputOption::VALUE_REQUIRED, 'Visualization name ([a-Z0-9])')
->addOption('description', null, InputOption::VALUE_REQUIRED, 'Plugin description, max 150 characters')
->addOption('pluginversion', null, InputOption::VALUE_OPTIONAL, 'Plugin version')
->addOption('full', null, InputOption::VALUE_OPTIONAL, 'If a value is set, an API and a Controller will be created as well. Option is only available for creating plugins, not for creating themes.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$pluginName = $this->getPluginName($input, $output);
$description = $this->getPluginDescription($input, $output);
$version = $this->getPluginVersion($input, $output);
$visualizationName = $this->getVisualizationName($input, $output);
$this->generatePluginFolder($pluginName);
$exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExampleVisualization';
$replace = array(
'SimpleTable' => $visualizationName,
'simpleTable' => lcfirst($visualizationName),
'Simple Table' => $visualizationName,
'ExampleVisualization' => $pluginName,
'ExampleVisualizationDescription' => $description
);
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles = array());
$this->writeSuccessMessage($output, array(
sprintf('Visualization plugin %s %s generated.', $pluginName, $version),
'Enjoy!'
));
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return string
* @throws \RunTimeException
*/
private function getVisualizationName(InputInterface $input, OutputInterface $output)
{
$self = $this;
$validate = function ($visualizationName) use ($self) {
if (empty($visualizationName)) {
throw new \RunTimeException('You have to enter a visualization name');
}
if (!ctype_alnum($visualizationName)) {
throw new \RunTimeException(sprintf('The visualization name %s is not valid', $visualizationName));
}
return $visualizationName;
};
$visualizationName = $input->getOption('visualizationname');
if (empty($visualizationName)) {
$dialog = $this->getHelperSet()->get('dialog');
$visualizationName = $dialog->askAndValidate($output, 'Enter a visualization name: ', $validate);
} else {
$validate($visualizationName);
}
$visualizationName = ucfirst($visualizationName);
return $visualizationName;
}
}

View file

@ -0,0 +1,142 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class GitCommit extends ConsoleCommand
{
protected function configure()
{
$this->setName('git:commit')
->setDescription('Commit')
->addOption('message', 'm', InputOption::VALUE_REQUIRED, 'Commit Message');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$submodules = $this->getSubmodulePaths();
foreach ($submodules as $submodule) {
if (empty($submodule)) {
continue;
}
$status = $this->getStatusOfSubmodule($submodule);
if (false !== strpos($status, '?? ')) {
$output->writeln(sprintf('<error>%s has untracked files or folders. Delete or add them and try again.</error>', $submodule));
$output->writeln('<error>Status:</error>');
$output->writeln(sprintf('<comment>%s</comment>', $status));
return;
}
}
$commitMessage = $input->getOption('message');
if (empty($commitMessage)) {
$output->writeln('No message specified. Use option -m or --message.');
return;
}
if (!$this->hasChangesToBeCommitted()) {
$dialog = $this->getHelperSet()->get('dialog');
$question = '<question>There are no changes to be commited in the super repo, do you just want to commit and converge submodules?</question>';
if (!$dialog->askConfirmation($output, $question, false)) {
$output->writeln('<info>Cool, nothing done. Stage files using "git add" and try again.</info>');
return;
}
}
foreach ($submodules as $submodule) {
if (empty($submodule)) {
continue;
}
$status = $this->getStatusOfSubmodule($submodule);
if (empty($status)) {
$output->writeln(sprintf('%s has no changes, will ignore', $submodule));
continue;
}
$cmd = sprintf('cd %s/%s && git pull && git add . && git commit -am "%s"', PIWIK_DOCUMENT_ROOT, $submodule, $commitMessage);
$this->passthru($cmd, $output);
}
if ($this->hasChangesToBeCommitted()) {
$cmd = sprintf('cd %s && git commit -m "%s"', PIWIK_DOCUMENT_ROOT, $commitMessage);
$this->passthru($cmd, $output);
}
foreach ($submodules as $submodule) {
if (empty($submodule)) {
continue;
}
$cmd = sprintf('cd %s && git add %s', PIWIK_DOCUMENT_ROOT, $submodule);
$this->passthru($cmd, $output);
}
if ($this->hasChangesToBeCommitted()) {
$cmd = sprintf('cd %s && git commit -m "Updating submodules"', PIWIK_DOCUMENT_ROOT);
$this->passthru($cmd, $output);
}
}
private function passthru($cmd, OutputInterface $output)
{
$output->writeln('Executing command: ' . $cmd);
passthru($cmd);
}
private function hasChangesToBeCommitted()
{
$cmd = sprintf('cd %s && git status --porcelain', PIWIK_DOCUMENT_ROOT);
$result = shell_exec($cmd);
$result = trim($result);
if (false !== strpos($result, 'M ')) {
// stages
return true;
}
if (false !== strpos($result, 'MM ')) {
// staged and modified
return true;
}
return false;
}
/**
* @return array
*/
private function getSubmodulePaths()
{
$cmd = sprintf("grep path .gitmodules | sed 's/.*= //'");
$submodules = shell_exec($cmd);
$submodules = explode("\n", $submodules);
return $submodules;
}
protected function getStatusOfSubmodule($submodule)
{
$cmd = sprintf('cd %s/%s && git status --porcelain', PIWIK_DOCUMENT_ROOT, $submodule);
$status = trim(shell_exec($cmd));
return $status;
}
}

View file

@ -0,0 +1,55 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class GitPull extends ConsoleCommand
{
protected function configure()
{
$this->setName('git:pull');
$this->setDescription('Pull Piwik repo and all submodules');
}
protected function getBranchName()
{
$cmd = sprintf('cd %s && git rev-parse --abbrev-ref HEAD', PIWIK_DOCUMENT_ROOT);
$branch = shell_exec($cmd);
return trim($branch);
}
protected function execute(InputInterface $input, OutputInterface $output)
{
if ('master' != $this->getBranchName()) {
$output->writeln('<info>Doing nothing because you are not on the master branch in super repo.</info>');
return;
}
$cmd = sprintf('cd %s && git checkout master && git pull && git submodule update --init --recursive --remote', PIWIK_DOCUMENT_ROOT);
$this->passthru($cmd, $output);
$cmd = 'git submodule foreach "(git checkout master; git pull)&"';
$this->passthru($cmd, $output);
}
private function passthru($cmd, OutputInterface $output)
{
$output->writeln('Executing command: ' . $cmd);
passthru($cmd);
}
}

View file

@ -0,0 +1,43 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class GitPush extends ConsoleCommand
{
protected function configure()
{
$this->setName('git:push');
$this->setDescription('Push Piwik repo and all submodules');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$cmd = sprintf('cd %s && git push --recurse-submodules=on-demand', PIWIK_DOCUMENT_ROOT);
$output->writeln('Executing command: ' . $cmd);
passthru($cmd);
}
private function hasUnpushedCommits()
{
$cmd = sprintf('cd %s && git log @{u}..',PIWIK_DOCUMENT_ROOT);
$hasUnpushedCommits = shell_exec($cmd);
$hasUnpushedCommits = trim($hasUnpushedCommits);
return !empty($hasUnpushedCommits);
}
}

View file

@ -0,0 +1,68 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Plugin\Manager;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* core:plugin console command.
*/
class ManagePlugin extends ConsoleCommand
{
private $operations = array();
protected function configure()
{
$this->setName('core:plugin');
$this->setDescription("Perform various actions regarding one or more plugins.");
$this->addArgument("operation", InputArgument::REQUIRED, "Operation to apply (can be 'activate' or 'deactivate').");
$this->addArgument("plugins", InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'Plugin name(s) to activate.');
$this->addOption('domain', null, InputOption::VALUE_REQUIRED, "The domain to activate the plugin for.");
$this->operations['activate'] = 'activatePlugin';
$this->operations['deactivate'] = 'deactivatePlugin';
}
/**
* Execute command like: ./console cloudadmin:plugin activate CustomAlerts --piwik-domain=testcustomer.piwik.pro
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$operation = $input->getArgument("operation");
$plugins = $input->getArgument('plugins');
if (empty($this->operations[$operation])) {
throw new Exception("Invalid operation '$operation'.");
}
$fn = $this->operations[$operation];
foreach ($plugins as $plugin) {
call_user_func(array($this, $fn), $input, $output, $plugin);
}
}
private function activatePlugin(InputInterface $input, OutputInterface $output, $plugin)
{
Manager::getInstance()->activatePlugin($plugin, $input, $output);
$output->writeln("Activated plugin <info>$plugin</info>");
}
private function deactivatePlugin(InputInterface $input, OutputInterface $output, $plugin)
{
Manager::getInstance()->deactivatePlugin($plugin, $input, $output);
$output->writeln("Deactivated plugin <info>$plugin</info>");
}
}

View file

@ -0,0 +1,60 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class ManageTestFiles extends ConsoleCommand
{
protected function configure()
{
$this->setName('development:test-files');
$this->setDescription("Manage test files.");
$this->addArgument('operation', InputArgument::REQUIRED, 'The operation to apply. Supported operations include: '
. '"copy"');
$this->addOption('file', null, InputOption::VALUE_REQUIRED, "The file (or files) to apply the operation to.");
// TODO: allow copying by regex pattern
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$operation = $input->getArgument('operation');
if ($operation == 'copy') {
$this->copy($input, $output);
} else {
throw new \Exception("Invalid operation '$operation'.");
}
}
private function copy($input, $output)
{
$file = $input->getOption('file');
$prefix = PIWIK_INCLUDE_PATH . '/tests/PHPUnit/Integration/processed/';
$guesses = array(
'/' . $file,
$prefix . $file,
$prefix . $file . '.xml'
);
foreach ($guesses as $guess) {
if (is_file($guess)) {
$file = $guess;
}
}
copy($file, PIWIK_INCLUDE_PATH . '/tests/PHPUnit/Integration/expected/' . basename($file));
}
}

View file

@ -0,0 +1,87 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class RunTests extends ConsoleCommand
{
protected function configure()
{
$this->setName('tests:run');
$this->setDescription('Run Piwik PHPUnit tests one group after the other');
$this->addArgument('group', InputArgument::OPTIONAL, 'Run only a specific test group. Separate multiple groups by comma, for instance core,integration', '');
$this->addOption('options', 'o', InputOption::VALUE_OPTIONAL, 'All options will be forwarded to phpunit', '');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$options = $input->getOption('options');
$groups = $input->getArgument('group');
$groups = explode(",", $groups);
$groups = array_map('ucfirst', $groups);
$groups = array_filter($groups, 'strlen');
$command = 'phpunit';
// force xdebug usage for coverage options
if (false !== strpos($options, '--coverage') && !extension_loaded('xdebug')) {
$output->writeln('<info>xdebug extension required for code coverage.</info>');
$output->writeln('<info>searching for xdebug extension...</info>');
$extensionDir = shell_exec('php-config --extension-dir');
$xdebugFile = trim($extensionDir) . DIRECTORY_SEPARATOR . 'xdebug.so';
if (!file_exists($xdebugFile)) {
$dialog = $this->getHelperSet()->get('dialog');
$xdebugFile = $dialog->askAndValidate($output, 'xdebug not found. Please provide path to xdebug.so', function($xdebugFile) {
return file_exists($xdebugFile);
});
} else {
$output->writeln('<info>xdebug extension found in extension path.</info>');
}
$output->writeln("<info>using $xdebugFile as xdebug extension.</info>");
$phpunitPath = trim(shell_exec('which phpunit'));
$command = sprintf('php -d zend_extension=%s %s', $xdebugFile, $phpunitPath);
}
if(empty($groups)) {
$groups = $this->getTestsGroups();
}
foreach($groups as $group) {
$params = '--group ' . $group . ' ' . str_replace('%group%', $group, $options);
$cmd = sprintf('cd %s/tests/PHPUnit && %s %s', PIWIK_DOCUMENT_ROOT, $command, $params);
$output->writeln('Executing command: <info>' . $cmd . '</info>');
passthru($cmd);
$output->writeln("");
}
}
private function getTestsGroups()
{
return array('Core', 'Plugins', 'Integration', 'UI');
}
}

View file

@ -0,0 +1,70 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class RunUITests extends ConsoleCommand
{
protected function configure()
{
$this->setName('tests:run-ui');
$this->setDescription('Run screenshot tests');
$this->addArgument('specs', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Run only a specific test spec. Separate multiple specs by comma, for instance core,integration', array());
$this->addOption("persist-fixture-data", null, InputOption::VALUE_NONE, "Persist test data in a database and do not execute tear down.");
$this->addOption('keep-symlinks', null, InputOption::VALUE_NONE, "Keep recursive directory symlinks so test pages can be viewed in a browser.");
$this->addOption('print-logs', null, InputOption::VALUE_NONE, "Print webpage logs even if tests succeed.");
$this->addOption('drop', null, InputOption::VALUE_NONE, "Drop the existing database and re-setup a persisted fixture.");
$this->addOption('plugin', null, InputOption::VALUE_REQUIRED, "Execute all tests for a plugin.");
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$specs = $input->getArgument('specs');
$persistFixtureData = $input->getOption("persist-fixture-data");
$keepSymlinks = $input->getOption('keep-symlinks');
$printLogs = $input->getOption('print-logs');
$drop = $input->getOption('drop');
$plugin = $input->getOption('plugin');
$options = array();
if ($persistFixtureData) {
$options[] = "--persist-fixture-data";
}
if ($keepSymlinks) {
$options[] = "--keep-symlinks";
}
if ($printLogs) {
$options[] = "--print-logs";
}
if ($drop) {
$options[] = "--drop";
}
if ($plugin) {
$options[] = "--plugin=" . $plugin;
}
$options = implode(" ", $options);
$specs = implode(" ", $specs);
$cmd = "phantomjs '" . PIWIK_INCLUDE_PATH . "/tests/lib/screenshot-testing/run-tests.js' $options $specs";
$output->writeln('Executing command: <info>' . $cmd . '</info>');
$output->writeln('');
passthru($cmd);
}
}

View file

@ -0,0 +1,156 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Url;
use Piwik\Piwik;
use Piwik\Config;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Console commands that sets up a fixture either in a local MySQL database or a remote one.
*
* TODO: use this console command in UI tests instead of setUpDatabase.php/tearDownDatabase.php scripts
*/
class SetupFixture extends ConsoleCommand
{
protected function configure()
{
$this->setName('tests:setup-fixture');
$this->setDescription('Create a database and fill it with data using a Piwik test fixture.');
$this->addArgument('fixture', InputArgument::REQUIRED,
"The class name of the fixture to apply. Doesn't need to have a namespace if it exists in the " .
"Piwik\\Tests\\Fixtures namespace.");
$this->addOption('db-name', null, InputOption::VALUE_REQUIRED,
"The name of the database that will contain the fixture data. This option is required to be set.");
$this->addOption('file', null, InputOption::VALUE_REQUIRED,
"The file location of the fixture. If this option is included the file will be required explicitly.");
$this->addOption('db-host', null, InputOption::VALUE_REQUIRED,
"The hostname of the MySQL database to use. Uses the default config value if not specified.");
$this->addOption('db-user', null, InputOption::VALUE_REQUIRED,
"The name of the MySQL user to use. Uses the default config value if not specified.");
$this->addOption('db-pass', null, InputOption::VALUE_REQUIRED,
"The MySQL user password to use. Uses the default config value if not specified.");
$this->addOption('teardown', null, InputOption::VALUE_NONE,
"If specified, the fixture will be torn down and the database deleted. Won't work if the --db-name " .
"option isn't supplied.");
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$dbName = $input->getOption('db-name');
if (!$dbName) {
throw new \Exception("Required argument --db-name is not set.");
}
$this->requireFixtureFiles();
$this->setIncludePathAsInTestBootstrap();
$file = $input->getOption('file');
if ($file) {
if (is_file($file)) {
require_once $file;
} else if (is_file(PIWIK_INCLUDE_PATH . '/' . $file)) {
require_once PIWIK_INCLUDE_PATH . '/' . $file;
} else {
throw new \Exception("Cannot find --file option file '$file'.");
}
}
$host = Url::getHost();
if (empty($host)) {
Url::setHost('localhost');
}
// get the fixture class
$fixtureClass = $input->getArgument('fixture');
if (class_exists("Piwik\\Tests\\Fixtures\\" . $fixtureClass)) {
$fixtureClass = "Piwik\\Tests\\Fixtures\\" . $fixtureClass;
}
if (!class_exists($fixtureClass)) {
throw new \Exception("Cannot find fixture class '$fixtureClass'.");
}
// create the fixture
$fixture = new $fixtureClass();
$fixture->dbName = $dbName;
$fixture->printToScreen = true;
Config::getInstance()->setTestEnvironment();
$fixture->createConfig = false;
// setup database overrides
$testingEnvironment = $fixture->getTestEnvironment();
$optionsToOverride = array(
'dbname' => $dbName,
'host' => $input->getOption('db-host'),
'user' => $input->getOption('db-user'),
'password' => $input->getOption('db-pass')
);
foreach ($optionsToOverride as $configOption => $value) {
if ($value) {
$configOverride = $testingEnvironment->configOverride;
$configOverride['database_tests'][$configOption] = $configOverride['database'][$configOption] = $value;
$testingEnvironment->configOverride = $configOverride;
Config::getInstance()->database[$configOption] = $value;
}
}
// perform setup and/or teardown
if ($input->getOption('teardown')) {
$testingEnvironment->save();
$fixture->performTearDown();
} else {
$fixture->performSetUp();
}
$this->writeSuccessMessage($output, array("Fixture successfully setup!"));
}
private function requireFixtureFiles()
{
require_once "PHPUnit/Autoload.php";
require_once PIWIK_INCLUDE_PATH . '/libs/PiwikTracker/PiwikTracker.php';
require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/FakeAccess.php';
require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/TestingEnvironment.php';
require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/Fixture.php';
$fixturesToLoad = array(
'/tests/PHPUnit/Fixtures/*.php',
'/tests/PHPUnit/UI/Fixtures/*.php',
);
foreach($fixturesToLoad as $fixturePath) {
foreach (glob(PIWIK_INCLUDE_PATH . $fixturePath) as $file) {
require_once $file;
}
}
}
private function setIncludePathAsInTestBootstrap()
{
if (!defined('PIWIK_INCLUDE_SEARCH_PATH')) {
define('PIWIK_INCLUDE_SEARCH_PATH', get_include_path()
. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/core'
. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/libs'
. PATH_SEPARATOR . PIWIK_INCLUDE_PATH . '/plugins');
}
@ini_set('include_path', PIWIK_INCLUDE_SEARCH_PATH);
@set_include_path(PIWIK_INCLUDE_SEARCH_PATH);
}
}

View file

@ -0,0 +1,81 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Http;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class SyncUITestScreenshots extends ConsoleCommand
{
protected function configure()
{
$this->setName('development:sync-ui-test-screenshots');
$this->setDescription('For Piwik core devs. Copies screenshots '
. 'from travis artifacts to tests/PHPUnit/UI/expected-ui-screenshots/');
$this->addArgument('buildnumber', InputArgument::REQUIRED, 'Travis build number you want to sync.');
$this->addArgument('screenshotsRegex', InputArgument::OPTIONAL,
'A regex to use when selecting screenshots to copy. If not supplied all screenshots are copied.', '.*');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$buildNumber = $input->getArgument('buildnumber');
$screenshotsRegex = $input->getArgument('screenshotsRegex');
if (empty($buildNumber)) {
throw new \InvalidArgumentException('Missing build number.');
}
$urlBase = sprintf('http://builds-artifacts.piwik.org/ui-tests.master/%s', $buildNumber);
$diffviewer = Http::sendHttpRequest($urlBase . "/screenshot-diffs/diffviewer.html", $timeout = 60);
$dom = new \DOMDocument();
$dom->loadHTML($diffviewer);
foreach ($dom->getElementsByTagName("tr") as $row) {
$columns = $row->getElementsByTagName("td");
$nameColumn = $columns->item(0);
$processedColumn = $columns->item(2);
$testPlugin = null;
if ($nameColumn
&& preg_match("/\(for ([a-zA-Z_]+) plugin\)/", $dom->saveXml($nameColumn), $matches)
) {
$testPlugin = $matches[1];
}
$file = null;
if ($processedColumn
&& preg_match("/href=\".*\/(.*)\"/", $dom->saveXml($processedColumn), $matches)
) {
$file = $matches[1];
}
if ($file !== null
&& preg_match("/" . $screenshotsRegex . "/", $file)
) {
if ($testPlugin == null) {
$downloadTo = "tests/PHPUnit/UI/expected-ui-screenshots/$file";
} else {
$downloadTo = "plugins/$testPlugin/tests/UI/expected-ui-screenshots/$file";
}
$output->write("<info>Downloading $file to .$downloadTo...</info>\n");
Http::sendHttpRequest("$urlBase/processed-ui-screenshots/$file", $timeout = 60, $userAgent = null,
PIWIK_DOCUMENT_ROOT . "/" . $downloadTo);
}
}
}
}

View file

@ -0,0 +1,33 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Plugin\ConsoleCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
*/
class WatchLog extends ConsoleCommand
{
protected function configure()
{
$this->setName('log:watch');
$this->setDescription('Outputs the last parts of the log files and follows as the log file grows. Does not work on Windows');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$cmd = sprintf('tail -f %s/tmp/logs/*.log', PIWIK_DOCUMENT_ROOT);
$output->writeln('Executing command: ' . $cmd);
passthru($cmd);
}
}