diff --git a/.gitignore b/.gitignore index 6d538ea9..974885d3 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,3 @@ node_modules/* yarn-error.log _ide_helper.php .phpstorm.meta.php -public/ diff --git a/app/Http/Controllers/MarketController.php b/app/Http/Controllers/MarketController.php index 4c96c626..6f395d9b 100644 --- a/app/Http/Controllers/MarketController.php +++ b/app/Http/Controllers/MarketController.php @@ -121,6 +121,7 @@ class MarketController extends Controller if ($zip->extractTo($plugins_dir) === false) { return json(trans('admin.plugins.market.unzip-failed', ['error' => 'Unable to extract the file.']), 4); } + $manager->copyPluginAssets(plugin($name)); } else { return json(trans('admin.plugins.market.unzip-failed', ['error' => $res]), 4); } diff --git a/app/Http/Controllers/SetupController.php b/app/Http/Controllers/SetupController.php index ac8c11ad..a111036b 100644 --- a/app/Http/Controllers/SetupController.php +++ b/app/Http/Controllers/SetupController.php @@ -41,7 +41,7 @@ class SetupController extends Controller ); } - $content = File::get('.env'); + $content = File::get('..' . DIRECTORY_SEPARATOR . '.env'); $content = str_replace( 'DB_CONNECTION = '.env('DB_CONNECTION'), 'DB_CONNECTION = '.$request->input('type'), @@ -77,7 +77,7 @@ class SetupController extends Controller 'DB_PREFIX = '.$request->input('prefix'), $content ); - File::put('.env', $content); + File::put('..' . DIRECTORY_SEPARATOR . '.env', $content); return redirect('setup/info'); } diff --git a/app/Services/Plugin.php b/app/Services/Plugin.php index 3c7a9a10..1f1d2fed 100644 --- a/app/Services/Plugin.php +++ b/app/Services/Plugin.php @@ -104,7 +104,7 @@ class Plugin implements Arrayable, ArrayAccess public function assets($relativeUri) { - $baseUrl = config('plugins.url') ?: url('plugins'); + $baseUrl = config('plugins.url') ?: url('public/plugins'); return "$baseUrl/{$this->getDirname()}/$relativeUri"; } diff --git a/app/Services/PluginManager.php b/app/Services/PluginManager.php index c15a04bc..f7f97b11 100644 --- a/app/Services/PluginManager.php +++ b/app/Services/PluginManager.php @@ -2,9 +2,11 @@ namespace App\Services; +use Storage; use App\Events; use Composer\Semver\Semver; use Illuminate\Support\Arr; +use Composer\Semver\Comparator; use Illuminate\Support\Collection; use Illuminate\Filesystem\Filesystem; use App\Exceptions\PrettyPageException; @@ -39,6 +41,11 @@ class PluginManager */ protected $plugins; + /** + * @var Collection + */ + protected $enabled; + public function __construct( Application $app, OptionRepository $option, @@ -58,6 +65,7 @@ class PluginManager { if (is_null($this->plugins)) { $plugins = new Collection(); + $enabled = $this->getFullEnabled(); $installed = []; @@ -68,7 +76,7 @@ class PluginManager } // traverse plugins dir - while($filename = @readdir($resource)) { + while ($filename = @readdir($resource)) { if ($filename == '.' || $filename == '..') continue; @@ -106,6 +114,13 @@ class PluginManager } $plugins->put($plugin->name, $plugin); + + if ( + $enabled->has($plugin->name) && + Comparator::notEqualTo($plugin->getVersion(), $enabled->get($plugin->name)) + ) { + $this->copyPluginAssets($plugin); + } } $this->plugins = $plugins->sortBy(function ($plugin, $name) { @@ -134,14 +149,18 @@ class PluginManager */ public function enable($name) { + if (is_null($this->enabled)) { + $this->convertPluginRecord(); + } + if (! $this->isEnabled($name)) { $plugin = $this->getPlugin($name); - $enabled = $this->getEnabled(); - - $enabled[] = $name; - - $this->setEnabled($enabled); + $this->enabled->push([ + 'name' => $name, + 'version' => $plugin->getVersion(), + ]); + $this->saveEnabled(); $plugin->setEnabled(true); @@ -156,17 +175,21 @@ class PluginManager */ public function disable($name) { - $enabled = $this->getEnabled(); + if (is_null($this->enabled)) { + $this->convertPluginRecord(); + } - if (($k = array_search($name, $enabled)) !== false) { - unset($enabled[$k]); + $rejected = $this->enabled->reject(function ($item) use ($name) { + return $item['name'] == $name; + }); + if ($rejected->count() !== $this->enabled->count()) { $plugin = $this->getPlugin($name); - - $this->setEnabled($enabled); - $plugin->setEnabled(false); + $this->enabled = $rejected; + $this->saveEnabled(); + $this->dispatcher->fire(new Events\PluginWasDisabled($plugin)); } } @@ -198,6 +221,10 @@ class PluginManager */ public function getEnabledPlugins() { + if (is_null($this->enabled)) { + $this->convertPluginRecord(); + } + return $this->getPlugins()->only($this->getEnabled()); } @@ -244,19 +271,42 @@ class PluginManager */ public function getEnabled() { - return (array) json_decode($this->option->get('plugins_enabled'), true); + $enabled = collect(json_decode($this->option->get('plugins_enabled'), true)); + + return $enabled->map(function ($item) { + if (is_string($item)) { + return $item; + } else { + return $item['name']; + } + })->values()->toArray(); + } + + /** + * Return enabled plugins with version information. + * + * @return Collection + */ + public function getFullEnabled() + { + $enabled = collect(json_decode($this->option->get('plugins_enabled'), true)); + $ret = collect(); + + $enabled->each(function ($item) use ($ret) { + if (is_array($item)) { + $ret->put($item['name'], $item['version']); + } + }); + + return $ret; } /** * Persist the currently enabled plugins. - * - * @param array $enabled */ - protected function setEnabled(array $enabled) + protected function saveEnabled() { - $enabled = array_values(array_unique($enabled)); - - $this->option->set('plugins_enabled', json_encode($enabled)); + $this->option->set('plugins_enabled', $this->enabled->values()->toJson()); // ensure to save options $this->option->save(); @@ -355,4 +405,49 @@ class PluginManager return config('plugins.directory') ?: base_path('plugins'); } + /** + * Copy plugin assets + * + * @param Plugin $plugin + * + * @return bool + */ + public function copyPluginAssets($plugin) + { + $dir = public_path('plugins/' . $plugin->name . '/assets'); + Storage::deleteDirectory($dir); + + return $this->filesystem->copyDirectory( + $this->getPluginsDir() . DIRECTORY_SEPARATOR . $plugin->name . DIRECTORY_SEPARATOR . 'assets', + $dir + ); + } + + /** + * Convert `plugins_enabled` field for backward compatibility. + * + * @return $this + */ + protected function convertPluginRecord() + { + $list = collect(json_decode($this->option->get('plugins_enabled'), true)); + $this->enabled = $list->map(function ($item) { + if (is_string($item)) { + $plugin = $this->getPlugin($item); + return [ + 'name' => $item, + 'version' => $plugin->getVersion(), + ]; + } else { + $plugin = $this->getPlugin($item['name']); + $item['version'] = $plugin->getVersion(); + return $item; + } + }); + + $this->saveEnabled(); + + return $this; + } + } diff --git a/app/helpers.php b/app/helpers.php index a7d9e0ac..7293465c 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -57,7 +57,7 @@ if (! function_exists('webpack_assets')) { if (app()->environment('development')) { return "http://127.0.0.1:8080/public/$relativeUri"; } else { - return url("public/$relativeUri"); + return url("app/$relativeUri"); } } } diff --git a/bootstrap/chkenv.php b/bootstrap/chkenv.php index 9d3e9842..1e894040 100644 --- a/bootstrap/chkenv.php +++ b/bootstrap/chkenv.php @@ -60,8 +60,8 @@ } } - $autoload = file_get_contents('vendor/autoload.php'); + $autoload = file_get_contents(__DIR__ . '/../vendor/autoload.php'); $lines = explode("\n", $autoload); $lines[1] = '$GLOBALS["env_checked"] = true;'; - file_put_contents('vendor/autoload.php', implode("\n", $lines)); + file_put_contents(__DIR__ . '/../vendor/autoload.php', implode("\n", $lines)); })(); diff --git a/config/options.php b/config/options.php index 544deaad..83031f1e 100644 --- a/config/options.php +++ b/config/options.php @@ -12,7 +12,7 @@ return [ 'api_type' => 'false', 'announcement' => 'Welcome to Blessing Skin {version}!', 'color_scheme' => 'skin-blue', - 'home_pic_url' => './public/bg.jpg', + 'home_pic_url' => './app/bg.jpg', 'custom_css' => '', 'custom_js' => '', 'player_name_rule' => 'official', @@ -42,5 +42,5 @@ return [ 'plugins_enabled' => '', 'copyright_prefer' => '0', 'score_per_closet_item' => '0', - 'favicon_url' => 'public/favicon.ico' + 'favicon_url' => 'app/favicon.ico' ]; diff --git a/package.json b/package.json index 2c0a9131..ffed284b 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "private": true, "scripts": { "dev": "webpack-serve", - "build": "rimraf public && node scripts/build", + "build": "rimraf public/app && node scripts/build", "lint": "eslint --ext=.js,.vue -f=beauty .", "test": "jest", "codecov": "codecov -F js", diff --git a/public/.gitignore b/public/.gitignore new file mode 100644 index 00000000..908f7bbc --- /dev/null +++ b/public/.gitignore @@ -0,0 +1 @@ +app/ diff --git a/.htaccess b/public/.htaccess similarity index 100% rename from .htaccess rename to public/.htaccess diff --git a/index.php b/public/index.php similarity index 52% rename from index.php rename to public/index.php index 812d3d5d..3ce6981e 100755 --- a/index.php +++ b/public/index.php @@ -9,9 +9,9 @@ @ini_set('display_errors', 'on'); -require __DIR__.'/bootstrap/autoload.php'; +require __DIR__.'/../bootstrap/autoload.php'; -if (!isset($GLOBALS['env_checked'])) require __DIR__.'/bootstrap/chkenv.php'; +if (!isset($GLOBALS['env_checked'])) require __DIR__.'/../bootstrap/chkenv.php'; // Process the request -require __DIR__.'/bootstrap/kernel.php'; +require __DIR__.'/../bootstrap/kernel.php'; diff --git a/public/plugins/.gitignore b/public/plugins/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/public/plugins/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/resources/assets/src/js/public-path.js b/resources/assets/src/js/public-path.js index a8fe193c..67899254 100644 --- a/resources/assets/src/js/public-path.js +++ b/resources/assets/src/js/public-path.js @@ -1,4 +1,4 @@ // eslint-disable-next-line no-undef __webpack_public_path__ = process.env.NODE_ENV === 'development' - ? 'http://127.0.0.1:8080/public/' - : blessing.base_url + '/public/'; + ? 'http://127.0.0.1:8080/' + : blessing.base_url + '/app/'; diff --git a/resources/views/common/dependencies/script.blade.php b/resources/views/common/dependencies/script.blade.php index b763ebf9..d6b4c999 100644 --- a/resources/views/common/dependencies/script.blade.php +++ b/resources/views/common/dependencies/script.blade.php @@ -1,10 +1,10 @@ -@if (file_exists(public_path($path = 'langs/'.config('app.locale').'.js'))) - +@if (file_exists(public_path($path = 'app/langs/'.config('app.locale').'.js'))) + @if (file_exists(resource_path($path = 'lang/overrides/'.config('app.locale').'/locale.js'))) @endif @else - + @if (file_exists(resource_path($path = 'lang/overrides/'.config('app.fallback_locale').'/locale.js'))) @endif diff --git a/resources/views/setup/master.blade.php b/resources/views/setup/master.blade.php index 801d5997..de36739b 100644 --- a/resources/views/setup/master.blade.php +++ b/resources/views/setup/master.blade.php @@ -5,7 +5,7 @@ @lang('setup.wizard.master.title') - + @yield('style') diff --git a/tests/SetupControllerTest.php b/tests/SetupControllerTest.php index 555abbc0..82d2ccb9 100644 --- a/tests/SetupControllerTest.php +++ b/tests/SetupControllerTest.php @@ -59,8 +59,8 @@ class SetupControllerTest extends TestCase 'password' => env('DB_PASSWORD'), 'prefix' => '', ]; - File::shouldReceive('get')->with('.env')->andReturn(''); - File::shouldReceive('put')->with('.env', ''); + File::shouldReceive('get')->with('..' . DIRECTORY_SEPARATOR . '.env')->andReturn(''); + File::shouldReceive('put')->with('..' . DIRECTORY_SEPARATOR . '.env', ''); $this->post('/setup/database', $fake)->assertRedirect('/setup/info'); } diff --git a/webpack.config.js b/webpack.config.js index 8c75ef52..0c095114 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -29,7 +29,7 @@ const config = { 'langs/zh_CN': './resources/lang/zh_CN/front-end.js', }, output: { - path: __dirname + '/public', + path: __dirname + '/public/app', filename: '[name].js', chunkFilename: devMode ? '[id].js' diff --git a/yarn.lock b/yarn.lock index 980d9a97..91cfdce9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2523,11 +2523,16 @@ commander@^2.18.0: resolved "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== -commander@^2.9.0, commander@~2.16.0: +commander@^2.9.0: version "2.16.0" resolved "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz#f16390593996ceb4f3eeb020b31d78528f7f8a50" integrity sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew== +commander@~2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" + integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== + commander@~2.17.1: version "2.17.1" resolved "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" @@ -9453,13 +9458,12 @@ typedarray@^0.0.6, typedarray@~0.0.5: integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= uglify-es@^3.3.4: - version "3.8.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-3.8.1.tgz#cb70070ac9e0a71add169dfb63c0a64fca2738ac" - integrity sha512-rPzPisCzW68Okj1zNrfa2dR9uEm43SevDmpR6FChoZABFk9dANGnzzBMgHYUXI3609//63fnVkyQ1SQmAMyjww== + version "3.3.9" + resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" + integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ== dependencies: - commander "~2.16.0" + commander "~2.13.0" source-map "~0.6.1" - source-map-support "~0.5.6" uglify-js@^3.1.4: version "3.4.9"