PDF rausgenommen
This commit is contained in:
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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;
|
||||
|
||||
/**
|
||||
*/
|
||||
class ClearCaches extends ConsoleCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('core:clear-caches');
|
||||
$this->setAliases(array('cache:clear'));
|
||||
$this->setDescription('Cleares all caches. This command can be useful for instance after updating Piwik files manually.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute command like: ./console core:clear-caches
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// Note: the logic for this command must be refactored in this helper function below.
|
||||
Filesystem::deleteAllCacheOnUpdate();
|
||||
|
||||
$this->writeSuccessMessage($output, array('Caches cleared'));
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 Piwik\Site;
|
||||
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)
|
||||
{
|
||||
$archiver = self::makeArchiver($input->getOption('url'), $input);
|
||||
$archiver->main();
|
||||
}
|
||||
|
||||
// also used by another console command
|
||||
public static function makeArchiver($url, InputInterface $input)
|
||||
{
|
||||
$archiver = new CronArchive();
|
||||
|
||||
$archiver->disableScheduledTasks = $input->getOption('disable-scheduled-tasks');
|
||||
$archiver->acceptInvalidSSLCertificate = $input->getOption("accept-invalid-ssl-certificate");
|
||||
$archiver->shouldArchiveAllSites = (bool) $input->getOption("force-all-websites");
|
||||
$archiver->shouldStartProfiler = (bool) $input->getOption("xhprof");
|
||||
$archiver->shouldArchiveSpecifiedSites = self::getSitesListOption($input, "force-idsites");
|
||||
$archiver->shouldSkipSpecifiedSites = self::getSitesListOption($input, "skip-idsites");
|
||||
$archiver->forceTimeoutPeriod = $input->getOption("force-timeout-for-periods");
|
||||
$archiver->shouldArchiveAllPeriodsSince = $input->getOption("force-all-periods");
|
||||
$archiver->restrictToDateRange = $input->getOption("force-date-range");
|
||||
$archiver->phpCliConfigurationOptions = $input->getOption("php-cli-options");
|
||||
|
||||
$restrictToPeriods = $input->getOption("force-periods");
|
||||
$restrictToPeriods = explode(',', $restrictToPeriods);
|
||||
$archiver->restrictToPeriods = array_map('trim', $restrictToPeriods);
|
||||
|
||||
$archiver->dateLastForced = $input->getOption('force-date-last-n');
|
||||
$archiver->concurrentRequestsPerWebsite = $input->getOption('concurrent-requests-per-website');
|
||||
$archiver->maxConcurrentArchivers = $input->getOption('concurrent-archivers');
|
||||
|
||||
$archiver->disableSegmentsArchiving = $input->getOption('skip-all-segments');
|
||||
|
||||
$segmentIds = $input->getOption('force-idsegments');
|
||||
$segmentIds = explode(',', $segmentIds);
|
||||
$segmentIds = array_map('trim', $segmentIds);
|
||||
$archiver->setSegmentsToForceFromSegmentIds($segmentIds);
|
||||
|
||||
$archiver->setUrlToPiwik($url);
|
||||
|
||||
return $archiver;
|
||||
}
|
||||
|
||||
private static function getSitesListOption(InputInterface $input, $optionName)
|
||||
{
|
||||
return Site::getIdSitesFromIdSitesString($input->getOption($optionName));
|
||||
}
|
||||
|
||||
// This is reused by another console command
|
||||
public static 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 without any option.
|
||||
* 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 feedback@matomo.org
|
||||
* Enjoy!");
|
||||
$command->addOption('url', null, InputOption::VALUE_REQUIRED,
|
||||
"Forces the value of this option to be used as the URL to Piwik. \nIf your system does not support"
|
||||
. " archiving with CLI processes, you may need to set this in order for the archiving HTTP requests to use"
|
||||
. " the desired URLs.");
|
||||
$command->addOption('force-all-websites', null, InputOption::VALUE_NONE,
|
||||
"If specified, the script will trigger archiving on all websites.\nUse with --force-all-periods=[seconds] "
|
||||
. "to also process those websites that had visits in the last [seconds] seconds.\nLaunching several processes"
|
||||
. " with this option will make them share the list of sites to process.");
|
||||
$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
|
||||
. " 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('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('skip-all-segments', null, InputOption::VALUE_NONE,
|
||||
'If specified, all segments will be skipped during archiving.');
|
||||
$command->addOption('force-idsites', null, InputOption::VALUE_OPTIONAL,
|
||||
'If specified, archiving will be processed only for these Sites Ids (comma separated)');
|
||||
$command->addOption('force-periods', null, InputOption::VALUE_OPTIONAL,
|
||||
"If specified, archiving will be processed only for these Periods (comma separated eg. day,week,month,year,range)");
|
||||
$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-date-range', null, InputOption::VALUE_OPTIONAL,
|
||||
"If specified, archiving will be processed only for periods included in this date range. Format: YYYY-MM-DD,YYYY-MM-DD");
|
||||
$command->addOption('force-idsegments', null, InputOption::VALUE_REQUIRED,
|
||||
'If specified, only these segments will be processed (if the segment should be applied to a site in the first place).'
|
||||
. "\nSpecify stored segment IDs, not the segments themselves, eg, 1,2,3. "
|
||||
. "\nNote: if identical segments exist w/ different IDs, they will both be skipped, even if you only supply one ID.");
|
||||
$command->addOption('concurrent-requests-per-website', null, InputOption::VALUE_OPTIONAL,
|
||||
"When processing a website and its segments, number of requests to process in parallel", CronArchive::MAX_CONCURRENT_API_REQUESTS);
|
||||
$command->addOption('concurrent-archivers', null, InputOption::VALUE_OPTIONAL,
|
||||
"The number of max archivers to run in parallel. Depending on how you start the archiver as a cronjob, you may need to double the amount of archivers allowed if the same process appears twice in the `ps ex` output.", false);
|
||||
$command->addOption('disable-scheduled-tasks', null, InputOption::VALUE_NONE,
|
||||
"Skips executing Scheduled tasks (sending scheduled reports, db optimization, etc.).");
|
||||
$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");
|
||||
$command->addOption('php-cli-options', null, InputOption::VALUE_OPTIONAL, 'Forwards the PHP configuration options to the PHP CLI command. For example "-d memory_limit=8G". Note: These options are only applied if the archiver actually uses CLI and not HTTP.', $default = '');
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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\Config;
|
||||
use Piwik\Filesystem;
|
||||
use Piwik\Plugin\ConsoleCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
*/
|
||||
class DevelopmentEnable extends ConsoleCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('development:enable');
|
||||
$this->setAliases(array('development:disable'));
|
||||
$this->setDescription('Enable or disable development mode. See config/global.ini.php in section [Development] for more information');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$commandName = $input->getFirstArgument();
|
||||
$enable = (false !== strpos($commandName, 'enable'));
|
||||
|
||||
$config = Config::getInstance();
|
||||
$development = $config->Development;
|
||||
|
||||
if ($enable) {
|
||||
$development['enabled'] = 1;
|
||||
$development['disable_merged_assets'] = 1;
|
||||
$message = 'Development mode enabled';
|
||||
} else {
|
||||
$development['enabled'] = 0;
|
||||
$development['disable_merged_assets'] = 0;
|
||||
$message = 'Development mode disabled';
|
||||
}
|
||||
|
||||
$config->Development = $development;
|
||||
$config->forceSave();
|
||||
|
||||
Filesystem::deleteAllCacheOnUpdate();
|
||||
|
||||
$this->writeSuccessMessage($output, array($message));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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\Development;
|
||||
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 DevelopmentManageTestFiles extends ConsoleCommand
|
||||
{
|
||||
public function isEnabled()
|
||||
{
|
||||
return Development::isEnabled();
|
||||
}
|
||||
|
||||
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/System/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/System/expected/' . basename($file));
|
||||
}
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 Piwik\Container\StaticContainer;
|
||||
use Piwik\Decompress\Tar;
|
||||
use Piwik\Development;
|
||||
use Piwik\Filesystem;
|
||||
use Piwik\Http;
|
||||
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 DevelopmentSyncProcessedSystemTests extends ConsoleCommand
|
||||
{
|
||||
public function isEnabled()
|
||||
{
|
||||
return Development::isEnabled();
|
||||
}
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('development:sync-system-test-processed');
|
||||
$this->setDescription('For Piwik core devs. Copies processed system tests from travis artifacts to local processed directories');
|
||||
$this->addArgument('buildnumber', InputArgument::REQUIRED, 'Travis build number you want to sync, eg "14820".');
|
||||
$this->addOption('expected', 'e', InputOption::VALUE_NONE, 'If given file will be copied in expected directories instead of processed');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->updateCoreFiles($input, $output);
|
||||
$this->updatePluginsFiles($input, $output);
|
||||
}
|
||||
|
||||
protected function updateCoreFiles(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$buildNumber = $input->getArgument('buildnumber');
|
||||
$expected = $input->getOption('expected');
|
||||
$targetDir = sprintf(PIWIK_INCLUDE_PATH . '/tests/PHPUnit/System/%s', $expected ? 'expected' : 'processed');
|
||||
$tmpDir = StaticContainer::get('path.tmp') . '/';
|
||||
|
||||
$this->validate($buildNumber, $targetDir, $tmpDir);
|
||||
|
||||
if (Common::stringEndsWith($buildNumber, '.1')) {
|
||||
// eg make '14820.1' to '14820' to be backwards compatible
|
||||
$buildNumber = substr($buildNumber, 0, -2);
|
||||
}
|
||||
|
||||
$filename = sprintf('system.%s.tar.bz2', $buildNumber);
|
||||
$urlBase = sprintf('https://builds-artifacts.matomo.org/matomo-org/matomo/%s', $filename);
|
||||
$tests = Http::sendHttpRequest($urlBase, $timeout = 120);
|
||||
|
||||
$tarFile = $tmpDir . $filename;
|
||||
file_put_contents($tarFile, $tests);
|
||||
|
||||
$tar = new Tar($tarFile, 'bz2');
|
||||
|
||||
if ($tar->extract($targetDir)) {
|
||||
$this->writeSuccessMessage($output, array(
|
||||
'All processed system test results were copied to <comment>' . $targetDir . '</comment>',
|
||||
'Compare them with the expected test results and commit them if needed.'
|
||||
));
|
||||
} else {
|
||||
$output->write('<error>' . $tar->errorInfo() . '.</error>');
|
||||
$output->writeln('<error> Check that you have the PHP bz2 extension installed and try again.');
|
||||
}
|
||||
|
||||
unlink($tarFile);
|
||||
}
|
||||
|
||||
|
||||
protected function updatePluginsFiles(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$buildNumber = $input->getArgument('buildnumber');
|
||||
$expected = $input->getOption('expected');
|
||||
$targetDir = sprintf(PIWIK_INCLUDE_PATH . '/plugins/%%s/tests/System/%s/', $expected ? 'expected' : 'processed');
|
||||
$tmpDir = StaticContainer::get('path.tmp') . '/';
|
||||
|
||||
if (Common::stringEndsWith($buildNumber, '.1')) {
|
||||
// eg make '14820.1' to '14820' to be backwards compatible
|
||||
$buildNumber = substr($buildNumber, 0, -2);
|
||||
}
|
||||
|
||||
$filename = sprintf('system.plugin.%s.tar.bz2', $buildNumber);
|
||||
$urlBase = sprintf('https://builds-artifacts.matomo.org/matomo-org/matomo/%s', $filename);
|
||||
$tests = Http::sendHttpRequest($urlBase, $timeout = 120);
|
||||
|
||||
$tarFile = $tmpDir . $filename;
|
||||
file_put_contents($tarFile, $tests);
|
||||
|
||||
$tar = new Tar($tarFile, 'bz2');
|
||||
|
||||
$extractionTarget = $tmpDir . '/artifacts';
|
||||
|
||||
Filesystem::mkdir($extractionTarget);
|
||||
|
||||
$success = $tar->extract($extractionTarget);
|
||||
if (! $success) {
|
||||
$output->write('<error>' . $tar->errorInfo() . '.</error>');
|
||||
$output->writeln('<error> Check that you have the PHP bz2 extension installed and try again.');
|
||||
unlink($tarFile);
|
||||
return;
|
||||
}
|
||||
|
||||
$artifacts = Filesystem::globr($extractionTarget, '*~~*');
|
||||
|
||||
foreach($artifacts as $artifact) {
|
||||
$artifactName = basename($artifact);
|
||||
list($plugin, $file) = explode('~~', $artifactName);
|
||||
$pluginTargetDir = sprintf($targetDir, $plugin);
|
||||
Filesystem::mkdir($pluginTargetDir);
|
||||
Filesystem::copy($artifact, $pluginTargetDir . $file);
|
||||
}
|
||||
|
||||
Filesystem::unlinkRecursive($extractionTarget, true);
|
||||
|
||||
$this->writeSuccessMessage($output, array(
|
||||
'All processed plugin system test results were copied to <comment>' . $targetDir . '</comment>',
|
||||
'Compare them with the expected test results and commit them if needed.'
|
||||
));
|
||||
|
||||
unlink($tarFile);
|
||||
}
|
||||
|
||||
private function validate($buildNumber, $targetDir, $tmpDir)
|
||||
{
|
||||
if (empty($buildNumber)) {
|
||||
throw new \InvalidArgumentException('Missing build number.');
|
||||
}
|
||||
|
||||
if (!is_writable($targetDir)) {
|
||||
throw new \RuntimeException('Target dir is not writable: ' . $targetDir);
|
||||
}
|
||||
|
||||
if (!is_writable($tmpDir)) {
|
||||
throw new \RuntimeException('Tempdir is not writable: ' . $tmpDir);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class GenerateAngularComponent extends GenerateAngularConstructBase
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('generate:angular-component')
|
||||
->setDescription('Generates a template for an AngularJS component')
|
||||
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin')
|
||||
->addOption('component', null, InputOption::VALUE_REQUIRED, 'The name of the component you want to create.');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$pluginName = $this->getPluginName($input, $output);
|
||||
$component = $this->getConstructName($input, $output, $optionName = 'component', $constructType = 'component');
|
||||
$pluginPath = $this->getPluginPath($pluginName);
|
||||
|
||||
$componentLower = $this->getSnakeCaseName($component);
|
||||
|
||||
$targetDir = $pluginPath . '/angularjs/' . $componentLower;
|
||||
|
||||
if (is_dir($targetDir) || file_exists($targetDir)) {
|
||||
throw new \Exception('The AngularJS component ' . $componentLower . ' already exists in plugin '
|
||||
. $pluginName);
|
||||
}
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('ExamplePlugin');
|
||||
$replace = array(
|
||||
'ExamplePlugin' => $pluginName,
|
||||
'example-component' => $componentLower,
|
||||
'componentClass' => lcfirst($component),
|
||||
'componentAs' => lcfirst($component),
|
||||
'Component' => $component,
|
||||
);
|
||||
|
||||
$componentPath = '/angularjs/example-component';
|
||||
|
||||
$whitelistFiles = array(
|
||||
'/angularjs',
|
||||
$componentPath,
|
||||
$componentPath . '/example-component.component.html',
|
||||
$componentPath . '/example-component.component.js',
|
||||
$componentPath . '/example-component.component.less',
|
||||
);
|
||||
|
||||
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
|
||||
|
||||
$replacedBasePath = '/angularjs/' . $componentLower . '/' . $componentLower;
|
||||
$js1 = $replacedBasePath . '.component.js';
|
||||
$less1 = $replacedBasePath . '.component.less';
|
||||
|
||||
$this->writeSuccessMessage($output, array(
|
||||
sprintf('AngularJS directive "%s" for plugin "%s" in "%s" generated', $component, $pluginName, $targetDir),
|
||||
sprintf('In <comment>%1$s/%2$s.php</comment> you should now require the JS files', $pluginPath, $pluginName),
|
||||
sprintf('<comment>%1$s%2$s</comment>', $pluginPath, $js1),
|
||||
sprintf('and the less file <comment>%1$s%2$s</comment>.', $pluginPath, $less1),
|
||||
'If you are not familiar with this have a look at <comment>https://developer.matomo.org/guides/working-with-piwiks-ui</comment>'
|
||||
));
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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\Output\OutputInterface;
|
||||
|
||||
abstract class GenerateAngularConstructBase extends GeneratePluginBase
|
||||
{
|
||||
/**
|
||||
* Convert MyComponentName => my-component-name
|
||||
* @param string $directiveCamelCase
|
||||
* @return string
|
||||
*/
|
||||
protected function getSnakeCaseName($camelCase)
|
||||
{
|
||||
return strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $camelCase));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @param string $optionName the name of the option to use.
|
||||
* @param string $constructType 'directive', 'component', etc.
|
||||
* @return string
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getConstructName(InputInterface $input, OutputInterface $output, $optionName, $constructType)
|
||||
{
|
||||
$testname = $input->getOption($optionName);
|
||||
|
||||
$validate = function ($testname) use ($constructType) {
|
||||
if (empty($testname)) {
|
||||
throw new \InvalidArgumentException("You have to enter a name for the $constructType");
|
||||
}
|
||||
|
||||
if (!ctype_alnum($testname)) {
|
||||
throw new \InvalidArgumentException("Only alphanumeric characters are allowed as a $constructType "
|
||||
. "name. Use CamelCase if the name of your $constructType contains multiple words.");
|
||||
}
|
||||
|
||||
return $testname;
|
||||
};
|
||||
|
||||
if (empty($testname)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$testname = $dialog->askAndValidate($output, "Enter the name of the $constructType you want to create: ",
|
||||
$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);
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
*/
|
||||
class GenerateAngularDirective extends GenerateAngularConstructBase
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('generate:angular-directive')
|
||||
->setDescription('Generates a template for an AngularJS directive')
|
||||
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin')
|
||||
->addOption('directive', null, InputOption::VALUE_REQUIRED, 'The name of the directive you want to create.');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$pluginName = $this->getPluginName($input, $output);
|
||||
$directive = $this->getConstructName($input, $output, $optionName = 'directive', $constructType = 'directive');
|
||||
$pluginPath = $this->getPluginPath($pluginName);
|
||||
|
||||
$directiveLower = $this->getSnakeCaseName($directive);
|
||||
|
||||
$targetDir = $pluginPath . '/angularjs/' . $directiveLower;
|
||||
|
||||
if (is_dir($targetDir) || file_exists($targetDir)) {
|
||||
throw new \Exception('The AngularJS directive ' . $directiveLower . ' already exists in plugin ' . $pluginName);
|
||||
}
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('ExamplePlugin');
|
||||
$replace = array(
|
||||
'ExamplePlugin' => $pluginName,
|
||||
'directive-component' => $directiveLower,
|
||||
'componentClass' => lcfirst($directive),
|
||||
'componentAs' => lcfirst($directive),
|
||||
'component' => $directiveLower,
|
||||
'Component' => $directive
|
||||
);
|
||||
|
||||
$componentPath = '/angularjs/directive-component';
|
||||
|
||||
$whitelistFiles = array(
|
||||
'/angularjs',
|
||||
$componentPath,
|
||||
$componentPath . '/component.controller.js',
|
||||
$componentPath . '/component.directive.html',
|
||||
$componentPath . '/component.directive.js',
|
||||
$componentPath . '/component.directive.less',
|
||||
);
|
||||
|
||||
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
|
||||
|
||||
$replacedBasePath = '/angularjs/' . $directiveLower . '/' . $directiveLower;
|
||||
$js1 = $replacedBasePath . '.controller.js';
|
||||
$js2 = $replacedBasePath . '.directive.js';
|
||||
$less1 = $replacedBasePath . '.directive.less';
|
||||
|
||||
$this->writeSuccessMessage($output, array(
|
||||
sprintf('AngularJS directive "%s" for plugin "%s" in "%s" generated', $directive, $pluginName, $targetDir),
|
||||
sprintf('In <comment>%1$s/%2$s.php</comment> you should now require the JS files', $pluginPath, $pluginName),
|
||||
sprintf('<comment>%1$s%2$s</comment>, <comment>%1$s%3$s</comment>', $pluginPath, $js1, $js2),
|
||||
sprintf('and the less file <comment>%1$s%2$s</comment>.', $pluginPath, $less1),
|
||||
'If you are not familiar with this have a look at <comment>https://developer.matomo.org/guides/working-with-piwiks-ui</comment>'
|
||||
));
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 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);
|
||||
$this->checkAndUpdateRequiredPiwikVersion($pluginName, $output);
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
*/
|
||||
class GenerateArchiver extends GeneratePluginBase
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('generate:archiver')
|
||||
->setDescription('Adds an Archiver to an existing plugin')
|
||||
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have an Archiver yet');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$pluginName = $this->getPluginName($input, $output);
|
||||
$this->checkAndUpdateRequiredPiwikVersion($pluginName, $output);
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('ExamplePlugin');
|
||||
$replace = array('ExamplePlugin' => $pluginName, 'EXAMPLEPLUGIN' => strtoupper($pluginName));
|
||||
$whitelistFiles = array('/Archiver.php');
|
||||
|
||||
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
|
||||
|
||||
$this->writeSuccessMessage($output, array(
|
||||
sprintf('Archiver.php for %s generated.', $pluginName),
|
||||
'You can now start implementing Archiver methods',
|
||||
'Enjoy!'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getPluginName(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$pluginNames = $this->getPluginNamesHavingNotSpecificFile('Archiver.php');
|
||||
$invalidName = 'You have to enter the name of an existing plugin which does not already have an Archiver';
|
||||
|
||||
return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 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);
|
||||
$this->checkAndUpdateRequiredPiwikVersion($pluginName, $output);
|
||||
|
||||
$commandName = $this->getCommandName($input, $output);
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('ExampleCommand');
|
||||
$replace = array(
|
||||
'ExampleCommandDescription' => $commandName,
|
||||
'ExampleCommand' => $pluginName,
|
||||
'examplecommand:helloworld' => strtolower($pluginName) . ':' . $this->buildCommandName($commandName),
|
||||
'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)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert MyComponentName => my-component-name
|
||||
* @param string $commandNameCamelCase
|
||||
* @return string
|
||||
*/
|
||||
protected function buildCommandName($commandNameCamelCase)
|
||||
{
|
||||
return strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $commandNameCamelCase));
|
||||
}
|
||||
/**
|
||||
* @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');
|
||||
}
|
||||
|
||||
if (!ctype_alnum($testname)) {
|
||||
throw new \InvalidArgumentException('Only alphanumeric characters are allowed as a command name. Use CamelCase if the name of your command contains multiple words.');
|
||||
}
|
||||
|
||||
return $testname;
|
||||
};
|
||||
|
||||
if (empty($testname)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$testname = $dialog->askAndValidate($output, 'Enter the name of the command (CamelCase): ', $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);
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 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);
|
||||
$this->checkAndUpdateRequiredPiwikVersion($pluginName, $output);
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,260 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 Piwik\DbHelper;
|
||||
use Piwik\Plugin\Manager;
|
||||
use Piwik\Plugin\Report;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class GenerateDimension extends GeneratePluginBase
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('generate:dimension')
|
||||
->setDescription('Adds a new dimension to an existing plugin. This allows you to persist new values during tracking.')
|
||||
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have a menu defined yet')
|
||||
->addOption('type', null, InputOption::VALUE_REQUIRED, 'Whether you want to create a "Visit", an "Action" or a "Conversion" dimension')
|
||||
->addOption('dimensionname', null, InputOption::VALUE_REQUIRED, 'A human readable name of the dimension which will be for instance visible in the UI')
|
||||
->addOption('columnname', null, InputOption::VALUE_REQUIRED, 'The name of the column in the MySQL database the dimension will be stored under')
|
||||
->addOption('columntype', null, InputOption::VALUE_REQUIRED, 'The MySQL type for your dimension, for instance "VARCHAR(255) NOT NULL".');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$pluginName = $this->getPluginName($input, $output);
|
||||
$this->checkAndUpdateRequiredPiwikVersion($pluginName, $output);
|
||||
|
||||
$type = $this->getDimensionType($input, $output);
|
||||
$dimensionName = $this->getDimensionName($input, $output);
|
||||
|
||||
if ('non-tracking-dimension' === $type) {
|
||||
$columnName = '';
|
||||
$columType = '';
|
||||
} else {
|
||||
$columnName = $this->getColumnName($input, $output, $type);
|
||||
$columType = $this->getColumnType($input, $output);
|
||||
}
|
||||
|
||||
$dimensionClassName = $this->getDimensionClassName($dimensionName);
|
||||
$translatedDimensionName = $this->makeTranslationIfPossible($pluginName, ucfirst($dimensionName));
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('ExampleTracker');
|
||||
$replace = array('example_action_dimension' => strtolower($columnName),
|
||||
'example_visit_dimension' => strtolower($columnName),
|
||||
'example_conversion_dimension' => strtolower($columnName),
|
||||
'INTEGER(11) DEFAULT 0 NOT NULL' => strtoupper($columType),
|
||||
'VARCHAR(255) DEFAULT NULL' => strtoupper($columType),
|
||||
'ExampleDimension' => $dimensionClassName,
|
||||
'ExampleVisitDimension' => $dimensionClassName,
|
||||
'ExampleActionDimension' => $dimensionClassName,
|
||||
'ExampleConversionDimension' => $dimensionClassName,
|
||||
'ExampleTracker_DimensionName' => $translatedDimensionName,
|
||||
'ExampleTracker' => $pluginName,
|
||||
);
|
||||
|
||||
$whitelistFiles = array('/Columns');
|
||||
|
||||
if ('visit' == $type) {
|
||||
$whitelistFiles[] = '/Columns/ExampleVisitDimension.php';
|
||||
} elseif ('action' == $type) {
|
||||
$whitelistFiles[] = '/Columns/ExampleActionDimension.php';
|
||||
} elseif ('conversion' == $type) {
|
||||
$whitelistFiles[] = '/Columns/ExampleConversionDimension.php';
|
||||
} elseif ('non-tracking-dimension' == $type) {
|
||||
$whitelistFiles[] = '/Columns/ExampleDimension.php';
|
||||
} else {
|
||||
throw new \InvalidArgumentException('This dimension type is not available');
|
||||
}
|
||||
|
||||
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
|
||||
|
||||
$this->writeSuccessMessage($output, array(
|
||||
sprintf('Columns/%s.php for %s generated.', ucfirst($dimensionClassName), $pluginName),
|
||||
'You should now implement the events within this file',
|
||||
'Enjoy!'
|
||||
));
|
||||
}
|
||||
|
||||
private function getDimensionClassName($dimensionName)
|
||||
{
|
||||
$dimensionName = trim($dimensionName);
|
||||
$dimensionName = str_replace(' ', '', $dimensionName);
|
||||
$dimensionName = preg_replace("/[^A-Za-z0-9]/", '', $dimensionName);
|
||||
|
||||
$dimensionName = ucfirst($dimensionName);
|
||||
|
||||
return $dimensionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getDimensionName(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$validate = function ($dimensionName) {
|
||||
if (empty($dimensionName)) {
|
||||
throw new \InvalidArgumentException('Please enter the name of your dimension');
|
||||
}
|
||||
|
||||
if (preg_match("/[^A-Za-z0-9 ]/", $dimensionName)) {
|
||||
throw new \InvalidArgumentException('Only alpha numerical characters and whitespaces are allowed');
|
||||
}
|
||||
|
||||
return $dimensionName;
|
||||
};
|
||||
|
||||
$dimensionName = $input->getOption('dimensionname');
|
||||
|
||||
if (empty($dimensionName)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$dimensionName = $dialog->askAndValidate($output, 'Enter a human readable name of your dimension, for instance "Browser": ', $validate);
|
||||
} else {
|
||||
$validate($dimensionName);
|
||||
}
|
||||
|
||||
$dimensionName = ucfirst($dimensionName);
|
||||
|
||||
return $dimensionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @param string $type
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getColumnName(InputInterface $input, OutputInterface $output, $type)
|
||||
{
|
||||
$validate = function ($columnName) use ($type) {
|
||||
if (empty($columnName)) {
|
||||
throw new \InvalidArgumentException('Please enter the name of the dimension column');
|
||||
}
|
||||
|
||||
if (preg_match("/[^A-Za-z0-9_ ]/", $columnName)) {
|
||||
throw new \InvalidArgumentException('Only alpha numerical characters, underscores and whitespaces are allowed');
|
||||
}
|
||||
|
||||
if ('visit' == $type) {
|
||||
$columns = array_keys(DbHelper::getTableColumns(Common::prefixTable('log_visit')));
|
||||
} elseif ('action' == $type) {
|
||||
$columns = array_keys(DbHelper::getTableColumns(Common::prefixTable('log_link_visit_action')));
|
||||
} elseif ('conversion' == $type) {
|
||||
$columns = array_keys(DbHelper::getTableColumns(Common::prefixTable('log_conversion')));
|
||||
} else {
|
||||
$columns = array();
|
||||
}
|
||||
|
||||
foreach ($columns as $column) {
|
||||
if (strtolower($column) === strtolower($columnName)) {
|
||||
throw new \InvalidArgumentException('This column name is already in use.');
|
||||
}
|
||||
}
|
||||
|
||||
return $columnName;
|
||||
};
|
||||
|
||||
$columnName = $input->getOption('columnname');
|
||||
|
||||
if (empty($columnName)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$columnName = $dialog->askAndValidate($output, 'Enter the name of the column under which it should be stored in the MySQL database, for instance "visit_total_time": ', $validate);
|
||||
} else {
|
||||
$validate($columnName);
|
||||
}
|
||||
|
||||
return $columnName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getColumnType(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$validate = function ($columnType) {
|
||||
if (empty($columnType)) {
|
||||
throw new \InvalidArgumentException('Please enter the type of the dimension column');
|
||||
}
|
||||
|
||||
return $columnType;
|
||||
};
|
||||
|
||||
$columnType = $input->getOption('columntype');
|
||||
|
||||
if (empty($columnType)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$columnType = $dialog->askAndValidate($output, 'Enter the type of the column under which it should be stored in the MySQL database, for instance "VARCHAR(255) NOT NULL": ', $validate);
|
||||
} else {
|
||||
$validate($columnType);
|
||||
}
|
||||
|
||||
return $columnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getDimensionType(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$acceptedValues = array('visit', 'action', 'conversion', 'non-tracking-dimension');
|
||||
|
||||
$validate = function ($type) use ($acceptedValues) {
|
||||
if (empty($type) || !in_array($type, $acceptedValues)) {
|
||||
throw new \InvalidArgumentException('Please enter a valid dimension type (' . implode(', ', $acceptedValues) . '). Choose "non-tracking-dimension" if you only need a blank dimension having a name: ');
|
||||
}
|
||||
|
||||
return $type;
|
||||
};
|
||||
|
||||
$type = $input->getOption('type');
|
||||
|
||||
if (empty($type)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$type = $dialog->askAndValidate($output, 'Please choose the type of dimension you want to create (' . implode(', ', $acceptedValues) . '). Choose "non-tracking-dimension" if you only need a blank dimension having a name: ', $validate, false, null, $acceptedValues);
|
||||
} else {
|
||||
$validate($type);
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 a name of an existing plugin.';
|
||||
|
||||
return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
*/
|
||||
class GenerateMenu extends GeneratePluginBase
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('generate:menu')
|
||||
->setDescription('Adds a plugin menu class to an existing plugin')
|
||||
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have a menu defined yet');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$pluginName = $this->getPluginName($input, $output);
|
||||
$this->checkAndUpdateRequiredPiwikVersion($pluginName, $output);
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('ExamplePlugin');
|
||||
$replace = array('ExamplePlugin' => $pluginName);
|
||||
$whitelistFiles = array('/Menu.php');
|
||||
|
||||
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
|
||||
|
||||
$this->writeSuccessMessage($output, array(
|
||||
sprintf('Menu.php for %s generated.', $pluginName),
|
||||
'You can now start defining your plugin menu',
|
||||
'Enjoy!'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getPluginName(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$pluginNames = $this->getPluginNamesHavingNotSpecificFile('Menu.php');
|
||||
$invalidName = 'You have to enter the name of an existing plugin which does not already have a menu defined';
|
||||
|
||||
return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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\Plugins\ExamplePlugin\ExamplePlugin;
|
||||
use Piwik\Plugin;
|
||||
use Piwik\Version;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
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('overwrite', null, InputOption::VALUE_NONE, 'Generate even if plugin directory already exists.');
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
$this->generatePluginFolder($pluginName);
|
||||
|
||||
$plugin = new ExamplePlugin();
|
||||
$info = $plugin->getInformation();
|
||||
$exampleDescription = $info['description'];
|
||||
|
||||
if ($isTheme) {
|
||||
$exampleFolder = Plugin\Manager::getPluginDirectory('ExampleTheme');
|
||||
$replace = array(
|
||||
'ExampleTheme' => $pluginName,
|
||||
$exampleDescription => $description,
|
||||
'0.1.0' => $version,
|
||||
'3.0.0-b1' => Version::VERSION
|
||||
);
|
||||
$whitelistFiles = array();
|
||||
|
||||
} else {
|
||||
|
||||
$exampleFolder = Plugin\Manager::getPluginDirectory('ExamplePlugin');
|
||||
$replace = array(
|
||||
'ExamplePlugin' => $pluginName,
|
||||
$exampleDescription => $description,
|
||||
'0.1.0' => $version,
|
||||
'3.0.0-b1' => Version::VERSION
|
||||
);
|
||||
$whitelistFiles = array(
|
||||
'/ExamplePlugin.php',
|
||||
'/plugin.json',
|
||||
'/README.md',
|
||||
'/CHANGELOG.md',
|
||||
'/screenshots',
|
||||
'/screenshots/.gitkeep',
|
||||
'/docs',
|
||||
'/docs/faq.md',
|
||||
'/docs/index.md',
|
||||
);
|
||||
}
|
||||
|
||||
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
|
||||
$this->checkAndUpdateRequiredPiwikVersion($pluginName, new NullOutput());
|
||||
|
||||
if ($isTheme) {
|
||||
$this->writeSuccessMessage($output, array(
|
||||
sprintf('Theme %s %s generated.', $pluginName, $version),
|
||||
'If you have not done yet check out our Theming guide <comment>https://developer.matomo.org/guides/theming</comment>',
|
||||
'Enjoy!'
|
||||
));
|
||||
} else {
|
||||
$this->writeSuccessMessage($output, array(
|
||||
sprintf('Plugin %s %s generated.', $pluginName, $version),
|
||||
'Our developer guides will help you developing this plugin, check out <comment>https://developer.matomo.org/guides</comment>',
|
||||
'To see a list of available generators execute <comment>./console list generate</comment>',
|
||||
'Enjoy!'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getPluginName(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$overwrite = $input->getOption('overwrite');
|
||||
|
||||
$self = $this;
|
||||
|
||||
$validate = function ($pluginName) use ($self, $overwrite) {
|
||||
if (empty($pluginName)) {
|
||||
throw new \RuntimeException('You have to enter a plugin name');
|
||||
}
|
||||
|
||||
if(strlen($pluginName) > 40) {
|
||||
throw new \RuntimeException('Your plugin name cannot be longer than 40 characters');
|
||||
}
|
||||
|
||||
if (!Plugin\Manager::getInstance()->isValidPluginName($pluginName)) {
|
||||
throw new \RuntimeException(sprintf('The plugin name %s is not valid. The name must start with a letter and is only allowed to contain numbers and letters.', $pluginName));
|
||||
}
|
||||
|
||||
$pluginPath = $self->getPluginPath($pluginName);
|
||||
|
||||
if (file_exists($pluginPath)
|
||||
&& !$overwrite
|
||||
) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,396 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 Piwik\Development;
|
||||
use Piwik\Filesystem;
|
||||
use Piwik\Plugin\ConsoleCommand;
|
||||
use Piwik\Plugin\Dependency;
|
||||
use Piwik\Plugin\Manager;
|
||||
use Piwik\Version;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
abstract class GeneratePluginBase extends ConsoleCommand
|
||||
{
|
||||
public function isEnabled()
|
||||
{
|
||||
return Development::isEnabled();
|
||||
}
|
||||
|
||||
public function getPluginPath($pluginName)
|
||||
{
|
||||
return Manager::getPluginDirectory($pluginName);
|
||||
}
|
||||
|
||||
private function createFolderWithinPluginIfNotExists($pluginNameOrCore, $folder)
|
||||
{
|
||||
if ($pluginNameOrCore === 'core') {
|
||||
$pluginPath = $this->getPathToCore();
|
||||
} else {
|
||||
$pluginPath = $this->getPluginPath($pluginNameOrCore);
|
||||
}
|
||||
|
||||
if (!file_exists($pluginPath . $folder)) {
|
||||
Filesystem::mkdir($pluginPath . $folder);
|
||||
}
|
||||
}
|
||||
|
||||
protected function createFileWithinPluginIfNotExists($pluginNameOrCore, $fileName, $content)
|
||||
{
|
||||
if ($pluginNameOrCore === 'core') {
|
||||
$pluginPath = $this->getPathToCore();
|
||||
} else {
|
||||
$pluginPath = $this->getPluginPath($pluginNameOrCore);
|
||||
}
|
||||
|
||||
if (!file_exists($pluginPath . $fileName)) {
|
||||
file_put_contents($pluginPath . $fileName, $content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a lang/en.json within the plugin in case it does not exist yet and adds a translation for the given
|
||||
* text.
|
||||
*
|
||||
* @param $pluginName
|
||||
* @param $translatedText
|
||||
* @param string $translationKey Optional, by default the key will be generated automatically
|
||||
* @return string Either the generated translation key or the original text if a different translation for this
|
||||
* generated translation key already exists.
|
||||
*/
|
||||
protected function makeTranslationIfPossible($pluginName, $translatedText, $translationKey = '')
|
||||
{
|
||||
$defaultLang = array($pluginName => array());
|
||||
|
||||
$this->createFolderWithinPluginIfNotExists($pluginName, '/lang');
|
||||
$this->createFileWithinPluginIfNotExists($pluginName, '/lang/en.json', $this->toJson($defaultLang));
|
||||
|
||||
$langJsonPath = $this->getPluginPath($pluginName) . '/lang/en.json';
|
||||
$translations = file_get_contents($langJsonPath);
|
||||
$translations = json_decode($translations, true);
|
||||
|
||||
if (empty($translations[$pluginName])) {
|
||||
$translations[$pluginName] = array();
|
||||
}
|
||||
|
||||
if (!empty($translationKey)) {
|
||||
$key = $translationKey;
|
||||
} else {
|
||||
$key = $this->buildTranslationKey($translatedText);
|
||||
}
|
||||
|
||||
if (array_key_exists($key, $translations[$pluginName])) {
|
||||
// we do not want to overwrite any existing translations
|
||||
if ($translations[$pluginName][$key] === $translatedText) {
|
||||
return $pluginName . '_' . $key;
|
||||
}
|
||||
|
||||
return $translatedText;
|
||||
}
|
||||
|
||||
$translations[$pluginName][$key] = $this->removeNonJsonCompatibleCharacters($translatedText);
|
||||
|
||||
file_put_contents($langJsonPath, $this->toJson($translations));
|
||||
|
||||
return $pluginName . '_' . $key;
|
||||
}
|
||||
|
||||
protected function checkAndUpdateRequiredPiwikVersion($pluginName, OutputInterface $output)
|
||||
{
|
||||
$pluginJsonPath = $this->getPluginPath($pluginName) . '/plugin.json';
|
||||
$relativePluginJson = Manager::getPluginDirectory($pluginName) . '/plugin.json';
|
||||
|
||||
if (!file_exists($pluginJsonPath) || !is_writable($pluginJsonPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$pluginJson = file_get_contents($pluginJsonPath);
|
||||
$pluginJson = json_decode($pluginJson, true);
|
||||
|
||||
if (empty($pluginJson)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($pluginJson['require'])) {
|
||||
$pluginJson['require'] = array();
|
||||
}
|
||||
|
||||
$piwikVersion = Version::VERSION;
|
||||
$nextMajorVersion = (int) substr($piwikVersion, 0, strpos($piwikVersion, '.')) + 1;
|
||||
$secondPartPiwikVersionRequire = ',<' . $nextMajorVersion . '.0.0-b1';
|
||||
if (false === strpos($piwikVersion, '-')) {
|
||||
// see https://github.com/composer/composer/issues/4080 we need to specify -stable otherwise it would match
|
||||
// $piwikVersion-dev meaning it would also match all pre-released. However, we only want to match a stable
|
||||
// release
|
||||
$piwikVersion.= '-stable';
|
||||
}
|
||||
|
||||
$newRequiredVersion = sprintf('>=%s,<%d.0.0-b1', $piwikVersion, $nextMajorVersion);
|
||||
|
||||
|
||||
if (!empty($pluginJson['require']['piwik'])) {
|
||||
$requiredVersion = trim($pluginJson['require']['piwik']);
|
||||
|
||||
if ($requiredVersion === $newRequiredVersion) {
|
||||
// there is nothing to updated
|
||||
return;
|
||||
}
|
||||
|
||||
// our generated versions look like ">=2.25.4,<3.0.0-b1".
|
||||
// We only updated the Piwik version in the first part if the piwik version looks like that or if it has only
|
||||
// one piwik version defined. In all other cases, eg user uses || etc we do not update it as user has customized
|
||||
// the piwik version.
|
||||
|
||||
foreach (['<>','!=', '<=','==', '^'] as $comparison) {
|
||||
if (strpos($requiredVersion, $comparison) === 0) {
|
||||
// user is using custom piwik version require, we do not overwrite anything.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($requiredVersion, '||') !== false || strpos($requiredVersion, ' ') !== false) {
|
||||
// user is using custom piwik version require, we do not overwrite anything.
|
||||
return;
|
||||
}
|
||||
|
||||
$requiredPiwikVersions = explode(',', (string) $requiredVersion);
|
||||
$numRequiredPiwikVersions = count($requiredPiwikVersions);
|
||||
|
||||
if ($numRequiredPiwikVersions > 2) {
|
||||
// user is using custom piwik version require, we do not overwrite anything.
|
||||
return;
|
||||
}
|
||||
|
||||
if ($numRequiredPiwikVersions === 2 &&
|
||||
!Common::stringEndsWith($requiredVersion, $secondPartPiwikVersionRequire)) {
|
||||
// user is using custom piwik version require, we do not overwrite anything
|
||||
return;
|
||||
}
|
||||
|
||||
// if only one piwik version is defined we update it to make sure it does now specify an upper version limit
|
||||
|
||||
$dependency = new Dependency();
|
||||
$missingVersion = $dependency->getMissingVersions($piwikVersion, $requiredVersion);
|
||||
|
||||
if (!empty($missingVersion)) {
|
||||
$msg = sprintf('We cannot generate this component as the plugin "%s" requires the Piwik version "%s" in the file "%s". Generating this component requires "%s". If you know your plugin is compatible with your Piwik version remove the required Piwik version in "%s" and try to execute this command again.', $pluginName, $requiredVersion, $relativePluginJson, $newRequiredVersion, $relativePluginJson);
|
||||
|
||||
throw new \Exception($msg);
|
||||
}
|
||||
|
||||
$output->writeln('');
|
||||
$output->writeln(sprintf('<comment>We have updated the required Piwik version from "%s" to "%s" in "%s".</comment>', $requiredVersion, $newRequiredVersion, $relativePluginJson));
|
||||
} else {
|
||||
$output->writeln('');
|
||||
$output->writeln(sprintf('<comment>We have updated your "%s" to require the Piwik version "%s".</comment>', $relativePluginJson, $newRequiredVersion));
|
||||
}
|
||||
|
||||
$pluginJson['require']['piwik'] = $newRequiredVersion;
|
||||
file_put_contents($pluginJsonPath, $this->toJson($pluginJson));
|
||||
}
|
||||
|
||||
private function toJson($value)
|
||||
{
|
||||
if (defined('JSON_PRETTY_PRINT')) {
|
||||
|
||||
return json_encode($value, JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
return json_encode($value);
|
||||
}
|
||||
|
||||
private function buildTranslationKey($translatedText)
|
||||
{
|
||||
$translatedText = preg_replace('/(\s+)/', '', $translatedText);
|
||||
$translatedText = preg_replace("/[^A-Za-z0-9]/", '', $translatedText);
|
||||
$translatedText = trim($translatedText);
|
||||
|
||||
return $this->removeNonJsonCompatibleCharacters($translatedText);
|
||||
}
|
||||
|
||||
private function removeNonJsonCompatibleCharacters($text)
|
||||
{
|
||||
return preg_replace('/[^(\x00-\x7F)]*/', '', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the given method and all needed use statements into an existing class. The target class name will be
|
||||
* built based on the given $replace argument.
|
||||
* @param string $sourceClassName
|
||||
* @param string $methodName
|
||||
* @param array $replace
|
||||
*/
|
||||
protected function copyTemplateMethodToExisitingClass($sourceClassName, $methodName, $replace)
|
||||
{
|
||||
$targetClassName = $this->replaceContent($replace, $sourceClassName);
|
||||
|
||||
if (Development::methodExists($targetClassName, $methodName)) {
|
||||
// we do not want to add the same method twice
|
||||
return;
|
||||
}
|
||||
|
||||
Development::checkMethodExists($sourceClassName, $methodName, 'Cannot copy template method: ');
|
||||
|
||||
$targetClass = new \ReflectionClass($targetClassName);
|
||||
$file = new \SplFileObject($targetClass->getFileName());
|
||||
|
||||
$methodCode = Development::getMethodSourceCode($sourceClassName, $methodName);
|
||||
$methodCode = $this->replaceContent($replace, $methodCode);
|
||||
$methodLine = $targetClass->getEndLine() - 1;
|
||||
|
||||
$sourceUses = Development::getUseStatements($sourceClassName);
|
||||
$targetUses = Development::getUseStatements($targetClassName);
|
||||
$usesToAdd = array_diff($sourceUses, $targetUses);
|
||||
if (empty($usesToAdd)) {
|
||||
$useCode = '';
|
||||
} else {
|
||||
$useCode = "\nuse " . implode("\nuse ", $usesToAdd) . "\n";
|
||||
}
|
||||
|
||||
// search for namespace line before the class starts
|
||||
$useLine = 0;
|
||||
foreach (new \LimitIterator($file, 0, $targetClass->getStartLine()) as $index => $line) {
|
||||
if (0 === strpos(trim($line), 'namespace ')) {
|
||||
$useLine = $index + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$newClassCode = '';
|
||||
foreach(new \LimitIterator($file) as $index => $line) {
|
||||
if ($index == $methodLine) {
|
||||
$newClassCode .= $methodCode;
|
||||
}
|
||||
|
||||
if (0 !== $useLine && $index == $useLine) {
|
||||
$newClassCode .= $useCode;
|
||||
}
|
||||
|
||||
$newClassCode .= $line;
|
||||
}
|
||||
|
||||
file_put_contents($targetClass->getFileName(), $newClassCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)) {
|
||||
$fileNamePlugin = $this->replaceContent($replace, $fileNamePlugin);
|
||||
$this->createFolderWithinPluginIfNotExists($pluginName, $fileNamePlugin);
|
||||
} else {
|
||||
$template = file_get_contents($file);
|
||||
$template = $this->replaceContent($replace, $template);
|
||||
|
||||
$fileNamePlugin = $this->replaceContent($replace, $fileNamePlugin);
|
||||
|
||||
$this->createFileWithinPluginIfNotExists($pluginName, $fileNamePlugin, $template);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected function getPluginNames()
|
||||
{
|
||||
$pluginNames = array();
|
||||
foreach (Manager::getPluginsDirectories() as $pluginsDir) {
|
||||
$pluginDirs = \_glob($pluginsDir . '*', GLOB_ONLYDIR);
|
||||
|
||||
foreach ($pluginDirs as $pluginDir) {
|
||||
$pluginNames[] = basename($pluginDir);
|
||||
}
|
||||
}
|
||||
|
||||
return $pluginNames;
|
||||
}
|
||||
|
||||
protected function getPluginNamesHavingNotSpecificFile($filename)
|
||||
{
|
||||
$pluginNames = array();
|
||||
foreach (Manager::getPluginsDirectories() as $pluginsDir) {
|
||||
$pluginDirs = \_glob($pluginsDir . '*', GLOB_ONLYDIR);
|
||||
|
||||
foreach ($pluginDirs as $pluginDir) {
|
||||
if (!file_exists($pluginDir . '/' . $filename)) {
|
||||
$pluginNames[] = basename($pluginDir);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return $pluginNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return string
|
||||
* @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);
|
||||
}
|
||||
|
||||
return $pluginName;
|
||||
}
|
||||
|
||||
private function getPathToCore()
|
||||
{
|
||||
$path = PIWIK_INCLUDE_PATH . '/core';
|
||||
return $path;
|
||||
}
|
||||
|
||||
private function replaceContent($replace, $contentToReplace)
|
||||
{
|
||||
foreach ((array) $replace as $key => $value) {
|
||||
$contentToReplace = str_replace($key, $value, $contentToReplace);
|
||||
}
|
||||
|
||||
return $contentToReplace;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,308 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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\Columns\Dimension;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugin\Manager;
|
||||
use Piwik\Plugin\Report;
|
||||
use Piwik\Plugin\ReportsProvider;
|
||||
use Piwik\Translate;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class GenerateReport extends GeneratePluginBase
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('generate:report')
|
||||
->setDescription('Adds a new report to an existing plugin')
|
||||
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have a menu defined yet')
|
||||
->addOption('reportname', null, InputOption::VALUE_REQUIRED, 'The name of the report you want to create')
|
||||
->addOption('category', null, InputOption::VALUE_REQUIRED, 'The name of the category the report belongs to')
|
||||
->addOption('dimension', null, InputOption::VALUE_OPTIONAL, 'The name of the dimension in case your report has a dimension')
|
||||
->addOption('documentation', null, InputOption::VALUE_REQUIRED, 'A documentation that explains what your report is about');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$pluginName = $this->getPluginName($input, $output);
|
||||
$this->checkAndUpdateRequiredPiwikVersion($pluginName, $output);
|
||||
|
||||
$reportName = $this->getReportName($input, $output);
|
||||
$category = $this->getCategory($input, $output, $pluginName);
|
||||
$documentation = $this->getDocumentation($input, $output);
|
||||
list($dimension, $dimensionClass) = $this->getDimension($input, $output, $pluginName);
|
||||
|
||||
$order = $this->getOrder($category);
|
||||
$apiName = $this->getApiName($reportName);
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('ExampleReport');
|
||||
$replace = array('GetExampleReport' => ucfirst($apiName),
|
||||
'getExampleReport' => lcfirst($apiName),
|
||||
'getApiReport' => lcfirst($apiName),
|
||||
'ExampleCategory' => $category,
|
||||
'ExampleReportName' => $this->makeTranslationIfPossible($pluginName, $reportName),
|
||||
'ExampleReportDocumentation' => $documentation,
|
||||
'999' => $order,
|
||||
'new ExitPageUrl()' => $dimension,
|
||||
'use Piwik\Plugins\Actions\Columns\ExitPageUrl;' => $dimensionClass,
|
||||
'ExampleReport' => $pluginName,
|
||||
);
|
||||
|
||||
$whitelistFiles = array('/Reports', '/Reports/Base.php', '/Reports/GetExampleReport.php');
|
||||
|
||||
if (file_exists($this->getPluginPath($pluginName) . '/API.php')) {
|
||||
$this->copyTemplateMethodToExisitingClass('Piwik\Plugins\ExampleReport\API', 'getExampleReport', $replace);
|
||||
} else {
|
||||
$whitelistFiles[] = '/API.php';
|
||||
}
|
||||
|
||||
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
|
||||
|
||||
$this->writeSuccessMessage($output, array(
|
||||
sprintf('plugins/%s/Reports/%s.php for %s generated.', $pluginName, ucfirst($apiName)),
|
||||
'You should now implement the method called <comment>"' . lcfirst($apiName) . '()"</comment> in API.php',
|
||||
// 'Read more about this here: link to developer guide',
|
||||
'Enjoy!'
|
||||
));
|
||||
}
|
||||
|
||||
private function getOrder($category)
|
||||
{
|
||||
$order = 1;
|
||||
|
||||
$reports = new ReportsProvider();
|
||||
|
||||
foreach ($reports->getAllReports() as $report) {
|
||||
if ($report->getCategoryId() === $category) {
|
||||
if ($report->getOrder() > $order) {
|
||||
$order = $report->getOrder() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
private function getApiName($reportName)
|
||||
{
|
||||
$reportName = trim($reportName);
|
||||
$reportName = str_replace(' ', '', $reportName);
|
||||
$reportName = preg_replace("/[^A-Za-z0-9]/", '', $reportName);
|
||||
|
||||
$apiName = 'get' . ucfirst($reportName);
|
||||
|
||||
return $apiName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getReportName(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$validate = function ($reportName) {
|
||||
if (empty($reportName)) {
|
||||
throw new \InvalidArgumentException('Please enter the name of your report');
|
||||
}
|
||||
|
||||
if (preg_match("/[^A-Za-z0-9 ]/", $reportName)) {
|
||||
throw new \InvalidArgumentException('Only alpha numerical characters and whitespaces are allowed');
|
||||
}
|
||||
|
||||
return $reportName;
|
||||
};
|
||||
|
||||
$reportName = $input->getOption('reportname');
|
||||
|
||||
if (empty($reportName)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$reportName = $dialog->askAndValidate($output, 'Enter the name of your report, for instance "Browser Families": ', $validate);
|
||||
} else {
|
||||
$validate($reportName);
|
||||
}
|
||||
|
||||
$reportName = ucfirst($reportName);
|
||||
|
||||
return $reportName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getDocumentation(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$validate = function ($documentation) {
|
||||
if (empty($documentation)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $documentation;
|
||||
};
|
||||
|
||||
$documentation = $input->getOption('documentation');
|
||||
|
||||
if (empty($documentation)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$documentation = $dialog->askAndValidate($output, 'Enter a documentation that describes the data of your report (you can leave it empty and define it later): ', $validate);
|
||||
} else {
|
||||
$validate($documentation);
|
||||
}
|
||||
|
||||
$documentation = ucfirst($documentation);
|
||||
|
||||
return $documentation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @param string $pluginName
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getCategory(InputInterface $input, OutputInterface $output, $pluginName)
|
||||
{
|
||||
$path = $this->getPluginPath($pluginName) . '/Reports/Base.php';
|
||||
if (file_exists($path)) {
|
||||
// category is already defined in base.php
|
||||
return '';
|
||||
}
|
||||
|
||||
$validate = function ($category) {
|
||||
if (empty($category)) {
|
||||
throw new \InvalidArgumentException('Please enter the name of the category your report belongs to');
|
||||
}
|
||||
|
||||
return $category;
|
||||
};
|
||||
|
||||
$category = $input->getOption('category');
|
||||
|
||||
$reports = new ReportsProvider();
|
||||
|
||||
$categories = array();
|
||||
foreach ($reports->getAllReports() as $report) {
|
||||
if ($report->getCategoryId()) {
|
||||
$categories[] = Piwik::translate($report->getCategoryId());
|
||||
}
|
||||
}
|
||||
$categories = array_values(array_unique($categories));
|
||||
|
||||
if (empty($category)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$category = $dialog->askAndValidate($output, 'Enter the report category, for instance "Visitor" (you can reuse any existing category or define a new one): ', $validate, false, null, $categories);
|
||||
} else {
|
||||
$validate($category);
|
||||
}
|
||||
|
||||
$translationKey = Translate::findTranslationKeyForTranslation($category);
|
||||
if (!empty($translationKey)) {
|
||||
return $translationKey;
|
||||
}
|
||||
|
||||
$category = ucfirst($category);
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @param string $pluginName
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getDimension(InputInterface $input, OutputInterface $output, $pluginName)
|
||||
{
|
||||
$dimensions = array();
|
||||
$dimensionNames = array();
|
||||
|
||||
$reports = new ReportsProvider();
|
||||
|
||||
foreach ($reports->getAllReports() as $report) {
|
||||
$dimension = $report->getDimension();
|
||||
if (is_object($dimension)) {
|
||||
$name = $dimension->getName();
|
||||
if (!empty($name)) {
|
||||
$dimensions[$name] = get_class($dimension);
|
||||
$dimensionNames[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$plugin = Manager::getInstance()->loadPlugin($pluginName);
|
||||
$dimensions = Dimension::getAllDimensions();
|
||||
$dimensions = array_merge($dimensions, Dimension::getDimensions($plugin));
|
||||
|
||||
foreach ($dimensions as $dimension) {
|
||||
$name = $dimension->getName();
|
||||
if (!empty($name)) {
|
||||
$dimensions[$name] = get_class($dimension);
|
||||
$dimensionNames[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
$dimensionNames = array_values(array_unique($dimensionNames));
|
||||
|
||||
$validate = function ($dimension) use ($dimensions) {
|
||||
if (empty($dimension)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!empty($dimension) && !array_key_exists($dimension, $dimensions)) {
|
||||
throw new \InvalidArgumentException('Leave dimension either empty or use an existing one. You can also create a new dimension by calling .console generate:dimension before generating this report.');
|
||||
}
|
||||
|
||||
return $dimension;
|
||||
};
|
||||
|
||||
$actualDimension = $input->getOption('dimension');
|
||||
|
||||
if (null === $actualDimension) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$actualDimension = $dialog->askAndValidate($output, 'Enter the report dimension, for instance "Browser" (you can leave it either empty or use an existing one): ', $validate, false, null, $dimensionNames);
|
||||
} else {
|
||||
$validate($actualDimension);
|
||||
}
|
||||
|
||||
if (empty($actualDimension)) {
|
||||
return array('null', '');
|
||||
}
|
||||
|
||||
$className = $dimensions[$actualDimension];
|
||||
$parts = explode('\\', $className);
|
||||
$name = end($parts);
|
||||
|
||||
return array('new ' . $name . '()', 'use ' . $className . ';');
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 a name of an existing plugin.';
|
||||
|
||||
return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
*/
|
||||
class GenerateScheduledTask extends GeneratePluginBase
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('generate:scheduledtask')
|
||||
->setDescription('Adds a tasks class to an existing plugin which allows you to specify scheduled tasks')
|
||||
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have any tasks defined yet');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$pluginName = $this->getPluginName($input, $output);
|
||||
$this->checkAndUpdateRequiredPiwikVersion($pluginName, $output);
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('ExamplePlugin');
|
||||
$replace = array('ExamplePlugin' => $pluginName);
|
||||
$whitelistFiles = array('/Tasks.php');
|
||||
|
||||
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
|
||||
|
||||
$this->writeSuccessMessage($output, array(
|
||||
sprintf('Tasks.php for %s generated.', $pluginName),
|
||||
'You can now start specifying your scheduled tasks',
|
||||
'Enjoy!'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getPluginName(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$pluginNames = $this->getPluginNamesHavingNotSpecificFile('Tasks.php');
|
||||
$invalidName = 'You have to enter the name of an existing plugin which does not already have any tasks defined';
|
||||
|
||||
return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 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 SystemSetting, UserSetting or MeasurableSetting class to an existing plugin')
|
||||
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have settings yet')
|
||||
->addOption('settingstype', null, InputOption::VALUE_REQUIRED, 'The type of settings you want to create. Should be one of these values: ' . implode(', ', $this->getSettingTypes()));
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$settingsType = $this->getSettingsType($input, $output);
|
||||
$settingsFilename = $settingsType . '.php';
|
||||
|
||||
$pluginName = $this->getPluginName($input, $output, $settingsType, $settingsFilename);
|
||||
$this->checkAndUpdateRequiredPiwikVersion($pluginName, $output);
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('ExampleSettingsPlugin');
|
||||
$replace = array('ExampleSettingsPlugin' => $pluginName);
|
||||
$whitelistFiles = array('/' . $settingsFilename);
|
||||
|
||||
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
|
||||
|
||||
$this->writeSuccessMessage($output, array(
|
||||
sprintf('%s for %s generated.', $settingsFilename, $pluginName),
|
||||
'You can now start defining your ' . $settingsType,
|
||||
'Enjoy!'
|
||||
));
|
||||
}
|
||||
|
||||
private function getSettingTypes()
|
||||
{
|
||||
return array('system', 'user', 'measurable');
|
||||
}
|
||||
|
||||
private function getSettingsType(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$availableTypes = $this->getSettingTypes();
|
||||
|
||||
$validate = function ($type) use ($availableTypes) {
|
||||
if (empty($type) || !in_array($type, $availableTypes)) {
|
||||
throw new \InvalidArgumentException('Please enter a valid settings type (' . implode(', ', $availableTypes) . '). ');
|
||||
}
|
||||
|
||||
return $type;
|
||||
};
|
||||
|
||||
$settingsType = $input->getOption('settingstype');
|
||||
|
||||
if (empty($settingsType)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$settingsType = $dialog->askAndValidate($output, 'Please choose the type of settings you want to create (' . implode(', ', $availableTypes) . '): ', $validate, false, null, $availableTypes);
|
||||
} else {
|
||||
$validate($settingsType);
|
||||
}
|
||||
|
||||
return ucfirst($settingsType) . 'Settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @param string $settingsType
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getPluginName(InputInterface $input, OutputInterface $output, $settingsType, $settingsFile)
|
||||
{
|
||||
$pluginNames = $this->getPluginNamesHavingNotSpecificFile($settingsFile);
|
||||
$invalidName = 'You have to enter the name of an existing plugin which does not already have ' . $settingsType;
|
||||
|
||||
return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 Piwik\Plugin\Manager;
|
||||
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", "system", or "ui" test');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$pluginName = $this->getPluginName($input, $output);
|
||||
$testType = $this->getTestType($input, $output);
|
||||
$testName = $this->getTestName($input, $output, $testType);
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('ExamplePlugin');
|
||||
$replace = array(
|
||||
'ExamplePlugin' => $pluginName,
|
||||
'SimpleTest' => $testName,
|
||||
'SimpleSystemTest' => $testName,
|
||||
'SimpleUITest_spec.js' => $testName . '_spec.js',
|
||||
'SimpleUITest' => $testName,
|
||||
);
|
||||
|
||||
$whitelistFiles = $this->getTestFilesWhitelist($testType);
|
||||
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
|
||||
|
||||
$messages = array(
|
||||
sprintf('Test %s for plugin %s generated.', $testName, $pluginName),
|
||||
);
|
||||
|
||||
if (strtolower($testType) === 'ui') {
|
||||
$messages[] = 'To run this test execute the command: ';
|
||||
$messages[] = '<comment>' . sprintf('./console tests:run-ui %s', $testName) . '</comment>';
|
||||
} else {
|
||||
$messages[] = 'To run all your plugin tests, execute the command: ';
|
||||
$messages[] = '<comment>' . sprintf('./console tests:run %s', $pluginName) . '</comment>';
|
||||
$messages[] = 'To run only this test: ';
|
||||
$messages[] = '<comment>' . sprintf('./console tests:run %s', $testName) . '</comment>';
|
||||
}
|
||||
|
||||
$messages[] = 'Enjoy!';
|
||||
|
||||
$this->writeSuccessMessage($output, $messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return string
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function getTestName(InputInterface $input, OutputInterface $output, $testType)
|
||||
{
|
||||
$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 (strtolower($testType) !== 'ui' && !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);
|
||||
}
|
||||
|
||||
public function getValidTypes()
|
||||
{
|
||||
return array('unit', 'integration', 'system', 'ui');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return string Unit, Integration, System
|
||||
*/
|
||||
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 ('Ui' == $testType) {
|
||||
return array(
|
||||
'/tests',
|
||||
'/tests/UI',
|
||||
'/tests/UI/.gitignore',
|
||||
'/tests/UI/expected-screenshots',
|
||||
'/tests/UI/expected-screenshots/.gitkeep',
|
||||
'/tests/UI/SimpleUITest_spec.js',
|
||||
);
|
||||
}
|
||||
|
||||
if ('System' == $testType) {
|
||||
return array(
|
||||
'/.gitignore',
|
||||
'/tests',
|
||||
'/tests/System',
|
||||
'/tests/System/SimpleSystemTest.php',
|
||||
'/tests/System/expected',
|
||||
'/tests/System/expected/test___API.get_day.xml',
|
||||
'/tests/System/expected/test___Goals.getItemsSku_day.xml',
|
||||
'/tests/System/processed',
|
||||
'/tests/System/processed/.gitignore',
|
||||
'/tests/Fixtures',
|
||||
'/tests/Fixtures/SimpleFixtureTrackFewVisits.php'
|
||||
);
|
||||
}
|
||||
|
||||
if ('Integration' == $testType) {
|
||||
|
||||
return array(
|
||||
'/tests',
|
||||
'/tests/Integration',
|
||||
'/tests/Integration/SimpleTest.php'
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'/tests',
|
||||
'/tests/Unit',
|
||||
'/tests/Unit/SimpleTest.php'
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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;
|
||||
use Piwik\Updater;
|
||||
use Piwik\Version;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class GenerateUpdate extends GeneratePluginBase
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('generate:update')
|
||||
->setDescription('Adds a new update to an existing plugin or "core"')
|
||||
->addOption('component', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin or "core"');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$component = $this->getComponent($input, $output);
|
||||
|
||||
$version = $this->getVersion($component);
|
||||
$namespace = $this->getNamespace($component);
|
||||
$className = $this->getUpdateClassName($component, $version);
|
||||
|
||||
$exampleFolder = Plugin\Manager::getPluginDirectory('ExamplePlugin');
|
||||
$replace = array('Piwik\Plugins\ExamplePlugin\Updates' => $namespace,
|
||||
'ExamplePlugin' => $component,
|
||||
'Updates_0_0_2' => $className,
|
||||
'0.0.2' => $version);
|
||||
$whitelistFiles = array('/Updates', '/Updates/0.0.2.php');
|
||||
|
||||
$this->copyTemplateToPlugin($exampleFolder, $component, $replace, $whitelistFiles);
|
||||
|
||||
$this->writeSuccessMessage($output, array(
|
||||
sprintf('Updates/%s.php for %s generated.', $version, $component),
|
||||
'You should have a look at the method update() or getSql() now.',
|
||||
'Enjoy!'
|
||||
));
|
||||
}
|
||||
|
||||
private function getUpdateClassName($component, $version)
|
||||
{
|
||||
$updater = new Updater();
|
||||
$className = $updater->getUpdateClassName($component, $version);
|
||||
$parts = explode('\\', $className);
|
||||
|
||||
return end($parts);
|
||||
}
|
||||
|
||||
private function getVersion($component)
|
||||
{
|
||||
if ($component === 'core') {
|
||||
return Version::VERSION;
|
||||
}
|
||||
|
||||
$pluginManager = Plugin\Manager::getInstance();
|
||||
|
||||
if ($pluginManager->isPluginLoaded($component)) {
|
||||
$plugin = $pluginManager->getLoadedPlugin($component);
|
||||
} else {
|
||||
$plugin = new Plugin($component);
|
||||
}
|
||||
|
||||
return $plugin->getVersion();
|
||||
}
|
||||
|
||||
private function getNamespace($component)
|
||||
{
|
||||
$updater = new Updater();
|
||||
$className = $updater->getUpdateClassName($component, 'xx');
|
||||
$className = str_replace('Updates_xx', '', $className);
|
||||
$className = trim($className, '\\');
|
||||
|
||||
return $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function getComponent(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$components = $this->getPluginNames();
|
||||
$components[] = 'core';
|
||||
|
||||
$validate = function ($component) use ($components) {
|
||||
if (!in_array($component, $components)) {
|
||||
throw new \InvalidArgumentException('You have to enter a name of an existing plugin or "core".');
|
||||
}
|
||||
|
||||
return $component;
|
||||
};
|
||||
|
||||
$component = $input->getOption('component');
|
||||
|
||||
if (empty($component)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$component = $dialog->askAndValidate($output, 'Enter the name of your plugin or "core": ', $validate, false, null, $components);
|
||||
} else {
|
||||
$validate($component);
|
||||
}
|
||||
|
||||
return $component;
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 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('overwrite', null, InputOption::VALUE_NONE, 'Generate even if plugin directory already exists.')
|
||||
->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);
|
||||
$this->checkAndUpdateRequiredPiwikVersion($pluginName, $output);
|
||||
$description = $this->getPluginDescription($input, $output);
|
||||
$version = $this->getPluginVersion($input, $output);
|
||||
$visualizationName = $this->getVisualizationName($input, $output);
|
||||
|
||||
$this->generatePluginFolder($pluginName);
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('ExampleVisualization');
|
||||
$replace = array(
|
||||
'SimpleTable' => $visualizationName,
|
||||
'simpleTable' => lcfirst($visualizationName),
|
||||
'Simple Table' => $this->makeTranslationIfPossible($pluginName, $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 (only AlNum allowed)', $visualizationName));
|
||||
}
|
||||
|
||||
return $visualizationName;
|
||||
};
|
||||
|
||||
$visualizationName = $input->getOption('visualizationname');
|
||||
|
||||
if (empty($visualizationName)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$visualizationName = $dialog->askAndValidate($output, 'Enter a visualization name (only AlNum allowed): ', $validate);
|
||||
} else {
|
||||
$validate($visualizationName);
|
||||
}
|
||||
|
||||
$visualizationName = ucfirst($visualizationName);
|
||||
|
||||
return $visualizationName;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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\Piwik;
|
||||
use Piwik\Plugin\Manager;
|
||||
use Piwik\Translate;
|
||||
use Piwik\Widget\WidgetsList;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
*/
|
||||
class GenerateWidget extends GeneratePluginBase
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('generate:widget')
|
||||
->setDescription('Adds a plugin widget class to an existing plugin')
|
||||
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have any widgets defined yet')
|
||||
->addOption('widgetname', null, InputOption::VALUE_REQUIRED, 'The name of the widget you want to create')
|
||||
->addOption('category', null, InputOption::VALUE_REQUIRED, 'The name of the category the widget should belong to');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$pluginName = $this->getPluginName($input, $output);
|
||||
$this->checkAndUpdateRequiredPiwikVersion($pluginName, $output);
|
||||
|
||||
$widgetName = $this->getWidgetName($input, $output);
|
||||
$category = $this->getCategory($input, $output);
|
||||
|
||||
if ($category === Piwik::translate($category)) {
|
||||
// no translation found...
|
||||
$category = $this->makeTranslationIfPossible($pluginName, $category);
|
||||
}
|
||||
|
||||
$widgetMethod = $this->getWidgetMethodName($widgetName);
|
||||
$widgetClass = ucfirst($widgetMethod);
|
||||
|
||||
$exampleFolder = Manager::getPluginDirectory('ExamplePlugin');
|
||||
$replace = array('ExamplePlugin' => $pluginName,
|
||||
'MyExampleWidget' => $widgetClass,
|
||||
'Example Widget Name' => $this->makeTranslationIfPossible($pluginName, $widgetName),
|
||||
'About Matomo' => $category);
|
||||
$whitelistFiles = array('/Widgets', '/Widgets/MyExampleWidget.php');
|
||||
|
||||
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
|
||||
|
||||
$this->writeSuccessMessage($output, array(
|
||||
sprintf('plugins/%s/Widgets/%s.php generated.', $pluginName, $widgetClass),
|
||||
'You can now start implementing the <comment>render()</comment> method.',
|
||||
'Enjoy!'
|
||||
));
|
||||
}
|
||||
|
||||
private function getWidgetMethodName($methodName)
|
||||
{
|
||||
$methodName = trim($methodName);
|
||||
$methodName = str_replace(' ', '', $methodName);
|
||||
$methodName = preg_replace("/[^A-Za-z0-9]/", '', $methodName);
|
||||
|
||||
if (0 !== strpos(strtolower($methodName), 'get')) {
|
||||
$methodName = 'get' . ucfirst($methodName);
|
||||
}
|
||||
|
||||
return lcfirst($methodName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getWidgetName(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$validate = function ($widgetName) {
|
||||
if (empty($widgetName)) {
|
||||
throw new \InvalidArgumentException('Please enter the name of your widget');
|
||||
}
|
||||
|
||||
if (preg_match("/[^A-Za-z0-9 ]/", $widgetName)) {
|
||||
throw new \InvalidArgumentException('Only alpha numerical characters and whitespaces are allowed');
|
||||
}
|
||||
|
||||
return $widgetName;
|
||||
};
|
||||
|
||||
$widgetName = $input->getOption('widgetname');
|
||||
|
||||
if (empty($widgetName)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$widgetName = $dialog->askAndValidate($output, 'Enter the name of your Widget, for instance "Browser Families": ', $validate);
|
||||
} else {
|
||||
$validate($widgetName);
|
||||
}
|
||||
|
||||
$widgetName = ucfirst($widgetName);
|
||||
|
||||
return $widgetName;
|
||||
}
|
||||
|
||||
protected function getExistingCategories()
|
||||
{
|
||||
$categories = array();
|
||||
foreach (WidgetsList::get()->getWidgetConfigs() as $widget) {
|
||||
if ($widget->getCategoryId()) {
|
||||
$categories[] = Piwik::translate($widget->getCategoryId());
|
||||
}
|
||||
}
|
||||
$categories = array_values(array_unique($categories));
|
||||
|
||||
return $categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
protected function getCategory(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$validate = function ($category) {
|
||||
if (empty($category)) {
|
||||
throw new \InvalidArgumentException('Please enter the name of the category your widget should belong to');
|
||||
}
|
||||
|
||||
return $category;
|
||||
};
|
||||
|
||||
$category = $input->getOption('category');
|
||||
$categories = $this->getExistingCategories();
|
||||
|
||||
if (empty($category)) {
|
||||
$dialog = $this->getHelperSet()->get('dialog');
|
||||
$category = $dialog->askAndValidate($output, 'Enter the widget category, for instance "Visitor" (you can reuse any existing category or define a new one): ', $validate, false, null, $categories);
|
||||
} else {
|
||||
$validate($category);
|
||||
}
|
||||
|
||||
$translationKey = Translate::findTranslationKeyForTranslation($category);
|
||||
if (!empty($translationKey)) {
|
||||
return $translationKey;
|
||||
}
|
||||
|
||||
$category = ucfirst($category);
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 a name of an existing plugin.';
|
||||
|
||||
return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName);
|
||||
}
|
||||
|
||||
}
|
148
msd2/tracking/piwik/plugins/CoreConsole/Commands/GitCommit.php
Normal file
148
msd2/tracking/piwik/plugins/CoreConsole/Commands/GitCommit.php
Normal file
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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\Development;
|
||||
use Piwik\Plugin\ConsoleCommand;
|
||||
use Piwik\SettingsPiwik;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
*/
|
||||
class GitCommit extends ConsoleCommand
|
||||
{
|
||||
public function isEnabled()
|
||||
{
|
||||
return Development::isEnabled() && SettingsPiwik::isGitDeployment();
|
||||
}
|
||||
|
||||
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 committed 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;
|
||||
}
|
||||
}
|
59
msd2/tracking/piwik/plugins/CoreConsole/Commands/GitPull.php
Normal file
59
msd2/tracking/piwik/plugins/CoreConsole/Commands/GitPull.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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 Piwik\SettingsPiwik;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
*/
|
||||
class GitPull extends ConsoleCommand
|
||||
{
|
||||
public function isEnabled()
|
||||
{
|
||||
return SettingsPiwik::isGitDeployment();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
48
msd2/tracking/piwik/plugins/CoreConsole/Commands/GitPush.php
Normal file
48
msd2/tracking/piwik/plugins/CoreConsole/Commands/GitPush.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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\Development;
|
||||
use Piwik\Plugin\ConsoleCommand;
|
||||
use Piwik\SettingsPiwik;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
*/
|
||||
class GitPush extends ConsoleCommand
|
||||
{
|
||||
public function isEnabled()
|
||||
{
|
||||
return Development::isEnabled() && SettingsPiwik::isGitDeployment();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @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\Container\StaticContainer;
|
||||
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)
|
||||
{
|
||||
$path = StaticContainer::get('path.tmp') . '/logs/';
|
||||
$cmd = sprintf('tail -f %s*.log', $path);
|
||||
|
||||
$output->writeln('Executing command: ' . $cmd);
|
||||
passthru($cmd);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user