-
Notifications
You must be signed in to change notification settings - Fork 6
/
Installer.php
290 lines (248 loc) · 9.21 KB
/
Installer.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
<?php
/**
* Routines to assist in the installation of various parts of QCubed with composer.
*
*/
namespace QCubed\Composer;
use Composer\Package\PackageInterface;
use Composer\Installer\LibraryInstaller;
use Composer\Repository\InstalledRepositoryInterface;
$__CONFIG_ONLY__ = true;
class Installer extends LibraryInstaller
{
/** Overrides **/
/**
* Return the types of packages that this installer is responsible for installing.
*
* @param $packageType
* @return bool
*/
public function supports($packageType)
{
return ('qcubed-plugin' === $packageType ||
'qcubed-framework' === $packageType);
}
public function getInstallPath(PackageInterface $package) {
$strDest = parent::getInstallPath($package);
$parts = explode('_', $package->getName());
if ('qcubed/plugin' === $parts[0]) {
$strDest = ($this->vendorDir ? $this->vendorDir . '/' : '') . 'qcubed/plugin/' . $parts[1];
}
return $strDest;
}
/**
* Respond to the install command.
*
* @param InstalledRepositoryInterface $repo
* @param PackageInterface $package
*/
public function install(InstalledRepositoryInterface $repo, PackageInterface $package) {
parent::install($repo, $package);
$strPackageName = $package->getName();
if (self::startsWith($strPackageName, 'qcubed/plugin')) {
$this->composerPluginInstall($package);
}
elseif (self::startsWith($strPackageName, 'qcubed/qcubed')) {
// updating the framework
$this->composerFrameworkInstall($package);
}
}
/**
* Move files out of the vendor directory and into the project directory that are in the plugin's install directory.
* @param $strPackageName
*/
protected function composerPluginInstall ($package) {
require_once(($this->vendorDir ? $this->vendorDir . '/' : '') . 'qcubed/qcubed/qcubed.inc.php'); // get the configuration options so we can know where to put the plugin files
// recursively copy the contents of the install subdirectory in the plugin.
$strPluginDir = $this->getPackageBasePath($package);
$strInstallDir = $strPluginDir . '/install';
$strDestDir = __INCLUDES__ . '/plugins';
$this->filesystem->ensureDirectoryExists($strDestDir);
$this->io->write('Copying files from ' . $strInstallDir . ' to ' . $strDestDir);
self::copy_dir($strInstallDir, $strDestDir);
}
protected function NormalizeNonPosixPath($s){
return str_replace('\\', '/', $s);
}
/**
* First time installation of framework. For first-time installation, we create the project directory and modify
* the configuration file.
*
* @param $strPackageName
*/
protected function composerFrameworkInstall ($package) {
$extra = $package->getExtra();
// recursively copy the contents of the install directory, providing each file is not there.
$strInstallDir = self::NormalizeNonPosixPath($this->getPackageBasePath($package)) . '/install/project';
$strDestDir = ($this->vendorDir ? $this->vendorDir . '/' : '') . '../project'; // try to find the default project location
$strDestDir = self::NormalizeNonPosixPath($strDestDir);
$this->io->write('Copying files from ' . $strInstallDir . ' to ' . $strDestDir);
self::copy_dir($strInstallDir, $strDestDir);
// Make sure particular directories are writable by the web server. These are listed in the extra section of the composer.json file.
// We are assuming that the first time installation is installed in a subdirectory of docroot.
$strInstallDir = self::NormalizeNonPosixPath(realpath(dirname($strDestDir)));
$strSubDirectory = '/' . basename($strInstallDir);
$strDocRoot = self::NormalizeNonPosixPath(realpath($strInstallDir . '/../'));
$strConfigDirectory = $strDestDir . '/includes/configuration';
$this->io->write('Updating permissions');
foreach ($extra['writePermission'] as $strDir) {
$strTargetDir = $strInstallDir . '/' . $strDir;
if(!file_exists($strTargetDir)){
mkdir($strTargetDir, 0777, true);
}
chmod ($strTargetDir, 0777);
}
// write configuration.inc.php only if it does not exists
if (!file_exists($strConfigDirectory . '/configuration.inc.php')) {
// fix up the configuration file
$strFile = file_get_contents($strConfigDirectory . '/configuration.inc.sample.php');
if ($strFile) {
$strFile = str_replace (['{docroot}', '{vd}', '{subdir}'], [$strDocRoot, '', $strSubDirectory], $strFile);
file_put_contents($strConfigDirectory . '/configuration.inc.php', $strFile);
}
}
}
/**
* Respond to an update command.
*
* @param InstalledRepositoryInterface $repo
* @param PackageInterface $initial
* @param PackageInterface $target
*/
public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) {
parent::install($repo, $initial, $target);
$strPackageName = $target->getName();
if (self::startsWith($strPackageName, 'qcubed/plugin')) {
$this->composerPluginInstall($target);
}
elseif (self::startsWith($strPackageName, 'qcubed/qcubed')) {
// updating the framework
$this->composerFrameworkUpdate($target);
}
}
/**
* Return true if the needle starts with the haystack.
*
* @param string $haystack
* @param string $needle
* @return bool
*/
protected static function startsWith($haystack, $needle) {
// search backwards starting from haystack length characters from the end
return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== FALSE;
}
/**
* Update a package. In particular, new install files will be moved to the correct location.
*
* @param $package
*/
protected function composerFrameworkUpdate ($package) {
require_once(($this->vendorDir ? $this->vendorDir . '/' : '') . 'qcubed/qcubed/qcubed.inc.php'); // get the configuration options so we can know where to put the plugin files
// recursively copy the contents of the install directory, providing each file is not there.
$strInstallDir = $this->getPackageBasePath($package) . '/install/project';
$strDestDir = __PROJECT__;
// copy_dir will not overwrite files, but will add any new stub files
$this->io->write('Copying files from ' . $strInstallDir . ' to ' . $strDestDir);
self::copy_dir($strInstallDir, $strDestDir);
}
/**
* Uninstalls a plugin if requested.
*
* @param InstalledRepositoryInterface $repo
* @param PackageInterface $package
*/
public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
{
$strPackageName = $package->getName();
if (self::startsWith($strPackageName, 'qcubed/plugin')) {
$this->composerPluginUninstall($package);
}
parent::uninstall ($repo, $package);
}
/**
* Delete the given plugin.
*
* @param PackageInterface $package
*/
public function composerPluginUninstall (PackageInterface $package) {
require_once(($this->vendorDir ? $this->vendorDir . '/' : '') . 'qcubed/qcubed/qcubed.inc.php'); // get the configuration options so we can know where the plugin files are
// recursively delete the contents of the install directory, providing each file is there.
$strPluginDir = $this->getPackageBasePath($package) . '/install';
$strDestDir = __INCLUDES__ . '/plugins';
$this->io->write('Removing files from ' . $strPluginDir);
self::remove_matching_dir($strPluginDir, $strDestDir);
}
/**
* Copy the contents of the source directory into the destination directory, creating the destination directory
* if it does not exist. If the destination file exists, it will NOT overwrite the file.
*
* @param string $src source directory
* @param string $dst destination directory
*/
protected static function copy_dir($src,$dst) {
if (!$src || !is_dir($src)) {
return;
}
$dir = opendir($src);
if (!file_exists($dst)) {
mkdir($dst);
}
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' )) {
if ( is_dir($src . '/' . $file) ) {
self::copy_dir($src . '/' . $file,$dst . '/' . $file);
}
else {
if (!file_exists($dst . '/' . $file)) {
copy($src . '/' . $file,$dst . '/' . $file);
}
}
}
}
closedir($dir);
}
/**
* Remove the files in the destination directory whose names match the files in the source directory.
*
* @param string $src Source directory
* @param string $dst Destination directory
*/
protected static function remove_matching_dir($src,$dst) {
if (!$dst || !$src || !is_dir($src) || !is_dir($dst)) return; // prevent deleting an entire disk by accidentally calling this with an empty string!
$dir = opendir($src);
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' )) {
if ( is_dir($src . '/' . $file) ) {
self::remove_dir($dst . '/' . $file);
}
else {
if (file_exists($dst . '/' . $file)) {
unlink($dst . '/' . $file);
}
}
}
}
closedir($dir);
}
/**
* Delete a directory and all of its contents.
*
* @param string $dst Directory to delete
*/
protected static function remove_dir($dst) {
if (!$dst || !is_dir($dst)) return; // prevent deleting an entire disk by accidentally calling this with an empty string!
$dir = opendir($dst);
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' )) {
if ( is_dir($dst . '/' . $file) ) {
self::remove_dir($dst . '/' . $file);
}
else {
unlink($dst . '/' . $file);
}
}
}
closedir($dir);
rmdir($dst);
}
}