Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add export command for managed entities and afforms #312

Merged
merged 10 commits into from
Nov 16, 2023
2 changes: 2 additions & 0 deletions src/CRM/CivixBundle/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use CRM\CivixBundle\Command\AddEntityCommand;
use CRM\CivixBundle\Command\AddEntityBoilerplateCommand;
use CRM\CivixBundle\Command\AddFormCommand;
use CRM\CivixBundle\Command\AddManagedEntityCommand;
use CRM\CivixBundle\Command\AddPageCommand;
use CRM\CivixBundle\Command\AddReportCommand;
use CRM\CivixBundle\Command\AddSearchCommand;
Expand Down Expand Up @@ -59,6 +60,7 @@ public function createCommands($context = 'default') {
$commands[] = new AddEntityCommand();
$commands[] = new AddEntityBoilerplateCommand();
$commands[] = new AddFormCommand();
$commands[] = new AddManagedEntityCommand();
$commands[] = new AddPageCommand();
$commands[] = new AddReportCommand();
$commands[] = new AddSearchCommand();
Expand Down
61 changes: 61 additions & 0 deletions src/CRM/CivixBundle/Builder/Content.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php
namespace CRM\CivixBundle\Builder;

use CRM\CivixBundle\Builder;
use Symfony\Component\Console\Output\OutputInterface;

/**
* Simply write a string to a file
*/
class Content implements Builder {

private $content;
protected $path;
protected $overwrite;

/**
* @param string $content
* @param string $path
* @param bool|string $overwrite TRUE (always overwrite), FALSE (preserve with error), 'ignore' (preserve quietly)
* @param
*/
public function __construct(string $content, string $path, $overwrite = FALSE) {
$this->content = $content;
$this->path = $path;
$this->overwrite = $overwrite;
}

public function loadInit(&$ctx) {
}

public function init(&$ctx) {
}

public function load(&$ctx) {
}

/**
* Write the content
*/
public function save(&$ctx, OutputInterface $output) {
$parent = dirname($this->path);
if (!is_dir($parent)) {
mkdir($parent, Dirs::MODE, TRUE);
}
if (file_exists($this->path) && $this->overwrite === 'ignore') {
// do nothing
}
elseif (file_exists($this->path) && !$this->overwrite) {
$output->writeln("<error>Skip " . $this->path . ": file already exists</error>");
}
else {
$output->writeln("<info>Write</info> " . $this->path);
file_put_contents($this->path, $this->getContent($ctx));
}
}

protected function getContent($ctx): string {
return $this->content;
}

}
11 changes: 10 additions & 1 deletion src/CRM/CivixBundle/Builder/Dirs.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@ class Dirs implements Builder {
// Note: Permissions will be further restricted by umask
const MODE = 0777;

public function __construct($paths) {
/**
* @var string[]
*/
private $paths;

public function __construct($paths = []) {
$this->paths = $paths;
}

public function addPath(string $path) {
$this->paths[] = $path;
}

public function loadInit(&$ctx) {
}

Expand Down
4 changes: 4 additions & 0 deletions src/CRM/CivixBundle/Builder/Info.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ public function getExtensionName() {
return empty($this->xml->name) ? 'FIXME' : $this->xml->name;
}

public function getExtensionUtilClass(): string {
return str_replace('/', '_', $this->getNamespace()) . '_ExtensionUtil';
}

/**
* Get the namespace into which civix should place files
* @return string
Expand Down
77 changes: 70 additions & 7 deletions src/CRM/CivixBundle/Builder/PhpData.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use Symfony\Component\VarExporter\VarExporter;

/**
* Read/write a serialized data file based on PHP's var_export() format
* Write a data file in PHP format
*/
class PhpData implements Builder {

Expand All @@ -21,10 +21,20 @@ class PhpData implements Builder {
protected $data;

/**
* @var
* @var string
*/
protected $header;

/**
* @var string[]
*/
private $keysToTranslate;

/**
* @var string
*/
private $extensionUtil;

public function __construct($path, $header = NULL) {
$this->path = $path;
$this->header = $header;
Expand Down Expand Up @@ -65,27 +75,80 @@ public function load(&$ctx) {
$this->data = include $this->path;
}

/**
* Specify fields that will be wrapped in E::ts()
*
* @param array $keysToTranslate
* @return void
*/
public function useTs(array $keysToTranslate) {
$this->keysToTranslate = $keysToTranslate;
}

/**
* Adds `use Foo_ExtensionUtil as E;` to the top of the file
*
* @param string $extensionUtilClass
* @return void
*/
public function useExtensionUtil(string $extensionUtilClass) {
$this->extensionUtil = $extensionUtilClass;
}

/**
* Write the xml document
*/
public function save(&$ctx, OutputInterface $output) {
$output->writeln("<info>Write</info> " . $this->path);

$content = "<?php\n";
if ($this->extensionUtil) {
$content .= "use $this->extensionUtil as E;\n";
}
if ($this->header) {
$content .= $this->header;
}
$content .= "\nreturn ";
$content .= preg_replace_callback('/^ +/m',
// VarExporter indents with 4x spaces. Civi/Drupal code standard is 2x spaces.
$data = $this->reduceIndentation(VarExporter::export($this->data));
$data = $this->ucConstants($data);
if ($this->keysToTranslate) {
$data = $this->translateStrings($data, $this->keysToTranslate);
}
$content .= "$data;\n";
file_put_contents($this->path, $content);
}

/**
* VarExporter indents with 4x spaces. Civi/Drupal code standard is 2x spaces.
*/
private function reduceIndentation(string $data): string {
return preg_replace_callback('/^ +/m',
function($m) {
$spaces = $m[0];
return substr($spaces, 0, ceil(strlen($spaces) / 2));
},
VarExporter::export($this->data)
$data
);
$content .= ";\n";
file_put_contents($this->path, $content);
}

/**
* Uppercase constants to match Civi/Drupal code standard
*/
private function ucConstants(string $data): string {
foreach (['null', 'false', 'true'] as $const) {
$uc = strtoupper($const);
$data = str_replace(" $const,", " $uc,", $data);
}
return $data;
}

/**
* Wrap strings in E::ts()
*/
private function translateStrings(string $data, array $keysToTranslate): string {
$keys = implode('|', array_unique($keysToTranslate));
$data = preg_replace("/'($keys)' => ('[^']+'),/", "'\$1' => E::ts(\$2),", $data);
return $data;
}

}
38 changes: 4 additions & 34 deletions src/CRM/CivixBundle/Builder/Template.php
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
<?php
namespace CRM\CivixBundle\Builder;

use CRM\CivixBundle\Builder;
use CRM\CivixBundle\Services;
use Symfony\Component\Console\Output\OutputInterface;

/**
* Build/update a file based on a template
*/
class Template implements Builder {
class Template extends Content {

protected $template;
protected $path;
protected $xml;
protected $templateEngine;
protected $enable;

/**
* @param string $template
Expand All @@ -23,41 +19,15 @@ class Template implements Builder {
* @param \Symfony\Component\Templating\EngineInterface|null $templateEngine
* @param
*/
public function __construct(string $template, string $path, $overwrite, $templateEngine = NULL) {
public function __construct(string $template, string $path, $overwrite = FALSE, $templateEngine = NULL) {
$this->template = $template;
$this->path = $path;
$this->overwrite = $overwrite;
$this->templateEngine = $templateEngine ?: Services::templating();
$this->enable = FALSE;
}

public function loadInit(&$ctx) {
}

public function init(&$ctx) {
}

public function load(&$ctx) {
}

/**
* Write the xml document
*/
public function save(&$ctx, OutputInterface $output) {
$parent = dirname($this->path);
if (!is_dir($parent)) {
mkdir($parent, Dirs::MODE, TRUE);
}
if (file_exists($this->path) && $this->overwrite === 'ignore') {
// do nothing
}
elseif (file_exists($this->path) && !$this->overwrite) {
$output->writeln("<error>Skip " . $this->path . ": file already exists</error>");
}
else {
$output->writeln("<info>Write</info> " . $this->path);
file_put_contents($this->path, $this->templateEngine->render($this->template, $ctx));
}
protected function getContent($ctx): string {
return $this->templateEngine->render($this->template, $ctx);
}

}
22 changes: 22 additions & 0 deletions src/CRM/CivixBundle/Command/AbstractCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,35 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Style\SymfonyStyle;

abstract class AbstractCommand extends Command {

protected function configure() {
$this->addOption('yes', NULL, InputOption::VALUE_NONE, 'Answer yes to any questions');
}

/**
* @var \Symfony\Component\Console\Style\StyleInterface
*/
private $io;

/**
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
*/
protected function initialize(InputInterface $input, OutputInterface $output) {
parent::initialize($input, $output);
$this->io = new SymfonyStyle($input, $output);
}

/**
* @return \Symfony\Component\Console\Style\StyleInterface
*/
protected function getIO() {
return $this->io;
}

protected function confirm(InputInterface $input, OutputInterface $output, $message, $default = TRUE) {
$message = '<info>' . $message . '</info>'; /* FIXME Let caller stylize */
if ($input->getOption('yes')) {
Expand Down
Loading