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