Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/Cli/Command/AddOn/Create.php
<?php

namespace XF\Cli\Command\AddOn;

use
Symfony\Component\Console\Command\Command;
use
Symfony\Component\Console\Helper\QuestionHelper;
use
Symfony\Component\Console\Input\ArrayInput;
use
Symfony\Component\Console\Input\InputInterface;
use
Symfony\Component\Console\Output\OutputInterface;
use
Symfony\Component\Console\Question\ConfirmationQuestion;
use
Symfony\Component\Console\Question\Question;
use
XF\Util\File;
use
XF\Util\Json;

use function
intval;

class
Create extends Command
{
    protected function
configure()
    {
       
$this
           
->setName('xf-addon:create')
            ->
setDescription('Creates an XF add-on and writes out the basic addon.json file.');
    }

    protected function
execute(InputInterface $input, OutputInterface $output)
    {
       
/** @var QuestionHelper $helper */
       
$helper = $this->getHelper('question');
       
       
$question = new Question("<question>Enter an ID for this add-on:</question> ");
       
$question->setValidator($this->getAddOnQuestionFieldValidator('addon_id'));
       
$addOnId = $helper->ask($input, $output, $question);
       
$output->writeln("");

       
$devOutput = \XF::app()->developmentOutput();
        if (
$devOutput->isAddOnSkipped($addOnId))
        {
           
$output->writeln("<error>Development output for this add-on ID has been disabled. Cannot continue.</error>");
            if (
strtolower(substr($addOnId, 0, 2)) == 'xf')
            {
               
$output->writeln("Note: use of add-on IDs starting with 'xf' is strongly discouraged.");
            }
            return
1;
        }

       
$checkPath = \XF::getAddOnDirectory();

        if (
strpos($addOnId, '/') !== false)
        {
           
$addOnIdParts = explode('/', $addOnId);
           
$vendor = reset($addOnIdParts);

            if (
file_exists(\XF::getAddOnDirectory() . \XF::$DS . $vendor))
            {
               
$checkPath = \XF::getAddOnDirectory() . \XF::$DS . $vendor;
            }

            if (
$addOnDir = opendir(\XF::getAddOnDirectory()))
            {
                while ((
$dir = readdir($addOnDir)) !== false)
                {
                    if (
strcasecmp($dir, $vendor) == 0 && $dir != $vendor)
                    {
                       
$output->writeln("<error>The '{$checkPath}' directory conflicts with an existing directory with the same name but a different case.</error>");
                        return
1;
                    }
                }
               
closedir($addOnDir);
            }
        }

        if (!
is_writable($checkPath))
        {
           
$checkPathPrintable = str_replace(\XF::getRootDirectory() . \XF::$DS, '', $checkPath);
           
$output->writeln("<error>The '{$checkPathPrintable}' directory is not writable.</error>");
            return
1;
        }

       
$addOnObj = new \XF\AddOn\AddOn($addOnId, \XF::app()->addOnManager());

       
$jsonPath = $addOnObj->getJsonPath();

        if (
file_exists($jsonPath))
        {
           
$output->writeln("<error>The addon.json file already exists at {$jsonPath}. You can create the add-on by installing it from the Admin CP.</error>");
            return
1;
        }

       
$question = new Question("<question>Enter a title:</question> ");
       
$question->setValidator($this->getAddOnQuestionFieldValidator('title'));
       
$title = $helper->ask($input, $output, $question);
       
$output->writeln("");

       
$question = new Question("<question>Enter a version ID.</question><info> This integer will be used for internal version comparisons. Each release of your add-on should increase this number: </info> ");
       
$question->setValidator(function($value)
        {
            if (!
preg_match('/^[0-9]+$/', $value))
            {
                throw new \
InvalidArgumentException("The version ID should contain numeric values only.");
            }
            return
$value;
        });
       
$versionId = $helper->ask($input, $output, $question);
       
$output->writeln("");

       
$versionString = \XF::repository('XF:AddOn')->inferVersionStringFromId($versionId);
        if (
$versionString)
        {
           
$output->writeln("<info>Version string set to: {$versionString}</info>");
           
$output->writeln("");
        }
        else
        {
           
$question = new Question("<question>Enter the version string.</question><info> e.g. 1.0.0 Alpha</info> ");
           
$question->setValidator($this->getAddOnQuestionFieldValidator('version_string'));
           
$versionString = $helper->ask($input, $output, $question);
           
$output->writeln("");
        }

       
$question = new ConfirmationQuestion("<question>Does this add-on supersede a XenForo 1 add-on? (y/n)</question> ");
       
$legacyAddOnId = null;
        if (
$helper->ask($input, $output, $question))
        {
           
$question = new Question("<question>What is the old add-on ID?</question> (Leave blank if unchanged.) ", $addOnId);
           
$legacyAddOnId = $helper->ask($input, $output, $question);
        }

       
$addOn = null;
       
$renamedLegacy = false;

        if (
$legacyAddOnId)
        {
           
$addOn = \XF::em()->find('XF:AddOn', $legacyAddOnId);
            if (
$addOn)
            {
               
$renamedLegacy = true;
            }
            else
            {
               
$output->writeln("<warning>No legacy add-on could be found with ID {$legacyAddOnId}. No data will be updated to be associated with this add-on.</warning>");
               
$addOn = \XF::em()->create('XF:AddOn');
            }
        }
        else
        {
           
$addOn = \XF::em()->create('XF:AddOn');
        }

       
$addOn->bulkSet([
           
'addon_id' => $addOnId,
           
'title' => $title,
           
'version_id' => $versionId,
           
'version_string' => $versionString,
           
'active' => true
       
]);

       
$output->writeln("");

       
$addOn->preSave();
        if (
$errors = $addOn->getErrors())
        {
           
$output->writeln("<error>An unexpected error occurred while saving the add-on: " . reset($errors) . "</error>");
            return
1;
        }

       
$addOn->save();

       
File::createDirectory($addOnObj->getAddOnDirectory(), false);

       
$json = [
           
'title' => $addOn->title,
           
'version_string' => $addOn->version_string,
           
'version_id' => intval($addOn->version_id)
        ];
        if (
$legacyAddOnId)
        {
           
$json['legacy_addon_id'] = $legacyAddOnId;
        }

       
$written = File::writeFile($jsonPath, Json::jsonEncodePretty(
           
$addOnObj->prepareJsonFile($json)
        ),
false);

        if (
$written)
        {
           
$addOn->fastUpdate('json_hash', \XF\Util\Hash::hashTextFile($jsonPath, 'sha256'));
           
$output->writeln("The addon.json file was successfully written out to $jsonPath");
        }
        else
        {
           
$output->writeln("<error>The addon.json file could not be written out to $jsonPath</error>");
        }

       
$output->writeln("");

       
$setupPath = $addOnObj->getSetupPath();

       
$question = new ConfirmationQuestion("<question>Does your add-on need a Setup file? (y/n)</question> ");
        if (
$helper->ask($input, $output, $question))
        {
           
$output->writeln("");

           
$question = new ConfirmationQuestion("<question>Does your Setup need to support running multiple steps? (y/n)</question> ");
            if (
$helper->ask($input, $output, $question))
            {
               
$setupContent = <<< SETUP
<?php

namespace
{$addOnObj->prepareAddOnIdForClass()};

use XF\AddOn\AbstractSetup;
use XF\AddOn\StepRunnerInstallTrait;
use XF\AddOn\StepRunnerUninstallTrait;
use XF\AddOn\StepRunnerUpgradeTrait;

class Setup extends AbstractSetup
{
    use StepRunnerInstallTrait;
    use StepRunnerUpgradeTrait;
    use StepRunnerUninstallTrait;
}
SETUP;

            }
            else
            {
               
$setupContent = <<< SETUP
<?php

namespace
{$addOnObj->prepareAddOnIdForClass()};

use XF\AddOn\AbstractSetup;

class Setup extends AbstractSetup
{
    public function install(array \$stepParams = [])
    {
        // TODO: Implement install() method.
    }

    public function upgrade(array \$stepParams = [])
    {
        // TODO: Implement upgrade() method.
    }

    public function uninstall(array \$stepParams = [])
    {
        // TODO: Implement uninstall() method.
    }
}
SETUP;
            }

           
$output->writeln("");

           
$written = File::writeFile($setupPath, $setupContent, false);
            if (
$written)
            {
               
$output->writeln("The Setup.php file was successfully written out to $setupPath");
            }
            else
            {
               
$output->writeln("The Setup.php file could not be written out to $setupPath");
            }
        }
        else
        {
           
$output->writeln("");
           
$output->writeln("If you change your mind, create a file named Setup.php in " . dirname($setupPath) . " which should extend AbstractSetup and optionally use one of the StepRunnerX traits.");
        }

       
$config = \XF::config();
        if (
$renamedLegacy && $config['development']['enabled'])
        {
           
$output->writeln("");

           
$question = new ConfirmationQuestion("<question>Existing legacy data was associated with this add-on. Would you like to export the development data now? (y/n)</question> ");
            if (
$helper->ask($input, $output, $question))
            {
               
$command = $this->getApplication()->find('xf-dev:export');
               
$childInput = new ArrayInput([
                   
'command' => 'xf-dev:export',
                   
'--addon' => $addOn->addon_id
               
]);
               
$command->run($childInput, $output);
            }
        }

        return
0;
    }

   
/**
     * @param $key
     *
     * @return \Closure
     */
   
protected function getAddOnQuestionFieldValidator($key)
    {
        return function(
$value) use($key)
        {
           
$addOn = \XF::em()->create('XF:AddOn');

           
$valid = $addOn->set($key, $value);
            if (!
$valid)
            {
               
$errors = $addOn->getErrors();
                if (isset(
$errors[$key]))
                {
                    throw new \
InvalidArgumentException($errors[$key]);
                }
            }
            return
$value;
        };
    }
}