Commit | Line | Data |
---|---|---|
d7424422 AE |
1 | <?php |
2 | namespace wcf\system\devtools\pip; | |
3 | use wcf\data\devtools\project\DevtoolsProject; | |
4 | use wcf\data\package\installation\plugin\PackageInstallationPlugin; | |
5 | use wcf\data\DatabaseObjectDecorator; | |
6 | use wcf\system\application\ApplicationHandler; | |
d7424422 | 7 | use wcf\system\WCF; |
d4955651 AE |
8 | use wcf\util\FileUtil; |
9 | use wcf\util\JSON; | |
d7424422 AE |
10 | |
11 | /** | |
12 | * Wrapper class for package installation plugins for use with the sync feature. | |
13 | * | |
14 | * @author Alexander Ebert | |
15 | * @copyright 2001-2017 WoltLab GmbH | |
16 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> | |
17 | * @package WoltLabSuite\Core\System\Devtools\Pip | |
18 | * @since 3.1 | |
19 | * | |
20 | * @method PackageInstallationPlugin getDecoratedObject() | |
21 | * @mixin PackageInstallationPlugin | |
22 | */ | |
23 | class DevtoolsPip extends DatabaseObjectDecorator { | |
24 | /** | |
25 | * @inheritDoc | |
26 | */ | |
27 | protected static $baseClass = PackageInstallationPlugin::class; | |
28 | ||
29 | /** | |
30 | * Returns true if the PIP class can be found. | |
31 | * | |
32 | * @return boolean | |
33 | */ | |
34 | public function classExists() { | |
35 | return class_exists($this->getDecoratedObject()->className); | |
36 | } | |
37 | ||
38 | /** | |
39 | * Returns true if the PIP is expected to be idempotent. | |
40 | * | |
41 | * @return boolean | |
42 | */ | |
43 | public function isIdempotent() { | |
44 | return is_subclass_of($this->getDecoratedObject()->className, IIdempotentPackageInstallationPlugin::class); | |
45 | } | |
46 | ||
47 | /** | |
48 | * Returns the default filename of this PIP. | |
49 | * | |
50 | * @return string | |
51 | */ | |
52 | public function getDefaultFilename() { | |
53 | return call_user_func([$this->getDecoratedObject()->className, 'getDefaultFilename']); | |
54 | } | |
55 | ||
d4955651 AE |
56 | public function getEffectiveDefaultFilename() { |
57 | return './' . preg_replace('~\.tar$~', '/', $this->getDefaultFilename()); | |
58 | } | |
59 | ||
d7424422 AE |
60 | /** |
61 | * Returns true if the PIP exists, has a default filename and is idempotent. | |
62 | * | |
63 | * @return boolean | |
64 | */ | |
65 | public function isSupported() { | |
66 | return $this->classExists() && $this->getDefaultFilename() && $this->isIdempotent(); | |
67 | } | |
68 | ||
d4955651 AE |
69 | public function getSyncDependencies($toJson = true) { |
70 | $dependencies = call_user_func([$this->getDecoratedObject()->className, 'getSyncDependencies']); | |
71 | ||
72 | return ($toJson) ? JSON::encode($dependencies) : $dependencies; | |
73 | } | |
74 | ||
d7424422 AE |
75 | /** |
76 | * Returns the first validation error. | |
77 | * | |
78 | * @return string | |
79 | */ | |
80 | public function getFirstError() { | |
81 | if (!$this->classExists()) { | |
82 | return WCF::getLanguage()->getDynamicVariable('wcf.acp.devtools.pip.error.className', ['className' => $this->getDecoratedObject()->className]); | |
83 | } | |
84 | else if (!$this->isIdempotent()) { | |
85 | return WCF::getLanguage()->get('wcf.acp.devtools.pip.error.notIdempotent'); | |
86 | } | |
87 | else if (!$this->getDefaultFilename()) { | |
88 | return WCF::getLanguage()->get('wcf.acp.devtools.pip.error.defaultFilename'); | |
89 | } | |
90 | ||
91 | throw new \LogicException("Please call `isSupported()` to check for potential errors."); | |
92 | } | |
93 | ||
94 | /** | |
95 | * Returns the list of valid targets for this pip. | |
96 | * | |
97 | * @param DevtoolsProject $project | |
98 | * @return string[] | |
99 | */ | |
100 | public function getTargets(DevtoolsProject $project) { | |
101 | if (!$this->isSupported()) { | |
102 | return []; | |
103 | } | |
104 | ||
105 | $path = $project->path; | |
106 | $defaultFilename = $this->getDefaultFilename(); | |
107 | $targets = []; | |
108 | ||
109 | // the core uses a significantly different file layout | |
110 | if ($project->isCore()) { | |
111 | switch ($this->getDecoratedObject()->pluginName) { | |
112 | case 'acpTemplate': | |
113 | case 'file': | |
114 | case 'template': | |
115 | // these pips are satisfied by definition | |
116 | return [$defaultFilename]; | |
117 | ||
118 | case 'language': | |
119 | foreach (glob($path . 'wcfsetup/install/lang/*.xml') as $file) { | |
120 | $targets[] = basename($file); | |
121 | } | |
122 | ||
123 | // `glob()` returns files in an arbitrary order | |
124 | sort($targets, SORT_NATURAL); | |
125 | ||
126 | return $targets; | |
127 | } | |
128 | ||
129 | if (strpos($defaultFilename, '*') !== false) { | |
130 | foreach (glob($path . 'com.woltlab.wcf/' . $defaultFilename) as $file) { | |
131 | $targets[] = basename($file); | |
132 | } | |
133 | ||
134 | // `glob()` returns files in an arbitrary order | |
135 | sort($targets, SORT_NATURAL); | |
136 | } | |
137 | else { | |
138 | if (file_exists($path . 'com.woltlab.wcf/' . $defaultFilename)) { | |
139 | $targets[] = $defaultFilename; | |
140 | } | |
141 | } | |
142 | } | |
143 | else { | |
144 | if (preg_match('~^(?<filename>.*)\.tar$~', $defaultFilename, $match)) { | |
145 | if (is_dir($path . $match['filename'])) { | |
146 | $targets[] = $defaultFilename; | |
147 | } | |
148 | ||
149 | // check for application-specific pips too | |
150 | foreach (ApplicationHandler::getInstance()->getAbbreviations() as $abbreviation) { | |
151 | if (is_dir($path . $match['filename'] . '_' . $abbreviation)) { | |
152 | $targets[] = $match['filename'] . "_{$abbreviation}.tar"; | |
153 | } | |
154 | } | |
155 | } | |
156 | else { | |
157 | if (strpos($defaultFilename, '*') !== false) { | |
158 | foreach (glob($path . $defaultFilename) as $file) { | |
159 | $targets[] = basename($file); | |
160 | } | |
161 | ||
162 | // `glob()` returns files in an arbitrary order | |
163 | sort($targets, SORT_NATURAL); | |
164 | } | |
165 | else { | |
166 | if (file_exists($path . $defaultFilename)) { | |
167 | $targets[] = $defaultFilename; | |
168 | } | |
169 | } | |
170 | } | |
171 | } | |
172 | ||
173 | return $targets; | |
174 | } | |
175 | ||
d4955651 AE |
176 | /** |
177 | * Computes and prepares the instruction value for the provided target file. | |
178 | * | |
179 | * @param DevtoolsProject $project | |
180 | * @param string $target | |
181 | * @return string | |
182 | */ | |
d7424422 AE |
183 | public function getInstructionValue(DevtoolsProject $project, $target) { |
184 | $defaultFilename = $this->getDefaultFilename(); | |
d4955651 AE |
185 | $pluginName = $this->getDecoratedObject()->pluginName; |
186 | $tar = $project->getPackageArchive()->getTar(); | |
187 | $tar->reset(); | |
d7424422 AE |
188 | |
189 | if ($project->isCore()) { | |
d4955651 | 190 | switch ($pluginName) { |
d7424422 AE |
191 | case 'acpTemplate': |
192 | case 'file': | |
193 | case 'template': | |
d4955651 AE |
194 | if ($pluginName === 'acpTemplate' || $pluginName === 'template') { |
195 | $path = ($pluginName === 'acpTemplate') ? 'wcfsetup/install/files/acp/templates/' : 'com.woltlab.wcf/templates/'; | |
196 | foreach (glob($project->path . $path . '*.tpl') as $template) { | |
197 | $tar->registerFile(basename($template), FileUtil::unifyDirSeparator($template)); | |
198 | } | |
199 | } | |
200 | else { | |
201 | $path = 'wcfsetup/install/files/'; | |
202 | ||
203 | $directory = new \RecursiveDirectoryIterator($project->path . $path); | |
204 | $filter = new \RecursiveCallbackFilterIterator($directory, function ($current) { | |
205 | /** @var \SplFileInfo $current */ | |
206 | $filename = $current->getFilename(); | |
207 | if ($filename[0] === '.') { | |
208 | // ignore dot files and files/directories starting with a dot | |
209 | return false; | |
210 | } | |
211 | else if ($filename === 'templates') { | |
212 | // ignores both `templates` and `acp/templates` | |
213 | return false; | |
214 | } | |
215 | ||
216 | return true; | |
217 | }); | |
218 | ||
219 | $iterator = new \RecursiveIteratorIterator($filter, \RecursiveIteratorIterator::SELF_FIRST); | |
220 | foreach ($iterator as $value => $item) { | |
221 | /** @var \SplFileInfo $item */ | |
222 | $itemPath = $item->getRealPath(); | |
223 | if (is_dir($itemPath)) continue; | |
224 | ||
225 | $tar->registerFile( | |
226 | FileUtil::getRelativePath($project->path . $path, $item->getPath()) . $item->getFilename(), | |
227 | $itemPath | |
228 | ); | |
229 | } | |
230 | } | |
231 | ||
232 | return $defaultFilename; | |
d7424422 AE |
233 | |
234 | case 'language': | |
235 | $filename = "wcfsetup/install/lang/{$target}"; | |
d4955651 | 236 | $tar->registerFile($filename, $project->path . $filename); |
d7424422 AE |
237 | |
238 | return $filename; | |
239 | ||
240 | default: | |
241 | $filename = "com.woltlab.wcf/{$target}"; | |
d4955651 | 242 | $tar->registerFile($filename, $project->path . $filename); |
d7424422 AE |
243 | |
244 | return $filename; | |
245 | } | |
246 | } | |
247 | else { | |
d4955651 AE |
248 | switch ($pluginName) { |
249 | case 'acpTemplate': | |
250 | case 'file': | |
251 | case 'template': | |
252 | if ($pluginName === 'acpTemplate' || $pluginName === 'template') { | |
253 | $path = ($pluginName === 'acpTemplate') ? 'acptemplates/' : 'templates/'; | |
254 | foreach (glob($project->path . $path . '*.tpl') as $template) { | |
255 | $tar->registerFile(basename($template), FileUtil::unifyDirSeparator($template)); | |
256 | } | |
257 | } | |
258 | else { | |
259 | $path = 'files/'; | |
260 | ||
261 | $directory = new \RecursiveDirectoryIterator($project->path . $path); | |
262 | $filter = new \RecursiveCallbackFilterIterator($directory, function ($current) { | |
263 | /** @var \SplFileInfo $current */ | |
264 | $filename = $current->getFilename(); | |
265 | if ($filename[0] === '.') { | |
266 | // ignore dot files and files/directories starting with a dot | |
267 | return false; | |
268 | } | |
269 | ||
270 | return true; | |
271 | }); | |
272 | ||
273 | $iterator = new \RecursiveIteratorIterator($filter, \RecursiveIteratorIterator::SELF_FIRST); | |
274 | foreach ($iterator as $value => $item) { | |
275 | /** @var \SplFileInfo $item */ | |
276 | $itemPath = $item->getRealPath(); | |
277 | if (is_dir($itemPath)) continue; | |
278 | ||
279 | $tar->registerFile( | |
280 | FileUtil::getRelativePath($project->path . $path, $item->getPath()) . $item->getFilename(), | |
281 | $itemPath | |
282 | ); | |
283 | } | |
284 | } | |
285 | ||
286 | return $defaultFilename; | |
287 | ||
288 | default: | |
289 | if (strpos($defaultFilename, '*') !== false) { | |
290 | $filename = str_replace('*', $target, $defaultFilename); | |
291 | $tar->registerFile($filename, $project->path . $filename); | |
292 | } | |
293 | else { | |
294 | $filename = "com.woltlab.wcf/{$target}"; | |
295 | $tar->registerFile($filename, $project->path . $filename); | |
296 | } | |
297 | ||
298 | return $filename; | |
d7424422 AE |
299 | } |
300 | } | |
d7424422 | 301 | } |
e7de794a | 302 | } |