1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
15: namespace Cake\Shell\Task;
16:
17: use Cake\Console\Shell;
18: use Cake\Core\Plugin;
19: use Cake\Filesystem\Folder;
20: use Cake\Utility\Inflector;
21:
22: 23: 24:
25: class AssetsTask extends Shell
26: {
27: 28: 29: 30: 31: 32: 33: 34: 35:
36: public function symlink($name = null)
37: {
38: $this->_process($this->_list($name));
39: }
40:
41: 42: 43: 44: 45: 46: 47: 48:
49: public function copy($name = null)
50: {
51: $this->_process($this->_list($name), true, $this->param('overwrite'));
52: }
53:
54: 55: 56: 57: 58: 59: 60: 61:
62: public function remove($name = null)
63: {
64: $plugins = $this->_list($name);
65:
66: foreach ($plugins as $plugin => $config) {
67: $this->out();
68: $this->out('For plugin: ' . $plugin);
69: $this->hr();
70:
71: $this->_remove($config);
72: }
73:
74: $this->out();
75: $this->out('Done');
76: }
77:
78: 79: 80: 81: 82: 83: 84:
85: protected function _list($name = null)
86: {
87: if ($name === null) {
88: $pluginsList = Plugin::loaded();
89: } else {
90: if (!Plugin::isLoaded($name)) {
91: $this->err(sprintf('Plugin %s is not loaded.', $name));
92:
93: return [];
94: }
95: $pluginsList = [$name];
96: }
97:
98: $plugins = [];
99:
100: foreach ($pluginsList as $plugin) {
101: $path = Plugin::path($plugin) . 'webroot';
102: if (!is_dir($path)) {
103: $this->verbose('', 1);
104: $this->verbose(
105: sprintf('Skipping plugin %s. It does not have webroot folder.', $plugin),
106: 2
107: );
108: continue;
109: }
110:
111: $link = Inflector::underscore($plugin);
112: $dir = WWW_ROOT;
113: $namespaced = false;
114: if (strpos($link, '/') !== false) {
115: $namespaced = true;
116: $parts = explode('/', $link);
117: $link = array_pop($parts);
118: $dir = WWW_ROOT . implode(DIRECTORY_SEPARATOR, $parts) . DIRECTORY_SEPARATOR;
119: }
120:
121: $plugins[$plugin] = [
122: 'srcPath' => Plugin::path($plugin) . 'webroot',
123: 'destDir' => $dir,
124: 'link' => $link,
125: 'namespaced' => $namespaced
126: ];
127: }
128:
129: return $plugins;
130: }
131:
132: 133: 134: 135: 136: 137: 138: 139:
140: protected function _process($plugins, $copy = false, $overwrite = false)
141: {
142: $overwrite = (bool)$this->param('overwrite');
143:
144: foreach ($plugins as $plugin => $config) {
145: $this->out();
146: $this->out('For plugin: ' . $plugin);
147: $this->hr();
148:
149: if ($config['namespaced'] &&
150: !is_dir($config['destDir']) &&
151: !$this->_createDirectory($config['destDir'])
152: ) {
153: continue;
154: }
155:
156: $dest = $config['destDir'] . $config['link'];
157:
158: if (file_exists($dest)) {
159: if ($overwrite && !$this->_remove($config)) {
160: continue;
161: } elseif (!$overwrite) {
162: $this->verbose(
163: $dest . ' already exists',
164: 1
165: );
166:
167: continue;
168: }
169: }
170:
171: if (!$copy) {
172: $result = $this->_createSymlink(
173: $config['srcPath'],
174: $dest
175: );
176: if ($result) {
177: continue;
178: }
179: }
180:
181: $this->_copyDirectory(
182: $config['srcPath'],
183: $dest
184: );
185: }
186:
187: $this->out();
188: $this->out('Done');
189: }
190:
191: 192: 193: 194: 195: 196:
197: protected function _remove($config)
198: {
199: if ($config['namespaced'] && !is_dir($config['destDir'])) {
200: $this->verbose(
201: $config['destDir'] . $config['link'] . ' does not exist',
202: 1
203: );
204:
205: return false;
206: }
207:
208: $dest = $config['destDir'] . $config['link'];
209:
210: if (!file_exists($dest)) {
211: $this->verbose(
212: $dest . ' does not exist',
213: 1
214: );
215:
216: return false;
217: }
218:
219: if (is_link($dest)) {
220:
221: if (@unlink($dest)) {
222: $this->out('Unlinked ' . $dest);
223:
224: return true;
225: } else {
226: $this->err('Failed to unlink ' . $dest);
227:
228: return false;
229: }
230: }
231:
232: $folder = new Folder($dest);
233: if ($folder->delete()) {
234: $this->out('Deleted ' . $dest);
235:
236: return true;
237: } else {
238: $this->err('Failed to delete ' . $dest);
239:
240: return false;
241: }
242: }
243:
244: 245: 246: 247: 248: 249:
250: protected function _createDirectory($dir)
251: {
252: $old = umask(0);
253:
254: $result = @mkdir($dir, 0755, true);
255:
256: umask($old);
257:
258: if ($result) {
259: $this->out('Created directory ' . $dir);
260:
261: return true;
262: }
263:
264: $this->err('Failed creating directory ' . $dir);
265:
266: return false;
267: }
268:
269: 270: 271: 272: 273: 274: 275:
276: protected function _createSymlink($target, $link)
277: {
278:
279: $result = @symlink($target, $link);
280:
281:
282: if ($result) {
283: $this->out('Created symlink ' . $link);
284:
285: return true;
286: }
287:
288: return false;
289: }
290:
291: 292: 293: 294: 295: 296: 297:
298: protected function _copyDirectory($source, $destination)
299: {
300: $folder = new Folder($source);
301: if ($folder->copy(['to' => $destination])) {
302: $this->out('Copied assets to directory ' . $destination);
303:
304: return true;
305: }
306:
307: $this->err('Error copying assets to directory ' . $destination);
308:
309: return false;
310: }
311:
312: 313: 314: 315: 316:
317: public function getOptionParser()
318: {
319: $parser = parent::getOptionParser();
320:
321: $parser->addSubcommand('symlink', [
322: 'help' => 'Symlink (copy as fallback) plugin assets to app\'s webroot.'
323: ])->addSubcommand('copy', [
324: 'help' => 'Copy plugin assets to app\'s webroot.'
325: ])->addSubcommand('remove', [
326: 'help' => 'Remove plugin assets from app\'s webroot.'
327: ])->addArgument('name', [
328: 'help' => 'A specific plugin you want to symlink assets for.',
329: 'optional' => true,
330: ])->addOption('overwrite', [
331: 'help' => 'Overwrite existing symlink / folder / files.',
332: 'default' => false,
333: 'boolean' => true
334: ]);
335:
336: return $parser;
337: }
338: }
339: