3 namespace wcf\system\form\builder\field\devtools\project;
5 use wcf\data\application\Application;
6 use wcf\data\package\installation\plugin\PackageInstallationPlugin;
7 use wcf\data\package\installation\plugin\PackageInstallationPluginList;
8 use wcf\system\application\ApplicationHandler;
9 use wcf\system\form\builder\field\AbstractFormField;
10 use wcf\system\form\builder\field\TDefaultIdFormField;
11 use wcf\system\form\builder\field\validation\FormFieldValidationError;
15 * Form field implementation for the instructions of a devtools project.
17 * @author Matthias Schmidt
18 * @copyright 2001-2019 WoltLab GmbH
19 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
22 final class DevtoolsProjectInstructionsFormField extends AbstractFormField
24 use TDefaultIdFormField;
27 * list of available applications
28 * @var null|Application[]
30 protected $applications;
33 * list of available package installation plugins
34 * @var null|PackageInstallationPlugin[]
36 protected $packageInstallationPlugins;
41 protected $templateName = 'shared_devtoolsProjectInstructionsFormField';
46 protected $value = [];
49 * names of package installation plugins that support the `application`
53 protected static $applicationPips = [
61 * Returns the applications for which file-based package installation plugins
64 * @return Application[]
66 public function getApplications()
68 if ($this->applications === null) {
69 $this->applications = [];
70 foreach (ApplicationHandler::getInstance()->getApplications() as $application) {
71 $this->applications[$application->getAbbreviation()] = $application;
74 \uasort($this->applications, static function (Application $app1, Application $app2) {
75 return $app1->getAbbreviation() <=> $app2->getAbbreviation();
79 return $this->applications;
85 public function getHtml()
87 if ($this->requiresLabel() && $this->getLabel() === null) {
88 throw new \UnexpectedValueException("Form field '{$this->getPrefixedId()}' requires a label.");
91 $this->removeClass('formError');
93 return WCF::getTPL()->fetch(
97 'apps' => $this->getApplications(),
99 'packageInstallationPlugins' => $this->getPackageInstallationPlugins(),
106 * Returns the package installation plugins that can be used for instructions.
108 * @return PackageInstallationPlugin[]
110 public function getPackageInstallationPlugins()
112 if ($this->packageInstallationPlugins === null) {
113 $packageInstallationPluginList = new PackageInstallationPluginList();
114 $packageInstallationPluginList->sqlOrderBy = 'pluginName ASC';
115 $packageInstallationPluginList->readObjects();
117 foreach ($packageInstallationPluginList as $packageInstallationPlugin) {
118 $this->packageInstallationPlugins[$packageInstallationPlugin->pluginName] = $packageInstallationPlugin;
122 return $this->packageInstallationPlugins;
128 public function readValue()
131 $this->getDocument()->hasRequestData($this->getPrefixedId())
132 && \is_array($this->getDocument()->getRequestData($this->getPrefixedId()))
134 $this->value = $this->getDocument()->getRequestData($this->getPrefixedId());
145 public function validate()
147 // everything is already validated by JavaScript thus we skip
148 // reporting specific errors and simply remove manipulated values
149 $this->value(\array_filter($this->getValue(), function ($instructions) {
150 if (!\is_array($instructions)) {
154 // ensure that all relevant elements are present
155 if (!isset($instructions['type'])) {
158 if (!isset($instructions['instructions'])) {
159 $instructions['instructions'] = [];
161 if (!\is_array($instructions['instructions'])) {
165 if ($instructions['type'] !== 'install' && $instructions['type'] !== 'update') {
169 if ($instructions['type'] === 'update') {
170 if (!isset($instructions['fromVersion'])) {
174 // Do not validate the actual version. The actual installation process
175 // does not validate it either and we should not *silently* drop uncommon
176 // (but valid) formats.
177 // The JavaScript validation should be sufficient to nudge the user
178 // into the correct direction.
181 foreach ($instructions['instructions'] as $instruction) {
182 if (!isset($instruction['pip']) || !isset($this->getPackageInstallationPlugins()[$instruction['pip']])) {
186 if (!empty($instruction['application'])) {
187 if (!isset($this->getApplications()[$instruction['application']])) {
191 if (!\in_array($instruction['pip'], static::$applicationPips)) {
196 $instruction['runStandalone'] = $instruction['runStandalone'] ?? 0;
197 $instruction['value'] = $instruction['value'] ?? '';
203 // the only thing left to validate is to ensure that there are
204 // installation instructions
205 $hasInstallInstructions = false;
206 foreach ($this->getValue() as $instructions) {
207 if ($instructions['type'] === 'install') {
208 $hasInstallInstructions = true;
213 if (!$hasInstallInstructions) {
214 $this->addValidationError(
215 new FormFieldValidationError(
216 'noInstallationInstructions',
217 'wcf.acp.devtools.project.instructions.error.noInstallationInstructions'
226 protected static function getDefaultId()
228 return 'instructions';