e1f4830fcf3e07a1f9e911da17777c4b2051e86b
[GitHub/WoltLab/WCF.git] /
1 <?php
2 namespace wcf\system\form\builder\field\devtools\project;
3 use wcf\data\application\Application;
4 use wcf\data\package\installation\plugin\PackageInstallationPlugin;
5 use wcf\data\package\installation\plugin\PackageInstallationPluginList;
6 use wcf\data\package\Package;
7 use wcf\system\application\ApplicationHandler;
8 use wcf\system\form\builder\field\AbstractFormField;
9 use wcf\system\form\builder\field\TDefaultIdFormField;
10 use wcf\system\form\builder\field\validation\FormFieldValidationError;
11
12 /**
13 * Form field implementation for the instructions of a devtools project.
14 *
15 * @author Matthias Schmidt
16 * @copyright 2001-2018 WoltLab GmbH
17 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
18 * @package WoltLabSuite\Core\System\Form\Builder\Field\Devtools\Project
19 * @since 3.2
20 */
21 class DevtoolsProjectInstructionsFormField extends AbstractFormField {
22 use TDefaultIdFormField;
23
24 /**
25 * list of available applications
26 * @var null|Application[]
27 */
28 protected $applications;
29
30 /**
31 * list of available package installation plugins
32 * @var null|PackageInstallationPlugin[]
33 */
34 protected $packageInstallationPlugins = null;
35
36 /**
37 * @inheritDoc
38 */
39 protected $templateName = '__devtoolsProjectInstructionsFormField';
40
41 /**
42 * @inheritDoc
43 */
44 protected $__value = [];
45
46 /**
47 * names of package installation plugins that support the `application`
48 * attribute
49 * @var string[]
50 */
51 protected static $applicationPips = [
52 'acpTemplate',
53 'file',
54 'script',
55 'template'
56 ];
57
58 /**
59 * Returns the applications for which file-based package installation plugins
60 * can deliver files.
61 *
62 * @return Application[]
63 */
64 public function getApplications() {
65 if ($this->applications === null) {
66 $this->applications = [];
67 foreach (ApplicationHandler::getInstance()->getApplications() as $application) {
68 $this->applications[$application->getAbbreviation()] = $application;
69 }
70
71 uasort($this->applications, function(Application $app1, Application $app2) {
72 return $app1->getAbbreviation() <=> $app2->getAbbreviation();
73 });
74 }
75
76 return $this->applications;
77 }
78
79 /**
80 * @inheritDoc
81 */
82 public function getHtmlVariables() {
83 return [
84 'apps' => $this->getApplications(),
85 'packageInstallationPlugins' => $this->getPackageInstallationPlugins()
86 ];
87 }
88
89 /**
90 * Returns the package installation plugins that can be used for instructions.
91 *
92 * @return PackageInstallationPlugin[]
93 */
94 public function getPackageInstallationPlugins() {
95 if ($this->packageInstallationPlugins === null) {
96 $packageInstallationPluginList = new PackageInstallationPluginList();
97 $packageInstallationPluginList->sqlOrderBy = 'pluginName ASC';
98 $packageInstallationPluginList->readObjects();
99
100 foreach ($packageInstallationPluginList as $packageInstallationPlugin) {
101 $this->packageInstallationPlugins[$packageInstallationPlugin->pluginName] = $packageInstallationPlugin;
102 }
103 }
104
105 return $this->packageInstallationPlugins;
106 }
107
108 /**
109 * @inheritDoc
110 */
111 public function readValue() {
112 if ($this->getDocument()->hasRequestData($this->getPrefixedId()) && is_array($this->getDocument()->getRequestData($this->getPrefixedId()))) {
113 $this->__value = $this->getDocument()->getRequestData($this->getPrefixedId());
114 }
115 else {
116 $this->__value = [];
117 }
118 }
119
120 /**
121 * @inheritDoc
122 */
123 public function validate() {
124 // everything is already validated by JavaScript thus we skip
125 // reporting specific errors and simply remove manipulated values
126 $this->value(array_filter($this->getValue(), function($instructions) {
127 if (!is_array($instructions)) {
128 return false;
129 }
130
131 // ensure that all relevant elements are present
132 if (!isset($instructions['type'])) {
133 return false;
134 }
135 if (!isset($instructions['instructions'])) {
136 $instructions['instructions'] = [];
137 }
138 if (!is_array($instructions['instructions'])) {
139 return false;
140 }
141
142 if ($instructions['type'] !== 'install' && $instructions['type'] !== 'update') {
143 return false;
144 }
145
146 if ($instructions['type'] === 'update') {
147 if (!isset($instructions['fromVersion'])) {
148 return false;
149 }
150
151 if (strpos($instructions['fromVersion'], '*') !== false) {
152 if (!Package::isValidVersion(str_replace('*', '0', $instructions['fromVersion']))) {
153 return false;
154 }
155 }
156 else if (!Package::isValidVersion($instructions['fromVersion'])) {
157 return false;
158 }
159 }
160
161 foreach ($instructions['instructions'] as $instruction) {
162 if (!isset($instruction['pip']) || !isset($this->getPackageInstallationPlugins()[$instruction['pip']])) {
163 return false;
164 }
165
166 if (!empty($instruction['application'])) {
167 if (!isset($this->getApplications()[$instruction['application']])) {
168 return false;
169 }
170
171 if (!in_array($instruction['pip'], static::$applicationPips)) {
172 return false;
173 }
174 }
175
176 $instruction['runStandalone'] = $instruction['runStandalone'] ?? 0;
177 $instruction['value'] = $instruction['value'] ?? '';
178 }
179
180 return true;
181 }));
182
183 // the only thing left to validate is to ensure that there are
184 // installation instructions
185 $hasInstallInstructions = false;
186 foreach ($this->getValue() as $instructions) {
187 if ($instructions['type'] === 'install') {
188 $hasInstallInstructions = true;
189 break;
190 }
191 }
192
193 if (!$hasInstallInstructions) {
194 $this->addValidationError(
195 new FormFieldValidationError(
196 'noInstallationInstructions',
197 'wcf.acp.devtools.project.instructions.error.noInstallationInstructions'
198 )
199 );
200 }
201 }
202
203 /**
204 * @inheritDoc
205 */
206 protected static function getDefaultId() {
207 return 'instructions';
208 }
209 }