diff --git a/src/Shell/Task/AssetsTask.php b/src/Shell/PluginAssetsShell.php similarity index 58% rename from src/Shell/Task/AssetsTask.php rename to src/Shell/PluginAssetsShell.php index f6a1dc78405..bf63fc90e13 100644 --- a/src/Shell/Task/AssetsTask.php +++ b/src/Shell/PluginAssetsShell.php @@ -12,40 +12,41 @@ * @since 3.0.0 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -namespace Cake\Shell\Task; +namespace Cake\Shell; use Cake\Console\Shell; -use Cake\Core\App; use Cake\Core\Plugin; use Cake\Filesystem\Folder; use Cake\Utility\Inflector; /** - * Task for symlinking / copying plugin assets to app's webroot. + * Shell for symlinking / copying plugin assets to app's webroot. * */ -class AssetsTask extends Shell { +class PluginAssetsShell extends Shell { /** - * Execution method always used for tasks + * Attempt to symlink plugin assets to app's webroot. If symlinking fails it + * fallback to copying the assets. For vendor namespaced plugin, parent folder + * for vendor name are created if required. * * @return void */ - public function main() { - $this->_process(); + public function symlink() { + $this->_process($this->_list()); } /** - * Process plugins + * Get list of plugins to process. Plugins without a webroot directory are skipped. * - * @return void + * @return array */ - protected function _process() { - $plugins = Plugin::loaded(); - foreach ($plugins as $plugin) { + protected function _list() { + $plugins = []; + foreach (Plugin::loaded() as $plugin) { $path = Plugin::path($plugin) . 'webroot'; if (!is_dir($path)) { - $this->out(); + $this->out('', 1, Shell::VERBOSE); $this->out( sprintf('Skipping plugin %s. It does not have webroot folder.', $plugin), 2, @@ -54,32 +55,67 @@ protected function _process() { continue; } - $this->out(); - $this->out('For plugin: ' . $plugin); - $this->hr(); - $link = Inflector::underscore($plugin); $dir = WWW_ROOT; - + $namespaced = false; if (strpos('/', $link) !== false) { + $namespaced = true; $parts = explode('/', $link); $link = array_pop($parts); $dir = WWW_ROOT . implode(DS, $parts) . DS; - if (!is_dir($dir) && !$this->_createDirectory($dir)) { - continue; - } } - if (file_exists($dir . $link)) { - $this->out($link . ' already exists', 1, Shell::VERBOSE); + $plugins[$plugin] = [ + 'srcPath' => Plugin::path($plugin) . 'webroot', + 'destDir' => $dir, + 'link' => $link, + 'namespaced' => $namespaced + ]; + } + return $plugins; + } + +/** + * Process plugins + * + * @return void + */ + protected function _process($plugins) { + foreach ($plugins as $plugin => $config) { + $path = Plugin::path($plugin) . 'webroot'; + + $this->out(); + $this->out('For plugin: ' . $plugin); + $this->hr(); + + if ($config['namespaced'] && + !is_dir($config['destDir']) && + !$this->_createDirectory($config['destDir']) + ) { + continue; + } + + if (file_exists($config['destDir'] . $config['link'])) { + $this->out( + $config['destDir'] . $config['link'] . ' already exists', + 1, + Shell::VERBOSE + ); continue; } - if ($this->_createSymlink($path, $dir . $link)) { + $result = $this->_createSymlink( + $config['srcPath'], + $config['destDir'] . $config['link'] + ); + if ($result) { continue; } - $this->_copyDirectory($path, $dir . $link); + $this->_copyDirectory( + $config['srcPath'], + $config['destDir'] . $config['link'] + ); } $this->out(); @@ -146,4 +182,19 @@ protected function _copyDirectory($source, $destination) { return false; } +/** + * Gets the option parser instance and configures it. + * + * @return \Cake\Console\ConsoleOptionParser + */ + public function getOptionParser() { + $parser = parent::getOptionParser(); + + $parser->addSubcommand('symlink', [ + 'help' => 'Symlink / copy assets to app\'s webroot' + ]); + + return $parser; + } + } diff --git a/src/Shell/PluginShell.php b/src/Shell/PluginShell.php deleted file mode 100644 index e3e58305ea3..00000000000 --- a/src/Shell/PluginShell.php +++ /dev/null @@ -1,56 +0,0 @@ -Assets->main(); - } - -/** - * Gets the option parser instance and configures it. - * - * @return \Cake\Console\ConsoleOptionParser - */ - public function getOptionParser() { - $parser = parent::getOptionParser(); - - $parser->addSubcommand('assets', [ - 'help' => 'Symlink / copy assets to app\'s webroot' - ]); - - return $parser; - } - -} diff --git a/tests/TestCase/Shell/Task/AssetsTaskTest.php b/tests/TestCase/Shell/PluginAssetsShellTest.php similarity index 63% rename from tests/TestCase/Shell/Task/AssetsTaskTest.php rename to tests/TestCase/Shell/PluginAssetsShellTest.php index e95ccb26ded..8ddf505227c 100644 --- a/tests/TestCase/Shell/Task/AssetsTaskTest.php +++ b/tests/TestCase/Shell/PluginAssetsShellTest.php @@ -12,20 +12,20 @@ * @since 3.0.0 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -namespace Cake\Test\TestCase\Shell\Task; +namespace Cake\Test\TestCase\Shell; use Cake\Core\App; use Cake\Core\Configure; use Cake\Core\Plugin; use Cake\Filesystem\Folder; -use Cake\Shell\Task\AssetsTask; +use Cake\Shell\PluginAssetsTask; use Cake\TestSuite\TestCase; /** - * SymlinkAssetsTask class + * PluginAssetsShellTest class * */ -class SymlinkAssetsTaskTest extends TestCase { +class PluginAssetsShellTest extends TestCase { /** * setUp method @@ -36,8 +36,8 @@ public function setUp() { parent::setUp(); $this->io = $this->getMock('Cake\Console\ConsoleIo', [], [], '', false); - $this->Task = $this->getMock( - 'Cake\Shell\Task\AssetsTask', + $this->shell = $this->getMock( + 'Cake\Shell\PluginAssetsShell', array('in', 'out', 'err', '_stop'), array($this->io) ); @@ -50,20 +50,20 @@ public function setUp() { */ public function tearDown() { parent::tearDown(); - unset($this->Task); + unset($this->shell); Plugin::unload(); } /** - * testExecute method + * testSymlink method * * @return void */ - public function testExecute() { + public function testSymlink() { Plugin::load('TestPlugin'); Plugin::load('Company/TestPluginThree'); - $this->Task->main(); + $this->shell->symlink(); $path = WWW_ROOT . 'test_plugin'; $link = new \SplFileInfo($path); @@ -71,6 +71,27 @@ public function testExecute() { $this->assertTrue(file_exists($path . DS . 'root.js')); unlink($path); + $path = WWW_ROOT . 'company' . DS . 'test_plugin_three'; + $link = new \SplFileInfo($path); + // If "company" directory exists beforehand "test_plugin_three" would + // be a link. But if the directory is created by the shell itself + // symlinking fails and the assets folder is copied as fallback. + $this->assertTrue($link->isDir()); + $this->assertTrue(file_exists($path . DS . 'css' . DS . 'company.css')); + $folder = new Folder(WWW_ROOT . 'company'); + $folder->delete(); + } + +/** + * testSymlinkWhenVendorDirectoryExits + * + * @return void + */ + public function testSymlinkWhenVendorDirectoryExits() { + Plugin::load('Company/TestPluginThree'); + mkdir(WWW_ROOT . 'company'); + + $this->shell->symlink(); $path = WWW_ROOT . 'company' . DS . 'test_plugin_three'; $link = new \SplFileInfo($path); $this->assertTrue($link->isLink()); @@ -87,7 +108,7 @@ public function testExecute() { public function testForPluginWithoutWebroot() { Plugin::load('TestPluginTwo'); - $this->Task->main(); + $this->shell->symlink(); $this->assertFalse(file_exists(WWW_ROOT . 'test_plugin_two')); }