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
;
12 * Wrapper class for package installation plugins for use with the sync feature.
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
20 * @method PackageInstallationPlugin getDecoratedObject()
21 * @mixin PackageInstallationPlugin
23 class DevtoolsPip
extends DatabaseObjectDecorator
{
27 protected static $baseClass = PackageInstallationPlugin
::class;
30 * Returns true if the PIP class can be found.
34 public function classExists() {
35 return class_exists($this->getDecoratedObject()->className
);
39 * Returns true if the PIP is expected to be idempotent.
43 public function isIdempotent() {
44 return is_subclass_of($this->getDecoratedObject()->className
, IIdempotentPackageInstallationPlugin
::class);
48 * Returns the default filename of this PIP.
52 public function getDefaultFilename() {
53 return call_user_func([$this->getDecoratedObject()->className
, 'getDefaultFilename']);
56 public function getEffectiveDefaultFilename() {
57 return './' . preg_replace('~\.tar$~', '/', $this->getDefaultFilename());
61 * Returns true if the PIP exists, has a default filename and is idempotent.
65 public function isSupported() {
66 return $this->classExists() && $this->getDefaultFilename() && $this->isIdempotent();
69 public function getSyncDependencies($toJson = true) {
70 $dependencies = call_user_func([$this->getDecoratedObject()->className
, 'getSyncDependencies']);
72 return ($toJson) ? JSON
::encode($dependencies) : $dependencies;
76 * Returns the first validation error.
80 public function getFirstError() {
81 if (!$this->classExists()) {
82 return WCF
::getLanguage()->getDynamicVariable('wcf.acp.devtools.pip.error.className', ['className' => $this->getDecoratedObject()->className
]);
84 else if (!$this->isIdempotent()) {
85 return WCF
::getLanguage()->get('wcf.acp.devtools.pip.error.notIdempotent');
87 else if (!$this->getDefaultFilename()) {
88 return WCF
::getLanguage()->get('wcf.acp.devtools.pip.error.defaultFilename');
91 throw new \
LogicException("Please call `isSupported()` to check for potential errors.");
95 * Returns the list of valid targets for this pip.
97 * @param DevtoolsProject $project
100 public function getTargets(DevtoolsProject
$project) {
101 if (!$this->isSupported()) {
105 $path = $project->path
;
106 $defaultFilename = $this->getDefaultFilename();
109 // the core uses a significantly different file layout
110 if ($project->isCore()) {
111 switch ($this->getDecoratedObject()->pluginName
) {
115 // these pips are satisfied by definition
116 return [$defaultFilename];
119 foreach (glob($path . 'wcfsetup/install/lang/*.xml') as $file) {
120 $targets[] = basename($file);
123 // `glob()` returns files in an arbitrary order
124 sort($targets, SORT_NATURAL
);
129 if (strpos($defaultFilename, '*') !== false) {
130 foreach (glob($path . 'com.woltlab.wcf/' . $defaultFilename) as $file) {
131 $targets[] = basename($file);
134 // `glob()` returns files in an arbitrary order
135 sort($targets, SORT_NATURAL
);
138 if (file_exists($path . 'com.woltlab.wcf/' . $defaultFilename)) {
139 $targets[] = $defaultFilename;
144 if (preg_match('~^(?<filename>.*)\.tar$~', $defaultFilename, $match)) {
145 if (is_dir($path . $match['filename'])) {
146 $targets[] = $defaultFilename;
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";
157 if (strpos($defaultFilename, '*') !== false) {
158 foreach (glob($path . $defaultFilename) as $file) {
159 $targets[] = basename($file);
162 // `glob()` returns files in an arbitrary order
163 sort($targets, SORT_NATURAL
);
166 if (file_exists($path . $defaultFilename)) {
167 $targets[] = $defaultFilename;
177 * Computes and prepares the instruction value for the provided target file.
179 * @param DevtoolsProject $project
180 * @param string $target
183 public function getInstructionValue(DevtoolsProject
$project, $target) {
184 $defaultFilename = $this->getDefaultFilename();
185 $pluginName = $this->getDecoratedObject()->pluginName
;
186 $tar = $project->getPackageArchive()->getTar();
189 if ($project->isCore()) {
190 switch ($pluginName) {
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));
201 $path = 'wcfsetup/install/files/';
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
211 else if ($filename === 'templates') {
212 // ignores both `templates` and `acp/templates`
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;
226 FileUtil
::getRelativePath($project->path
. $path, $item->getPath()) . $item->getFilename(),
232 return $defaultFilename;
235 $filename = "wcfsetup/install/lang/{$target}";
236 $tar->registerFile($filename, $project->path
. $filename);
241 $filename = "com.woltlab.wcf/{$target}";
242 $tar->registerFile($filename, $project->path
. $filename);
248 switch ($pluginName) {
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));
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
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;
280 FileUtil
::getRelativePath($project->path
. $path, $item->getPath()) . $item->getFilename(),
286 return $defaultFilename;
289 if (strpos($defaultFilename, '*') !== false) {
290 $filename = str_replace('*', $target, $defaultFilename);
291 $tar->registerFile($filename, $project->path
. $filename);
294 $filename = "com.woltlab.wcf/{$target}";
295 $tar->registerFile($filename, $project->path
. $filename);