Commit 532cd679 authored by Nicolas Widart's avatar Nicolas Widart

Merge commit '40857fd7' as 'Modules/Workshop'

parents 1cc483fe 40857fd7
.idea/
.php_cs.cache
vendor/
composer.lock
/Modules
<?php
$finder = Symfony\CS\Finder\DefaultFinder::create()
->exclude('Modules')
->exclude('vendor')
->in(__DIR__)
;
return Symfony\CS\Config\Config::create()
->setUsingCache(true)
->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
->fixers(array(
// Concatenation should be used with at least one whitespace around.
'concat_with_spaces',
// Unused use statements must be removed.
'ordered_use',
// Removes extra empty lines.
'extra_empty_lines',
// Removes line breaks between use statements.
'remove_lines_between_uses',
// An empty line feed should precede a return statement.
'return',
// Unused use statements must be removed.
'unused_use',
// Remove trailing whitespace at the end of blank lines.
'whitespacy_lines',
// There MUST be one blank line after the namespace declaration.
'line_after_namespace',
// There should be exactly one blank line before a namespace declaration.
'single_blank_line_before_namespace',
// Each namespace use MUST go on its own line and there MUST be one blank line after the use statements block.
'single_line_after_imports',
// Ensure there is no code on the same line as the PHP open tag and it is followed by a blankline.
'blankline_after_open_tag',
// Remove duplicated semicolons.
'duplicate_semicolon',
// PHP multi-line arrays should have a trailing comma.
'multiline_array_trailing_comma',
// There should be no empty lines after class opening brace.
'no_blank_lines_after_class_opening',
// There should not be blank lines between docblock and the documented element.
'no_empty_lines_after_phpdocs',
// Phpdocs should start and end with content, excluding the very first and last line of the docblocks.
'phpdoc_trim',
// Removes line breaks between use statements.
'remove_lines_between_uses',
))
->finder($finder);
rules:
php.interface_has_no_interface_suffix:
enabled: false
language: php
php:
- 7
- 5.6
- hhvm
env:
- LARAVEL_VERSION="~5.2" TESTBENCH_VERSION="~3.2"
before_script:
- travis_retry composer install --no-interaction --prefer-source
script: phpunit
sudo: false
notifications:
slack: asgardcms:M9PGEANvlt3pD4gcnLX6Kyjk
email:
- n.widart@gmail.com
- josh@joshbrown.me
matrix:
allow_failures:
- php: hhvm
<?php
namespace Modules\Workshop\Composers;
use Illuminate\Contracts\View\View;
use Modules\Workshop\Manager\ModuleManager;
class MigrateViewComposer
{
/**
* @var ModuleManager
*/
private $module;
public function __construct(ModuleManager $module)
{
$this->module = $module;
}
public function compose(View $view)
{
$view->modules = $this->module->enabled();
}
}
<?php
return [
/*
|--------------------------------------------------------------------------
| Custom Sidebar Class
|--------------------------------------------------------------------------
| If you want to customise the admin sidebar ordering or grouping
| You can define your own sidebar class for this module.
| No custom sidebar: null
*/
'custom-sidebar' => null,
];
<?php
return [
'workshop.modules' => [
'index' => 'workshop::modules.list resource',
'show' => 'workshop::modules.show resource',
'update' => 'workshop::modules.update resource',
'disable' => 'workshop::modules.disable resource',
'enable' => 'workshop::modules.enable resource',
'publish' => 'workshop::modules.publish assets'
],
'workshop.themes' => [
'index' => 'workshop::themes.list resource',
'show' => 'workshop::themes.show resource',
'publish' => 'workshop::themes.publish assets'
],
];
<?php
namespace Modules\Workshop\Console;
use Illuminate\Console\Command;
use Modules\Workshop\Scaffold\Module\Generators\EntityGenerator;
use Symfony\Component\Console\Input\InputArgument;
final class EntityScaffoldCommand extends Command
{
protected $name = 'asgard:entity:scaffold';
protected $description = 'Scaffold a new entity with all its resources.';
/**
* @var EntityGenerator
*/
private $entityGenerator;
public function __construct(EntityGenerator $entityGenerator)
{
parent::__construct();
$this->entityGenerator = $entityGenerator;
}
public function fire()
{
$this->entityGenerator
->forModule($this->argument('module'))
->type('Eloquent')
->generate([$this->argument('entity')], false);
$this->info('Entity files generated.');
}
protected function getArguments()
{
return array(
array('entity', InputArgument::REQUIRED, 'The name of the entity.'),
array('module', InputArgument::REQUIRED, 'The name of module will be used.'),
);
}
}
<?php
namespace Modules\Workshop\Console;
use Illuminate\Console\Command;
use Modules\Workshop\Scaffold\Module\ModuleScaffold;
class ModuleScaffoldCommand extends Command
{
protected $name = 'asgard:module:scaffold';
protected $description = 'Scaffold a new module';
/**
* @var array
*/
protected $entities = [];
/**
* @var array
*/
protected $valueObjects = [];
/**
* @var string The type of entities to generate [Eloquent or Doctrine]
*/
protected $entityType;
/**
* @var ModuleScaffold
*/
private $moduleScaffold;
public function __construct(ModuleScaffold $moduleScaffold)
{
parent::__construct();
$this->moduleScaffold = $moduleScaffold;
}
/**
*
*/
public function fire()
{
$moduleName = $this->ask('Please enter the module name in the following format: vendor/name');
list($vendor, $name) = $this->separateVendorAndName($moduleName);
$this->checkForModuleUniqueness($name);
$this->askForEntities();
$this->askForValueObjects();
$this->moduleScaffold
->vendor($vendor)
->name($name)
->setEntityType($this->entityType)
->withEntities($this->entities)
->withValueObjects($this->valueObjects)
->scaffold();
$this->info('Module generated and is ready to be used.');
}
/**
*
*/
private function askForEntities()
{
$this->entityType = $this->anticipate('Do you want to use Eloquent or Doctrine ?', ['Eloquent', 'Doctrine'], 'Eloquent');
do {
$entity = $this->ask('Enter entity name. Leaving option empty will continue script.', '<none>');
if (!empty($entity) && $entity !== '<none>') {
$this->entities[] = ucfirst($entity);
}
} while ($entity !== '<none>');
}
/**
*
*/
private function askForValueObjects()
{
do {
$valueObject = $this->ask('Enter value object name. Leaving option empty will continue script.', '<none>');
if (!empty($valueObject) && $valueObject !== '<none>') {
$this->valueObjects[] = ucfirst($valueObject);
}
} while ($valueObject !== '<none>');
}
/**
* Extract the vendor and module name as two separate values
* @param string $fullName
* @return array
*/
private function separateVendorAndName($fullName)
{
$explodedFullName = explode('/', $fullName);
return [
$explodedFullName[0],
ucfirst($explodedFullName[1]),
];
}
/**
* Check if the given module name does not already exists
*
* @param string $name
*/
private function checkForModuleUniqueness($name)
{
/** @var \Illuminate\Filesystem\Filesystem $files */
$files = app('Illuminate\Filesystem\Filesystem');
/** @var \Illuminate\Contracts\Config\Repository $config */
$config = app('Illuminate\Contracts\Config\Repository');
if ($files->isDirectory($config->get('modules.paths.modules') . "/{$name}")) {
return $this->error("The module [$name] already exists");
}
}
}
<?php
namespace Modules\Workshop\Console;
use Illuminate\Console\Command;
use Modules\Workshop\Scaffold\Theme\ThemeScaffold;
class ThemeScaffoldCommand extends Command
{
protected $signature = 'asgard:theme:scaffold';
protected $description = 'Scaffold a new theme';
/**
* @var ThemeScaffold
*/
private $themeScaffold;
public function __construct(ThemeScaffold $themeScaffold)
{
parent::__construct();
$this->themeScaffold = $themeScaffold;
}
public function fire()
{
$themeName = $this->ask('Please enter the theme name in the following format: vendor/name');
list($vendor, $name) = $this->separateVendorAndName($themeName);
$type = $this->choice('Would you like to create a front end or backend theme ?', ['Frontend', 'Backend'], 0);
$this->themeScaffold->setName($name)->setVendor($vendor)->forType(strtolower($type))->generate();
$this->info("Generated a fresh theme called [$themeName]. You'll find it in the Themes/ folder");
}
/**
* Extract the vendor and module name as two separate values
* @param string $fullName
* @return array
*/
private function separateVendorAndName($fullName)
{
$explodedFullName = explode('/', $fullName);
return [
$explodedFullName[0],
ucfirst($explodedFullName[1]),
];
}
}
<?php
namespace Modules\Workshop\Console;
use Illuminate\Console\Command;
use Modules\Core\Services\Composer;
use Symfony\Component\Console\Input\InputArgument;
class UpdateModuleCommand extends Command
{
protected $name = 'asgard:module:update';
protected $description = 'Update a module';
/**
* @var \Modules\Core\Services\Composer
*/
private $composer;
public function __construct(Composer $composer)
{
parent::__construct();
$this->composer = $composer;
}
public function fire()
{
$packageName = $this->getModulePackageName($this->argument('module'));
$this->composer->enableOutput($this);
$this->composer->update($packageName);
}
/**
* Make the full package name for the given module name
* @param string $module
* @return string
*/
private function getModulePackageName($module)
{
return "asgardcms/{$module}-module";
}
protected function getArguments()
{
return [
['module', InputArgument::REQUIRED, 'The module name'],
];
}
}
<?php
namespace Modules\Workshop\Database\Seeders;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Seeder;
class WorkshopDatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
}
}
<?php
namespace Modules\Workshop\Http\Controllers\Admin;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\View;
use Modules\Core\Http\Controllers\Admin\AdminBaseController;
use Modules\Workshop\Manager\ModuleManager;
use Nwidart\Modules\Module;
use Nwidart\Modules\Repository;
use Symfony\Component\Console\Output\BufferedOutput;
class ModulesController extends AdminBaseController
{
/**
* @var ModuleManager
*/
private $moduleManager;
/**
* @var Repository
*/
private $modules;
public function __construct(ModuleManager $moduleManager, Repository $modules)
{
parent::__construct();
$this->moduleManager = $moduleManager;
$this->modules = $modules;
}
/**
* Display a list of all modules
* @return View
*/
public function index()
{
$modules = $this->modules->all();
return view('workshop::admin.modules.index', compact('modules'));
}
/**
* Display module info
* @param Module $module
* @return View
*/
public function show(Module $module)
{
$changelog = $this->moduleManager->changelogFor($module);
return view('workshop::admin.modules.show', compact('module', 'changelog'));
}
/**
* Disable the given module
* @param Module $module
* @return mixed
*/
public function disable(Module $module)
{
if ($this->isCoreModule($module)) {
return redirect()->route('admin.workshop.modules.show', [$module->getLowerName()])
->with('error', trans('workshop::modules.module cannot be disabled'));
}
$module->disable();
return redirect()->route('admin.workshop.modules.show', [$module->getLowerName()])
->with('success', trans('workshop::modules.module disabled'));
}
/**
* Enable the given module
* @param Module $module
* @return mixed
*/
public function enable(Module $module)
{
$module->enable();
return redirect()->route('admin.workshop.modules.show', [$module->getLowerName()])->with('success',
trans('workshop::modules.module enabled'));
}
/**
* Update a given module
* @param Request $request
* @return Response json
*/
public function update(Request $request)
{
$output = new BufferedOutput();
Artisan::call('asgard:update', ['module' => $request->get('module')], $output);
return Response::json(['updated' => true, 'message' => $output->fetch()]);
}
/**
* Check if the given module is a core module that should be be disabled
* @param Module $module
* @return bool
*/
private function isCoreModule(Module $module)
{
$coreModules = array_flip(config('asgard.core.config.CoreModules'));
return isset($coreModules[$module->getLowerName()]);
}
}
<?php
namespace Modules\Workshop\Http\Controllers\Admin;
use FloatingPoint\Stylist\Theme\Theme;
use Modules\Core\Http\Controllers\Admin\AdminBaseController;
use Modules\Workshop\Manager\ThemeManager;
class ThemesController extends AdminBaseController
{
/**
* @var ThemeManager
*/
private $themeManager;
public function __construct(ThemeManager $themeManager)
{
parent::__construct();
$this->themeManager = $themeManager;
}
/**
* @return \Illuminate\View\View
*/
public function index()
{
$themes = $this->themeManager->all();
return view('workshop::admin.themes.index', compact('themes'));
}
/**
* @param Theme $theme
* @return \Illuminate\View\View
*/
public function show(Theme $theme)
{
return view('workshop::admin.themes.show', compact('theme'));
}
}
<?php
namespace Modules\Workshop\Http\Controllers\Admin;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\View;
use Laracasts\Flash\Flash;
use Modules\Core\Http\Controllers\Admin\AdminBaseController;
use Modules\Workshop\Http\Requests\GenerateModuleRequest;
use Modules\Workshop\Http\Requests\InstallModuleRequest;
use Modules\Workshop\Http\Requests\MigrateModuleRequest;
use Modules\Workshop\Http\Requests\SeedModuleRequest;
use Symfony\Component\Console\Output\BufferedOutput;
class WorkbenchController extends AdminBaseController
{
public function __construct()
{
parent::__construct();
}
/**
* Show the index view
* @return mixed
*/
public function index()
{
return View::make('workshop::admin.workbench.index');
}
/**
* Generate a module given its name
* @param GenerateModuleRequest $request
* @return mixed
*/
public function generate(GenerateModuleRequest $request)
{
$output = new BufferedOutput();
Artisan::call('module:make', ['name' => $request->name], $output);
Flash::message($output->fetch());
return Redirect::route('admin.workshop.workbench.index');
}
/**
* Run the migration for the given module
* @param MigrateModuleRequest $request
* @return mixed
*/
public function migrate(MigrateModuleRequest $request)
{
$output = new BufferedOutput();
Artisan::call('module:migrate', ['module' => $request->module], $output);
Flash::message($output->fetch());
return Redirect::route('admin.workshop.workbench.index');
}
/**
* Run the install command for the given vendor/module
* @param InstallModuleRequest $request
* @return mixed
*/
public function install(InstallModuleRequest $request)
{
$output = new BufferedOutput();
$arguments = [];
$arguments['name'] = $request->vendorName;
if ($request->subtree) {
$arguments['--tree'] = '';
}
Artisan::call('module:install', $arguments, $output);
Flash::message($output->fetch());
return Redirect::route('admin.workshop.workbench.index');
}
/**
* Run the seed command for the given module
* @param SeedModuleRequest $request
* @return mixed
*/
public function seed(SeedModuleRequest $request)
{
$output = new BufferedOutput();
Artisan::call('module:seed', ['module' => $request->module], $output);
Flash::message($output->fetch());
return Redirect::route('admin.workshop.workbench.index');
}
}
<?php
namespace Modules\Workshop\Http\Controllers\Api;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Artisan;
use InvalidArgumentException;
use Nwidart\Modules\Module;
class ModulesController extends Controller
{
public function publishAssets(Module $module)
{
try {
Artisan::call('module:publish', ['module' => $module->getName()]);
} catch (InvalidArgumentException $e) {
}
}
}
<?php
namespace Modules\Workshop\Http\Controllers\Api;
use FloatingPoint\Stylist\Theme\Theme;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Artisan;
use InvalidArgumentException;
class ThemeController extends Controller
{
public function publishAssets(Theme $theme)
{
try {
Artisan::call('stylist:publish', ['theme' => $theme->getName()]);
} catch (InvalidArgumentException $e) {
}
}
}
<?php
namespace Modules\Workshop\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class GenerateModuleRequest extends FormRequest
{
public function rules()
{
return [
'name' => 'required',
];
}
public function authorize()
{
return true;
}
public function messages()
{
return [];
}
}
<?php
namespace Modules\Workshop\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class InstallModuleRequest extends FormRequest
{
public function rules()
{
return [
'vendorName' => 'required',
];
}
public function authorize()
{
return true;
}
public function messages()
{
return [];
}
}
<?php
namespace Modules\Workshop\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MigrateModuleRequest extends FormRequest
{
public function rules()
{
return [
'module' => 'required',
];
}
public function authorize()
{
return true;
}
public function messages()
{
return [];
}
}
<?php
namespace Modules\Workshop\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ModulesRequest extends FormRequest
{
public function rules()
{
return [];
}
public function authorize()
{
return true;
}
public function messages()
{
return [];
}
}
<?php
namespace Modules\Workshop\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class SeedModuleRequest extends FormRequest
{
public function rules()
{
return [
'module' => 'required',
];
}
public function authorize()
{
return true;
}
public function messages()
{
return [];
}
}
<?php
use Illuminate\Routing\Router;
/** @var Router $router */
$router->group(['prefix' => 'workshop', 'middleware' => 'api.token'], function (Router $router) {
$router->post('modules/{module}/publish', [
'as' => 'api.workshop.module.publish',
'uses' => 'ModulesController@publishAssets',
'middleware' => 'token-can:workshop.modules.publish',
]);
$router->post('themes/{theme}/publish', [
'as' => 'api.workshop.theme.publish',
'uses' => 'ThemeController@publishAssets',
'middleware' => 'token-can:workshop.themes.publish',
]);
});
<?php
use Illuminate\Routing\Router;
/** @var Router $router */
$router->bind('module', function ($module) {
return app(\Nwidart\Modules\Repository::class)->find($module);
});
$router->bind('theme', function ($theme) {
return app(\Modules\Workshop\Manager\ThemeManager::class)->find($theme);
});
$router->group(['prefix' => '/workshop'],
function (Router $router) {
$router->get('modules', [
'as' => 'admin.workshop.modules.index',
'uses' => 'ModulesController@index',
'middleware' => 'can:workshop.modules.index',
]);
$router->get('modules/{module}', [
'as' => 'admin.workshop.modules.show',
'uses' => 'ModulesController@show',
'middleware' => 'can:workshop.modules.show',
]);
$router->post('modules/update', [
'as' => 'admin.workshop.modules.update',
'uses' => 'ModulesController@update',
'middleware' => 'can:workshop.modules.update',
]);
$router->post('modules/disable/{module}', [
'as' => 'admin.workshop.modules.disable',
'uses' => 'ModulesController@disable',
'middleware' => 'can:workshop.modules.disable',
]);
$router->post('modules/enable/{module}', [
'as' => 'admin.workshop.modules.enable',
'uses' => 'ModulesController@enable',
'middleware' => 'can:workshop.modules.enable',
]);
$router->get('themes', [
'as' => 'admin.workshop.themes.index',
'uses' => 'ThemesController@index',
'middleware' => 'can:workshop.themes.index',
]);
$router->get('themes/{theme}', [
'as' => 'admin.workshop.themes.show',
'uses' => 'ThemesController@show',
'middleware' => 'can:workshop.themes.show',
]);
}
);
# License (MIT)
Copyright (c) 2016 Nicolas Widart , n.widart@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
<?php
namespace Modules\Workshop\Manager;
use Illuminate\Config\Repository as Config;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Collection;
use Nwidart\Modules\Module;
use Symfony\Component\Yaml\Parser;
class ModuleManager
{
/**
* @var Module
*/
private $module;
/**
* @var Config
*/
private $config;
/**
* @var PackageInformation
*/
private $packageVersion;
/**
* @var Filesystem
*/
private $finder;
/**
* @param Config $config
* @param PackageInformation $packageVersion
* @param Filesystem $finder
*/
public function __construct(Config $config, PackageInformation $packageVersion, Filesystem $finder)
{
$this->module = app('modules');
$this->config = $config;
$this->packageVersion = $packageVersion;
$this->finder = $finder;
}
/**
* Return all modules
* @return \Illuminate\Support\Collection
*/
public function all()
{
$modules = new Collection($this->module->all());
foreach ($modules as $module) {
$moduleName = $module->getName();
$package = $this->packageVersion->getPackageInfo("asgardcms/$moduleName-module");
$module->version = isset($package->version) ? $package->version: 'N/A';
$module->versionUrl = '#';
if (isset($package->source->url)) {
$packageUrl = str_replace('.git', '', $package->source->url);
$module->versionUrl = $packageUrl . '/tree/' . $package->dist->reference;
}
}
return $modules;
}
/**
* Return all the enabled modules
* @return array
*/
public function enabled()
{
return $this->module->enabled();
}
/**
* Get the core modules that shouldn't be disabled
* @return array|mixed
*/
public function getCoreModules()
{
$coreModules = $this->config->get('asgard.core.config.CoreModules');
$coreModules = array_flip($coreModules);
return $coreModules;
}
/**
* Get the enabled modules, with the module name as the key
* @return array
*/
public function getFlippedEnabledModules()
{
$enabledModules = $this->module->enabled();
$enabledModules = array_map(function (Module $module) {
return $module->getName();
}, $enabledModules);
return array_flip($enabledModules);
}
/**
* Disable the given modules
* @param $enabledModules
*/
public function disableModules($enabledModules)
{
$coreModules = $this->getCoreModules();
foreach ($enabledModules as $moduleToDisable => $value) {
if (isset($coreModules[$moduleToDisable])) {
continue;
}
$module = $this->module->get($moduleToDisable);
$module->disable();
}
}
/**
* Enable the given modules
* @param $modules
*/
public function enableModules($modules)
{
foreach ($modules as $moduleToEnable => $value) {
$module = $this->module->get($moduleToEnable);
$module->enable();
}
}
/**
* Get the changelog for the given module
* @param Module $module
* @return array
*/
public function changelogFor(Module $module)
{
$path = $module->getPath() . '/changelog.yml';
if (! $this->finder->isFile($path)) {
return [];
}
$yamlParser = new Parser();
$changelog = $yamlParser->parse(file_get_contents($path));
$changelog['versions'] = $this->limitLastVersionsAmount(array_get($changelog, 'versions', []));
return $changelog;
}
/**
* Limit the versions to the last 5
* @param array $versions
* @return array
*/
private function limitLastVersionsAmount(array $versions)
{
return array_slice($versions, 0, 5);
}
}
<?php
namespace Modules\Workshop\Manager;
use Illuminate\Contracts\Filesystem\Filesystem;
class PackageInformation
{
/**
* @var Filesystem
*/
private $finder;
public function __construct(Filesystem $finder)
{
$this->finder = $finder;
}
/**
* Get the exact installed version for the specified package
* @param string $packageName
* @return string mixed
*/
public function getPackageInfo($packageName)
{
$composerLock = json_decode($this->finder->get('composer.lock'));
foreach ($composerLock->packages as $package) {
if ($package->name == $packageName) {
return $package;
}
}
}
}
<?php
namespace Modules\Workshop\Manager;
use FloatingPoint\Stylist\Theme\Exceptions\ThemeNotFoundException;
use FloatingPoint\Stylist\Theme\Json;
use FloatingPoint\Stylist\Theme\Theme;
use Illuminate\Filesystem\Filesystem;
use Symfony\Component\Yaml\Parser;
class StylistThemeManager implements ThemeManager
{
/**
* @var Filesystem
*/
private $finder;
public function __construct(Filesystem $finder)
{
$this->finder = $finder;
}
/**
* @return array
*/
public function all()
{
$directories = $this->getDirectories();
$themes = [];
foreach ($directories as $directory) {
$themes[] = $this->getThemeInfoForPath($directory);
}
return $themes;
}
/**
* @param string $themeName
* @return Theme
* @throws ThemeNotFoundException
*/
public function find($themeName)
{
foreach ($this->getDirectories() as $directory) {
if (! str_contains(strtolower($directory), strtolower($themeName))) {
continue;
}
return $this->getThemeInfoForPath($directory);
}
throw new ThemeNotFoundException($themeName);
}
/**
* @param string $directory
* @return Theme
*/
private function getThemeInfoForPath($directory)
{
$themeJson = new Json($directory);
$theme = new Theme(
$themeJson->getJsonAttribute('name'),
$themeJson->getJsonAttribute('description'),
$directory,
$themeJson->getJsonAttribute('parent')
);
$theme->version = $themeJson->getJsonAttribute('version');
$theme->type = ucfirst($themeJson->getJsonAttribute('type'));
$theme->changelog = $this->getChangelog($directory);
$theme->active = $this->getStatus($theme);
return $theme;
}
/**
* Get all theme directories
* @return array
*/
private function getDirectories()
{
$themePath = config('stylist.themes.paths', [base_path('/Themes')]);
return $this->finder->directories($themePath[0]);
}
/**
* @param string $directory
* @return array
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
private function getChangelog($directory)
{
if (! $this->finder->isFile($directory . '/changelog.yml')) {
return [];
}
$yamlFile = $this->finder->get($directory . '/changelog.yml');
$yamlParser = new Parser();
$changelog = $yamlParser->parse($yamlFile);
$changelog['versions'] = $this->limitLastVersionsAmount(array_get($changelog, 'versions', []));
return $changelog;
}
/**
* Limit the versions to the last 5
* @param array $versions
* @return array
*/
private function limitLastVersionsAmount(array $versions)
{
return array_slice($versions, 0, 5);
}
/**
* Check if the theme is active based on its type
* @param Theme $theme
* @return bool
*/
private function getStatus(Theme $theme)
{
if ($theme->type !== 'Backend') {
return setting('core::template') === $theme->getName();
}
return config('asgard.core.core.admin-theme') === $theme->getName();
}
}
<?php
namespace Modules\Workshop\Manager;
interface ThemeManager
{
/**
* Get all themes
* @return array
*/
public function all();
}
<?php
namespace Modules\Workshop\Providers;
use Modules\Core\Providers\RoutingServiceProvider as CoreRoutingServiceProvider;
class RouteServiceProvider extends CoreRoutingServiceProvider
{
/**
* The root namespace to assume when generating URLs to actions.
* @var string
*/
protected $namespace = 'Modules\Workshop\Http\Controllers';
/**
* @return string
*/
protected function getFrontendRoute()
{
return false;
}
/**
* @return string
*/
protected function getBackendRoute()
{
return __DIR__ . '/../Http/backendRoutes.php';
}
/**
* @return string
*/
protected function getApiRoute()
{
return __DIR__ . '/../Http/apiRoutes.php';
}
}
<?php
namespace Modules\Workshop\Providers;
use Illuminate\Support\ServiceProvider;
use Modules\Core\Services\Composer;
use Modules\Core\Traits\CanPublishConfiguration;
use Modules\Workshop\Console\EntityScaffoldCommand;
use Modules\Workshop\Console\ModuleScaffoldCommand;
use Modules\Workshop\Console\ThemeScaffoldCommand;
use Modules\Workshop\Console\UpdateModuleCommand;
use Modules\Workshop\Manager\StylistThemeManager;
use Modules\Workshop\Manager\ThemeManager;
use Modules\Workshop\Scaffold\Module\Generators\EntityGenerator;
use Modules\Workshop\Scaffold\Module\Generators\FilesGenerator;
use Modules\Workshop\Scaffold\Module\Generators\ValueObjectGenerator;
use Modules\Workshop\Scaffold\Module\ModuleScaffold;
use Modules\Workshop\Scaffold\Theme\ThemeGeneratorFactory;
use Modules\Workshop\Scaffold\Theme\ThemeScaffold;
class WorkshopServiceProvider extends ServiceProvider
{
use CanPublishConfiguration;
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->registerCommands();
$this->bindThemeManager();
}
public function boot()
{
$this->publishConfig('workshop', 'permissions');
$this->publishConfig('workshop', 'config');
$this->loadMigrationsFrom(__DIR__.'/../Database/Migrations');
}
/**
* Register artisan commands
*/
private function registerCommands()
{
$this->registerModuleScaffoldCommand();
$this->registerUpdateCommand();
$this->registerThemeScaffoldCommand();
$this->commands([
'command.asgard.module.scaffold',
'command.asgard.module.update',
'command.asgard.theme.scaffold',
EntityScaffoldCommand::class,
]);
}
/**
* Register the scaffold command
*/
private function registerModuleScaffoldCommand()
{
$this->app->singleton('asgard.module.scaffold', function ($app) {
return new ModuleScaffold(
$app['files'],
$app['config'],
new EntityGenerator($app['files'], $app['config']),
new ValueObjectGenerator($app['files'], $app['config']),
new FilesGenerator($app['files'], $app['config'])
);
});
$this->app->singleton('command.asgard.module.scaffold', function ($app) {
return new ModuleScaffoldCommand($app['asgard.module.scaffold']);
});
}
/**
* Register the update module command
*/
private function registerUpdateCommand()
{
$this->app->singleton('command.asgard.module.update', function ($app) {
return new UpdateModuleCommand(new Composer($app['files'], base_path()));
});
}
/**
* Register the theme scaffold command
*/
private function registerThemeScaffoldCommand()
{
$this->app->singleton('asgard.theme.scaffold', function ($app) {
return new ThemeScaffold(new ThemeGeneratorFactory(), $app['files']);
});
$this->app->singleton('command.asgard.theme.scaffold', function ($app) {
return new ThemeScaffoldCommand($app['asgard.theme.scaffold']);
});
}
/**
* Bind the theme manager
*/
private function bindThemeManager()
{
$this->app->singleton(ThemeManager::class, function ($app) {
return new StylistThemeManager($app['files']);
});
}
}
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('workshop::modules.title') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('user::users.breadcrumb.home') }}</a></li>
<li class="active">{{ trans('workshop::modules.breadcrumb.modules') }}</li>
</ol>
@stop
@section('styles')
<style>
.jsUpdateModule {
transition: all .5s ease-in-out;
}
</style>
@stop
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header">
</div>
<!-- /.box-header -->
<div class="box-body">
<table class="data-table table table-bordered table-hover">
<thead>
<tr>
<th>{{ trans('workshop::modules.table.name') }}</th>
<th width="15%">{{ trans('workshop::modules.table.version') }}</th>
<th width="15%">{{ trans('workshop::modules.table.enabled') }}</th>
</tr>
</thead>
<tbody>
<?php if (isset($modules)): ?>
<?php foreach ($modules as $module): ?>
<tr>
<td>
<a href="{{ route('admin.workshop.modules.show', [$module->getLowerName()]) }}">
{{ $module->name }}
</a>
</td>
<td>
<a href="{{ route('admin.workshop.modules.show', [$module->getLowerName()]) }}">
{{ str_replace('v', '', $module->version) }}
</a>
</td>
<td>
<a href="{{ route('admin.workshop.modules.show', [$module->getLowerName()]) }}">
<span class="label label-{{$module->enabled() ? 'success' : 'danger'}}">
{{ $module->enabled() ? trans('workshop::modules.enabled') : trans('workshop::modules.disabled') }}
</span>
</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
<tfoot>
<tr>
<th>{{ trans('workshop::modules.table.name') }}</th>
<th>{{ trans('workshop::modules.table.version') }}</th>
<th>{{ trans('workshop::modules.table.enabled') }}</th>
</tr>
</tfoot>
</table>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
</div>
</div>
@stop
@section('scripts')
<?php $locale = locale(); ?>
<script>
$(function () {
$('.data-table').dataTable({
"paginate": true,
"lengthChange": true,
"filter": true,
"sort": true,
"info": true,
"autoWidth": true,
"order": [[ 0, "asc" ]],
"language": {
"url": '<?php echo Module::asset("core:js/vendor/datatables/{$locale}.json") ?>'
},
"columns": [
null,
null,
null,
]
});
});
</script>
<script>
$( document ).ready(function() {
$('input[type="checkbox"].flat-blue, input[type="radio"].flat-blue').iCheck({
checkboxClass: 'icheckbox_flat-blue',
radioClass: 'iradio_flat-blue'
});
$('.jsUpdateModule').on('click', function(e) {
$(this).data('loading-text', '<i class="fa fa-spinner fa-spin"></i> Loading ...');
var $btn = $(this).button('loading');
var token = '<?= csrf_token() ?>';
$.ajax({
type: 'POST',
url: '<?= route('admin.workshop.modules.update') ?>',
data: {module: $btn.data('module'), _token: token},
success: function(data) {
console.log(data);
if (data.updated) {
$btn.button('reset');
$btn.removeClass('btn-primary');
$btn.addClass('btn-success');
$btn.html('<i class="fa fa-check"></i> Module updated!')
setTimeout(function() {
$btn.removeClass('btn-success');
$btn.addClass('btn-primary');
$btn.html('Update')
}, 2000);
}
}
});
});
});
</script>
@stop
<span class="label label-{{ $label }}">{{ $title }}</span>
<ul>
<?php foreach ($data as $dataLine): ?>
<li class="text-{{ $color }}">
{!! $dataLine !!}
</li>
<?php endforeach; ?>
</ul>
<?php foreach ($changelog['versions'] as $version => $info): ?>
<dl class="dl-horizontal">
<dt>
<?php if (str_contains($version, ['unreleased', 'dev'])): ?>
{{ $version }}
<?php else: ?>
<a href="{{ $changelog['url'].'/releases/tag/'.$version }}" target="_blank">
<i class="fa fa-external-link-square"></i> {{ $version }}
</a>
<?php endif; ?>
</dt>
<dd>
<?php if (isset($info['added'])): ?>
@include('workshop::admin.modules.partials.changelog-part', [
'title' => trans('workshop::modules.added'),
'label' => 'success',
'color' => 'green',
'data' => $info['added']
])
<?php endif; ?>
<?php if (isset($info['changed'])): ?>
@include('workshop::admin.modules.partials.changelog-part', [
'title' => trans('workshop::modules.changed'),
'label' => 'warning',
'color' => 'orange',
'data' => $info['changed']
])
<?php endif; ?>
<?php if (isset($info['removed'])): ?>
@include('workshop::admin.modules.partials.changelog-part', [
'title' => trans('workshop::modules.removed'),
'label' => 'danger',
'color' => 'red',
'data' => $info['removed']
])
<?php endif; ?>
</dd>
</dl>
<?php endforeach; ?>
@extends('layouts.master')
@section('content-header')
<h1>
<small>
<a href="{{ route('admin.workshop.modules.index') }}" data-toggle="tooltip"
title="" data-original-title="{{ trans('core::core.back') }}">
<i class="fa fa-reply"></i>
</a>
</small>
{{ $module->name }} <small>{{ trans('workshop::modules.module') }}</small>
</h1>
<ol class="breadcrumb">
<li><a href="{{ route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('user::users.breadcrumb.home') }}</a></li>
<li><a href="{{ route('admin.workshop.modules.index') }}">{{ trans('workshop::modules.breadcrumb.modules') }}</a></li>
<li class="active">{{ trans('workshop::modules.viewing module') }} {{ $module->name }}</li>
</ol>
@stop
@section('styles')
<style>
.module-type {
text-align: center;
}
.module-type span {
display: block;
}
.module-type i {
font-size: 124px;
}
form {
display: inline;
}
</style>
@stop
@section('content')
<div class="row">
<div class="col-md-12">
<div class="box box-primary">
<div class="box-header">
<div class="box-tools pull-right">
<?php $status = $module->enabled() ? 'disable' : 'enable'; ?>
<button class="btn btn-box-tool jsPublishAssets" data-toggle="tooltip"
title="" data-original-title="{{ trans("workshop::modules.publish assets") }}">
<i class="fa fa-cloud-upload"></i>
{{ trans("workshop::modules.publish assets") }}
</button>
<?php $routeName = $module->enabled() ? 'disable' : 'enable' ?>
{!! Form::open(['route' => ["admin.workshop.modules.$routeName", $module->getName()], 'method' => 'post']) !!}
<button class="btn btn-box-tool" data-toggle="tooltip" type="submit"
title="" data-original-title="{{ trans("workshop::modules.{$status}") }}">
<i class="fa fa-toggle-{{ $module->enabled() ? 'on' : 'off' }}"></i>
{{ trans("workshop::modules.{$status}") }}
</button>
{!! Form::close() !!}
</div>
</div>
<div class="box-body">
<div class="row">
<div class="col-sm-12 module-details">
<div class="module-type pull-left">
<i class="fa fa-cube"></i>
<span>{{ $module->version }}</span>
</div>
<h2>{{ ucfirst($module->getName()) }}</h2>
<p>{{ $module->getDescription() }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
<?php if (!empty($changelog) && count($changelog['versions'])): ?>
<div class="row">
<div class="col-md-12">
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title"><i class="fa fa-bars"></i> {{ trans('workshop::modules.changelog')}}</h3>
<div class="box-tools pull-right">
<button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
</div>
</div>
<div class="box-body">
@include('workshop::admin.modules.partials.changelog')
</div>
</div>
</div>
</div>
<?php endif; ?>
@stop
@section('scripts')
<script>
$( document ).ready(function() {
$('.jsPublishAssets').on('click',function (event) {
event.preventDefault();
var $self = $(this);
$self.find('i').toggleClass('fa-cloud-upload fa-refresh fa-spin');
$.ajax({
type: 'POST',
url: '{{ route('api.workshop.module.publish', [$module->getName()]) }}',
data: {_token: '{{ csrf_token() }}'},
success: function() {
$self.find('i').toggleClass('fa-cloud-upload fa-refresh fa-spin');
}
});
});
});
</script>
@stop
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('workshop::themes.title') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('user::users.breadcrumb.home') }}</a></li>
<li class="active">{{ trans('workshop::themes.breadcrumb.themes') }}</li>
</ol>
@stop
@section('styles')
<style>
.jsUpdateModule {
transition: all .5s ease-in-out;
}
</style>
@stop
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="box box-primary">
<div class="box-header">
</div>
<!-- /.box-header -->
<div class="box-body">
<table class="data-table table table-bordered table-hover">
<thead>
<tr>
<th>{{ trans('workshop::modules.table.name') }}</th>
<th width="15%">{{ trans('workshop::themes.type') }}</th>
<th width="15%">{{ trans('workshop::modules.table.version') }}</th>
<th width="15%">{{ trans('workshop::modules.table.enabled') }}</th>
</tr>
</thead>
<tbody>
<?php if (isset($themes)): ?>
<?php foreach ($themes as $theme): ?>
<tr>
<td>
<a href="{{ route('admin.workshop.themes.show', [$theme->getName()]) }}">
{{ $theme->getName() }}
</a>
</td>
<td>
<a href="{{ route('admin.workshop.themes.show', [$theme->getName()]) }}">
{{ $theme->type }}
</a>
</td>
<td>
<a href="{{ route('admin.workshop.themes.show', [$theme->getName()]) }}">
{{ $theme->version }}
</a>
</td>
<td>
<a href="{{ route('admin.workshop.themes.show', [$theme->getName()]) }}">
<span class="label label-{{$theme->active ? 'success' : 'danger'}}">
{{ $theme->active ? trans('workshop::modules.enabled') : trans('workshop::modules.disabled') }}
</span>
</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
<tfoot>
<tr>
<th>{{ trans('workshop::modules.table.name') }}</th>
<th>{{ trans('workshop::themes.type') }}</th>
<th>{{ trans('workshop::modules.table.version') }}</th>
<th>{{ trans('workshop::modules.table.enabled') }}</th>
</tr>
</tfoot>
</table>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
</div>
</div>
@stop
@section('scripts')
<?php $locale = locale(); ?>
<script>
$(function () {
$('.data-table').dataTable({
"paginate": true,
"lengthChange": true,
"filter": true,
"sort": true,
"info": true,
"autoWidth": true,
"order": [[ 0, "asc" ]],
"language": {
"url": '<?php echo Module::asset("core:js/vendor/datatables/{$locale}.json") ?>'
},
"columns": [
null,
null,
null,
]
});
});
</script>
<script>
$( document ).ready(function() {
$('input[type="checkbox"].flat-blue, input[type="radio"].flat-blue').iCheck({
checkboxClass: 'icheckbox_flat-blue',
radioClass: 'iradio_flat-blue'
});
$('.jsUpdateModule').on('click', function(e) {
$(this).data('loading-text', '<i class="fa fa-spinner fa-spin"></i> Loading ...');
var $btn = $(this).button('loading');
var token = '<?= csrf_token() ?>';
$.ajax({
type: 'POST',
url: '<?= route('admin.workshop.modules.update') ?>',
data: {module: $btn.data('module'), _token: token},
success: function(data) {
console.log(data);
if (data.updated) {
$btn.button('reset');
$btn.removeClass('btn-primary');
$btn.addClass('btn-success');
$btn.html('<i class="fa fa-check"></i> Module updated!')
setTimeout(function() {
$btn.removeClass('btn-success');
$btn.addClass('btn-primary');
$btn.html('Update')
}, 2000);
}
}
});
});
});
</script>
@stop
@extends('layouts.master')
@section('content-header')
<h1>
<small>
<a href="{{ route('admin.workshop.themes.index') }}" data-toggle="tooltip"
title="" data-original-title="{{ trans('core::core.back') }}">
<i class="fa fa-reply"></i>
</a>
</small>
{{ $theme->getName() }} <small>{{ trans('workshop::themes.theme') }}</small>
</h1>
<ol class="breadcrumb">
<li><a href="{{ route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('user::users.breadcrumb.home') }}</a></li>
<li><a href="{{ route('admin.workshop.themes.index') }}">{{ trans('workshop::themes.breadcrumb.themes') }}</a></li>
<li class="active">{{ trans('workshop::themes.viewing theme', ['theme' => $theme->getName()]) }}</li>
</ol>
@stop
@section('styles')
<style>
.module-type {
text-align: center;
}
.module-type span {
display: block;
}
.module-type i {
font-size: 124px;
margin-right: 20px;
}
.module-type span {
margin-left: -20px;
}
form {
display: inline;
}
</style>
@stop
@section('content')
<div class="row">
<div class="col-md-12">
<div class="box box-primary">
<div class="box-header">
<div class="box-tools pull-right">
<button class="btn btn-box-tool jsPublishAssets" data-toggle="tooltip"
title="" data-original-title="{{ trans("workshop::modules.publish assets") }}">
<i class="fa fa-cloud-upload"></i>
{{ trans("workshop::modules.publish assets") }}
</button>
</div>
</div>
<div class="box-body">
<div class="row">
<div class="col-sm-6 module-details">
<div class="module-type pull-left">
<i class="fa fa-picture-o"></i>
<span>
{{ $theme->version }}
</span>
</div>
<h2>{{ ucfirst($theme->getName()) }}</h2>
<p>{{ $theme->getDescription() }}</p>
</div>
<div class="col-sm-6">
<dl class="dl-horizontal">
<dt>{{ trans('workshop::themes.type') }}:</dt>
<dd>{{ $theme->type }}</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
</div>
<?php if (!empty($theme->changelog) && count($theme->changelog['versions'])): ?>
<div class="row">
<div class="col-md-12">
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title"><i class="fa fa-bars"></i> {{ trans('workshop::modules.changelog')}}</h3>
<div class="box-tools pull-right">
<button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
</div>
</div>
<div class="box-body">
@include('workshop::admin.modules.partials.changelog', [
'changelog' => $theme->changelog
])
</div>
</div>
</div>
</div>
<?php endif; ?>
@stop
@section('scripts')
<script>
$( document ).ready(function() {
$('.jsPublishAssets').on('click',function (event) {
event.preventDefault();
var $self = $(this);
$self.find('i').toggleClass('fa-cloud-upload fa-refresh fa-spin');
$.ajax({
type: 'POST',
url: '{{ route('api.workshop.theme.publish', [$theme->getName()]) }}',
data: {_token: '{{ csrf_token() }}'},
success: function() {
$self.find('i').toggleClass('fa-cloud-upload fa-refresh fa-spin');
}
});
});
});
</script>
@stop
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('workshop::workbench.title') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ URL::route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('user::users.breadcrumb.home') }}</a></li>
<li class="active">{{ trans('workshop::workbench.title') }}</li>
</ol>
@stop
@section('content')
<div class="row">
<div class="col-md-12">
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<li class="active"><a href="#tab_1-1" data-toggle="tab">{{ trans('workshop::workbench.tab.generators') }}</a></li>
<li><a href="#tab_2-2" data-toggle="tab">{{ trans('workshop::workbench.tab.migrations') }}</a></li>
<li><a href="#tab_3-3" data-toggle="tab">{{ trans('workshop::workbench.tab.seeds') }}</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tab_1-1">
@include('workshop::admin.workbench.tabs.generate')
</div>
<div class="tab-pane" id="tab_2-2">
@include('workshop::admin.workbench.tabs.migrate')
</div>
<div class="tab-pane" id="tab_3-3">
@include('workshop::admin.workbench.tabs.seed')
</div>
</div>
</div>
</div>
</div>
@stop
@section('scripts')
<script>
$( document ).ready(function() {
$('input[type="checkbox"].flat-blue, input[type="radio"].flat-blue').iCheck({
checkboxClass: 'icheckbox_flat-blue',
radioClass: 'iradio_flat-blue'
});
});
</script>
@stop
<div class="row">
<div class="col-md-6">
{!! Form::open(['route' => 'admin.workshop.workbench.generate.index', 'method' => 'post']) !!}
<div class="box-body">
<h4>{{ trans('workshop::workbench.subtitle.generate new module') }}</h4>
<div class='form-group{{ $errors->has('name') ? ' has-error' : '' }}'>
{!! Form::label('name', trans('workshop::workbench.form.module name')) !!}
{!! Form::text('name', Input::old('name'), ['class' => 'form-control', 'placeholder' => trans('workshop::workbench.form.module name')]) !!}
{!! $errors->first('name', '<span class="help-block">:message</span>') !!}
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat">{{ trans('workshop::workbench.button.generate new module') }}</button>
</div>
{!! Form::close() !!}
</div>
<div class="col-md-6">
{!! Form::open(['route' => 'admin.workshop.workbench.install.index', 'method' => 'post']) !!}
<div class="box-body">
<h4>{{ trans('workshop::workbench.subtitle.install new module by vendor name') }}</h4>
<div class='form-group{{ $errors->has('vendorName') ? ' has-error' : '' }}'>
{!! Form::label('vendorName', trans('workshop::workbench.form.vendor name of the module')) !!}
{!! Form::text('vendorName', Input::old('vendorName'), ['class' => 'form-control', 'placeholder' => trans('workshop::workbench.form.vendor name of the module')]) !!}
{!! $errors->first('vendorName', '<span class="help-block">:message</span>') !!}
</div>
<div class="checkbox">
<label for="subtree">
<input id="subtree" name="subtree" type="checkbox" class="flat-blue" value="true" /> {{ trans('workshop::workbench.form.install as subtree') }}
</label>
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat">{{ trans('workshop::workbench.button.install new module') }}</button>
</div>
{!! Form::close() !!}
</div>
</div>
{!! Form::open(['route' => 'admin.workshop.workbench.migrate.index', 'method' => 'post']) !!}
<div class="box-body">
<div class='form-group{{ $errors->has('module') ? ' has-error' : '' }}'>
{!! Form::label('module', trans('workshop::workbench.form.module name')) !!}
{!! Form::select('module', $modules, null, ['class' => 'form-control']) !!}
{!! $errors->first('module', '<span class="help-block">:message</span>') !!}
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat">{{ trans('workshop::workbench.button.migrate') }}</button>
</div>
{!! Form::close() !!}
{!! Form::open(['route' => 'admin.workshop.workbench.seed.index', 'method' => 'post']) !!}
<div class="box-body">
<div class='form-group{{ $errors->has('module') ? ' has-error' : '' }}'>
{!! Form::label('module', trans('workshop::workbench.form.module name')) !!}
{!! Form::select('module', $modules, null, ['class' => 'form-control']) !!}
{!! $errors->first('module', '<span class="help-block">:message</span>') !!}
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat">{{ trans('workshop::workbench.button.seed') }}</button>
</div>
{!! Form::close() !!}
<?php
namespace Modules\Workshop\Scaffold\Module\Exception;
class ModuleExistsException extends \Exception
{
}
<?php
namespace Modules\Workshop\Scaffold\Module\Generators;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Filesystem\Filesystem;
class EntityGenerator extends Generator
{
/**
* @var \Illuminate\Contracts\Console\Kernel
*/
protected $artisan;
public function __construct(Filesystem $finder, Repository $config)
{
parent::__construct($finder, $config);
$this->artisan = app('Illuminate\Contracts\Console\Kernel');
}
protected $views = [
'index-view.stub' => 'Resources/views/admin/$ENTITY_NAME$/index.blade',
'create-view.stub' => 'Resources/views/admin/$ENTITY_NAME$/create.blade',
'edit-view.stub' => 'Resources/views/admin/$ENTITY_NAME$/edit.blade',
'create-fields.stub' => 'Resources/views/admin/$ENTITY_NAME$/partials/create-fields.blade',
'edit-fields.stub' => 'Resources/views/admin/$ENTITY_NAME$/partials/edit-fields.blade',
];
/**
* Generate the given entities
*
* @param array $entities
* @param bool $regenerateSidebar
*/
public function generate(array $entities, $regenerateSidebar = true)
{
$entityType = strtolower($this->entityType);
$entityTypeStub = "entity-{$entityType}.stub";
if ($regenerateSidebar === true) {
$this->generateSidebarExtender($entities);
}
foreach ($entities as $entity) {
$this->writeFile(
$this->getModulesPath("Entities/$entity"),
$this->getContentForStub($entityTypeStub, $entity)
);
$this->writeFile(
$this->getModulesPath("Entities/{$entity}Translation"),
$this->getContentForStub("{$entityType}-entity-translation.stub", $entity)
);
if ($this->entityType == 'Eloquent') {
$this->generateMigrationsFor($entity);
}
$this->generateRepositoriesFor($entity);
$this->generateControllerFor($entity);
$this->generateViewsFor($entity);
$this->generateLanguageFilesFor($entity);
$this->appendBindingsToServiceProviderFor($entity);
$this->appendResourceRoutesToRoutesFileFor($entity);
$this->appendPermissionsFor($entity);
$this->appendSidebarLinksFor($entity);
}
}
/**
* Generate the repositories for the given entity
*
* @param string $entity
*/
private function generateRepositoriesFor($entity)
{
if (! $this->finder->isDirectory($this->getModulesPath('Repositories/' . $this->entityType))) {
$this->finder->makeDirectory($this->getModulesPath('Repositories/' . $this->entityType));
}
$entityType = strtolower($this->entityType);
$this->writeFile(
$this->getModulesPath("Repositories/{$entity}Repository"),
$this->getContentForStub('repository-interface.stub', $entity)
);
$this->writeFile(
$this->getModulesPath("Repositories/Cache/Cache{$entity}Decorator"),
$this->getContentForStub('cache-repository-decorator.stub', $entity)
);
$this->writeFile(
$this->getModulesPath("Repositories/{$this->entityType}/{$this->entityType}{$entity}Repository"),
$this->getContentForStub("{$entityType}-repository.stub", $entity)
);
}
/**
* Generate the controller for the given entity
*
* @param string $entity
*/
private function generateControllerFor($entity)
{
$path = $this->getModulesPath('Http/Controllers/Admin');
if (! $this->finder->isDirectory($path)) {
$this->finder->makeDirectory($path);
}
$this->writeFile(
$this->getModulesPath("Http/Controllers/Admin/{$entity}Controller"),
$this->getContentForStub('admin-controller.stub', $entity)
);
}
/**
* Generate views for the given entity
*
* @param string $entity
*/
private function generateViewsFor($entity)
{
$lowerCasePluralEntity = strtolower(str_plural($entity));
$this->finder->makeDirectory($this->getModulesPath("Resources/views/admin/{$lowerCasePluralEntity}/partials"), 0755, true);
foreach ($this->views as $stub => $view) {
$view = str_replace('$ENTITY_NAME$', $lowerCasePluralEntity, $view);
$this->writeFile(
$this->getModulesPath($view),
$this->getContentForStub($stub, $entity)
);
}
}
/**
* Generate language files for the given entity
* @param string $entity
*/
private function generateLanguageFilesFor($entity)
{
$lowerCaseEntity = str_plural(strtolower($entity));
$path = $this->getModulesPath('Resources/lang/en');
if (!$this->finder->isDirectory($path)) {
$this->finder->makeDirectory($path);
}
$this->writeFile(
$this->getModulesPath("Resources/lang/en/{$lowerCaseEntity}"),
$this->getContentForStub('lang-entity.stub', $entity)
);
}
/**
* Generate migrations file for eloquent entities
*
* @param string $entity
*/
private function generateMigrationsFor($entity)
{
usleep(250000);
$lowercasePluralEntityName = strtolower(str_plural($entity));
$lowercaseModuleName = strtolower($this->name);
$migrationName = $this->getDateTimePrefix() . "create_{$lowercaseModuleName}_{$lowercasePluralEntityName}_table";
$this->writeFile(
$this->getModulesPath("Database/Migrations/{$migrationName}"),
$this->getContentForStub('create-table-migration.stub', $entity)
);
usleep(250000);
$lowercaseEntityName = strtolower($entity);
$migrationName = $this->getDateTimePrefix() . "create_{$lowercaseModuleName}_{$lowercaseEntityName}_translations_table";
$this->writeFile(
$this->getModulesPath("Database/Migrations/{$migrationName}"),
$this->getContentForStub('create-translation-table-migration.stub', $entity)
);
}
/**
* Append the IoC bindings for the given entity to the Service Provider
*
* @param string $entity
* @throws FileNotFoundException
*/
private function appendBindingsToServiceProviderFor($entity)
{
$moduleProviderContent = $this->finder->get($this->getModulesPath("Providers/{$this->name}ServiceProvider.php"));
$binding = $this->getContentForStub('bindings.stub', $entity);
$moduleProviderContent = str_replace('// add bindings', $binding, $moduleProviderContent);
$this->finder->put($this->getModulesPath("Providers/{$this->name}ServiceProvider.php"), $moduleProviderContent);
}
/**
* Append the routes for the given entity to the routes file
*
* @param string $entity
* @throws FileNotFoundException
*/
private function appendResourceRoutesToRoutesFileFor($entity)
{
$routeContent = $this->finder->get($this->getModulesPath('Http/backendRoutes.php'));
$content = $this->getContentForStub('route-resource.stub', $entity);
$routeContent = str_replace('// append', $content, $routeContent);
$this->finder->put($this->getModulesPath('Http/backendRoutes.php'), $routeContent);
}
/**
* @param string $entity
* @throws FileNotFoundException
*/
private function appendPermissionsFor($entity)
{
$permissionsContent = $this->finder->get($this->getModulesPath('Config/permissions.php'));
$content = $this->getContentForStub('permissions-append.stub', $entity);
$permissionsContent = str_replace('// append', $content, $permissionsContent);
$this->finder->put($this->getModulesPath('Config/permissions.php'), $permissionsContent);
}
/**
* @param string $entity
*/
private function appendSidebarLinksFor($entity)
{
$sidebarComposerContent = $this->finder->get($this->getModulesPath('Sidebar/SidebarExtender.php'));
$content = $this->getContentForStub('append-sidebar-extender.stub', $entity);
$sidebarComposerContent = str_replace('// append', $content, $sidebarComposerContent);
$this->finder->put($this->getModulesPath('Sidebar/SidebarExtender.php'), $sidebarComposerContent);
}
/**
* Generate a filled sidebar view composer
* Or an empty one of no entities
* @param $entities
*/
private function generateSidebarExtender($entities)
{
if (count($entities) > 0) {
return $this->writeFile(
$this->getModulesPath('Sidebar/SidebarExtender'),
$this->getContentForStub('sidebar-extender.stub', 'abc')
);
}
return $this->writeFile(
$this->getModulesPath('Sidebar/SidebarExtender'),
$this->getContentForStub('empty-sidebar-view-composer.stub', 'abc')
);
}
/**
* Get the current time with microseconds
* @return string
*/
private function getDateTimePrefix()
{
$t = microtime(true);
$micro = sprintf("%06d", ($t - floor($t)) * 1000000);
$d = new \DateTime(date('Y-m-d H:i:s.' . $micro, $t));
return $d->format("Y_m_d_Hisu_");
}
}
<?php
namespace Modules\Workshop\Scaffold\Module\Generators;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
class FilesGenerator extends Generator
{
/**
* Generate the given files
*
* @param array $files
* @return void
*/
public function generate(array $files)
{
foreach ($files as $stub => $file) {
$this->writeFile(
$this->getModulesPath($file),
$this->getContentFor($stub)
);
}
}
/**
* Generate the base module service provider
* @return $this
*/
public function generateModuleProvider()
{
$this->writeFile(
$this->getModulesPath("Providers/{$this->name}ServiceProvider"),
$this->getContentFor('module-service-provider.stub')
);
return $this;
}
/**
* Get the content for the given file
*
* @param $stub
* @return string
* @throws FileNotFoundException
*/
private function getContentFor($stub)
{
$stub = $this->finder->get($this->getStubPath($stub));
return str_replace(
['$MODULE$', '$LOWERCASE_MODULE$', '$PLURAL_MODULE$', '$UPPERCASE_PLURAL_MODULE$'],
[$this->name, strtolower($this->name), strtolower(str_plural($this->name)), str_plural($this->name)],
$stub
);
}
}
<?php
namespace Modules\Workshop\Scaffold\Module\Generators;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Filesystem\Filesystem;
abstract class Generator
{
/**
* @var Filesystem
*/
protected $finder;
/**
* @var string Module Name
*/
protected $name;
/**
* @var string The type of entities to generate [Eloquent or Doctrine]
*/
protected $entityType;
/**
* @var Repository
*/
private $config;
public function __construct(Filesystem $finder, Repository $config)
{
$this->finder = $finder;
$this->config = $config;
}
/**
* Generate the given files
* @param array $files
* @return void
*/
abstract public function generate(array $files);
/**
* Set the module name
* @param string $moduleName
* @return $this
*/
public function forModule($moduleName)
{
$this->name = $moduleName;
return $this;
}
/**
* Set the entity type on the class
*
*@param string $entityType
* @return EntityGenerator
*/
public function type($entityType)
{
$this->entityType = $entityType;
return $this;
}
/**
* Return the current module path
* @param string $path
* @return string
*/
protected function getModulesPath($path = '')
{
return $this->config->get('modules.paths.modules') . "/{$this->name}/$path";
}
/**
* Get the path the stubs for the given filename
*
* @param $filename
* @return string
*/
protected function getStubPath($filename)
{
return __DIR__ . "/../stubs/$filename";
}
/**
* Write the given content to the given file
* @param string $path
* @param string $content
*/
protected function writeFile($path, $content)
{
$this->finder->put("$path.php", $content);
}
/**
* @param string $stub
* @param string $class
* @return string
* @throws FileNotFoundException
*/
protected function getContentForStub($stub, $class)
{
$stub = $this->finder->get($this->getStubPath($stub));
return str_replace(
[
'$MODULE_NAME$',
'$LOWERCASE_MODULE_NAME$',
'$PLURAL_LOWERCASE_MODULE_NAME$',
'$CLASS_NAME$',
'$LOWERCASE_CLASS_NAME$',
'$PLURAL_LOWERCASE_CLASS_NAME$',
'$PLURAL_CLASS_NAME$',
'$ENTITY_TYPE$',
],
[
$this->name,
strtolower($this->name),
strtolower(str_plural($this->name)),
$class,
strtolower($class),
strtolower(str_plural($class)),
str_plural($class),
$this->entityType,
],
$stub
);
}
}
<?php
namespace Modules\Workshop\Scaffold\Module\Generators;
class ValueObjectGenerator extends Generator
{
/**
* Generate the given files
*
* @param array $valueObjects
* @return void
*/
public function generate(array $valueObjects)
{
if (! $this->finder->isDirectory($this->getModulesPath('ValueObjects'))) {
$this->finder->makeDirectory($this->getModulesPath('ValueObjects'));
}
foreach ($valueObjects as $valueObject) {
$this->writeFile(
$this->getModulesPath("ValueObjects/$valueObject"),
$this->getContentForStub('value-object.stub', $valueObject)
);
}
}
}
<?php
namespace Modules\Workshop\Scaffold\Module;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Filesystem\Filesystem;
use Modules\Workshop\Scaffold\Module\Exception\ModuleExistsException;
use Modules\Workshop\Scaffold\Module\Generators\EntityGenerator;
use Modules\Workshop\Scaffold\Module\Generators\FilesGenerator;
use Modules\Workshop\Scaffold\Module\Generators\ValueObjectGenerator;
class ModuleScaffold
{
/**
* Contains the vendor name
* @var string
*/
protected $vendor;
/**
* Contains the Module name
* @var string
*/
protected $name;
/**
* Contains an array of entities to generate
* @var array
*/
protected $entities;
/**
* Contains an array of value objects to generate
* @var array
*/
protected $valueObjects;
/**
* @var array of files to generate
*/
protected $files = [
'permissions.stub' => 'Config/permissions',
'routes.stub' => 'Http/backendRoutes',
'route-provider.stub' => 'Providers/RouteServiceProvider',
];
/**
* @var string The type of entities to generate [Eloquent or Doctrine]
*/
protected $entityType;
/**
* @var Kernel
*/
private $artisan;
/**
* @var Filesystem
*/
private $finder;
/**
* @var Repository
*/
private $config;
/**
* @var EntityGenerator
*/
private $entityGenerator;
/**
* @var ValueObjectGenerator
*/
private $valueObjectGenerator;
/**
* @var FilesGenerator
*/
private $filesGenerator;
public function __construct(
Filesystem $finder,
Repository $config,
EntityGenerator $entityGenerator,
ValueObjectGenerator $valueObjectGenerator,
FilesGenerator $filesGenerator
) {
$this->artisan = app('Illuminate\Contracts\Console\Kernel');
$this->finder = $finder;
$this->config = $config;
$this->entityGenerator = $entityGenerator;
$this->valueObjectGenerator = $valueObjectGenerator;
$this->filesGenerator = $filesGenerator;
}
/**
*
*/
public function scaffold()
{
if ($this->finder->isDirectory($this->getModulesPath())) {
throw new ModuleExistsException();
}
$this->artisan->call("module:make", ['name' => [$this->name]]);
$this->addDataToComposerFile();
$this->removeUnneededFiles();
$this->addFolders();
$this->filesGenerator->forModule($this->name)
->generateModuleProvider()
->generate($this->files);
$this->cleanUpModuleJson();
$this->entityGenerator->forModule($this->getName())->type($this->entityType)->generate($this->entities);
$this->valueObjectGenerator->forModule($this->getName())->type($this->entityType)->generate($this->valueObjects);
$this->addModuleToIgnoredExceptions();
}
/**
* @param string $vendor
* @return $this
*/
public function vendor($vendor)
{
$this->vendor = $vendor;
return $this;
}
/**
* @param string $name
* @return $this
*/
public function name($name)
{
$this->name = $name;
return $this;
}
/**
* Get the name of module will created. By default in studly case.
*
* @return string
*/
public function getName()
{
return studly_case($this->name);
}
/**
* Set the entity type [Eloquent, Doctrine]
* @param string $entityType
* @return $this
*/
public function setEntityType($entityType)
{
$this->entityType = $entityType;
return $this;
}
/**
* @param array $entities
* @return $this
*/
public function withEntities(array $entities)
{
$this->entities = $entities;
return $this;
}
/**
* @param array $valueObjects
* @return $this
*/
public function withValueObjects(array $valueObjects)
{
$this->valueObjects = $valueObjects;
return $this;
}
/**
* Return the current module path
* @param string $path
* @return string
*/
private function getModulesPath($path = '')
{
return $this->config->get('modules.paths.modules') . "/{$this->getName()}/$path";
}
/**
* Rename the default vendor name 'pingpong-modules'
* by the input vendor name
*/
private function renameVendorName()
{
$composerJsonContent = $this->finder->get($this->getModulesPath('composer.json'));
$composerJsonContent = str_replace('nwidart', $this->vendor, $composerJsonContent);
$this->finder->put($this->getModulesPath('composer.json'), $composerJsonContent);
}
/**
* Remove the default generated view resources
*/
private function removeViewResources()
{
$this->finder->delete($this->getModulesPath('Resources/views/index.blade.php'));
$this->finder->delete($this->getModulesPath('Resources/views/layouts/master.blade.php'));
$this->finder->deleteDirectory($this->getModulesPath('Resources/views/layouts'));
}
/**
* Remove all unneeded files
*/
private function removeUnneededFiles()
{
$this->renameVendorName();
$this->removeViewResources();
$this->finder->delete($this->getModulesPath('Http/routes.php'));
$this->finder->delete($this->getModulesPath("Http/Controllers/{$this->name}Controller.php"));
}
/**
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
private function cleanUpModuleJson()
{
$moduleJson = $this->finder->get($this->getModulesPath('module.json'));
$moduleJson = $this->loadProviders($moduleJson);
$moduleJson = $this->setModuleOrderOrder($moduleJson);
$moduleJson = $this->removeStartPhpFile($moduleJson);
$this->finder->put($this->getModulesPath('module.json'), $moduleJson);
}
/**
* Load the routing service provider
* @param string $content
* @return string
*/
private function loadProviders($content)
{
$newProviders = <<<JSON
"Modules\\\\{$this->name}\\\Providers\\\\{$this->name}ServiceProvider",
"Modules\\\\{$this->name}\\\Providers\\\RouteServiceProvider"
JSON;
$oldProvider = '"Modules\\\\' . $this->name . '\\\\Providers\\\\' . $this->name . 'ServiceProvider"';
return str_replace($oldProvider, $newProviders, $content);
}
/**
* Set the module order to 1
* @param string $content
* @return string
*/
private function setModuleOrderOrder($content)
{
return str_replace('"order": 0,', '"order": 1,', $content);
}
/**
* Remove the start.php start file
* Also removes the auto loading of that file
* @param string $content
* @return string
*/
private function removeStartPhpFile($content)
{
$this->finder->delete($this->getModulesPath('start.php'));
return str_replace('"start.php"', '', $content);
}
/**
* Add required folders
*/
private function addFolders()
{
$this->finder->makeDirectory($this->getModulesPath('Sidebar'));
$this->finder->makeDirectory($this->getModulesPath('Repositories/Cache'));
}
/**
* Add more data in composer json
* - a asgard/module type
* - package requirements
* - minimum stability
* - prefer stable
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
private function addDataToComposerFile()
{
$composerJson = $this->finder->get($this->getModulesPath('composer.json'));
$name = ucfirst($this->name);
$search = <<<JSON
"description": "",
JSON;
$replace = <<<JSON
"description": "",
"type": "asgard-module",
"license": "MIT",
"require": {
"php": ">=5.6",
"composer/installers": "~1.0",
"asgardcms/core-module": "~2.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0",
"orchestra/testbench": "~3.2"
},
"autoload-dev": {
"psr-4": {
"Modules\\\\$name\\\\": ".",
"Modules\\\\": "Modules/"
}
},
"minimum-stability": "stable",
"prefer-stable": true,
JSON;
$composerJson = str_replace($search, $replace, $composerJson);
$this->finder->put($this->getModulesPath('composer.json'), $composerJson);
}
/**
* Adding the module name to the .gitignore file so that it can be committed
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
private function addModuleToIgnoredExceptions()
{
$modulePath = $this->config->get('modules.paths.modules');
if ($this->finder->exists($modulePath . '/.gitignore') === false) {
return;
}
$moduleGitIgnore = $this->finder->get($modulePath . '/.gitignore');
$moduleGitIgnore .= '!' . $this->getName() . PHP_EOL;
$this->finder->put($modulePath . '/.gitignore', $moduleGitIgnore);
}
}
<?php
namespace Modules\$MODULE_NAME$\Http\Controllers\Admin;
use Illuminate\Http\Request;
use Modules\$MODULE_NAME$\Entities\$CLASS_NAME$;
use Modules\$MODULE_NAME$\Repositories\$CLASS_NAME$Repository;
use Modules\Core\Http\Controllers\Admin\AdminBaseController;
class $CLASS_NAME$Controller extends AdminBaseController
{
/**
* @var $CLASS_NAME$Repository
*/
private $$LOWERCASE_CLASS_NAME$;
public function __construct($CLASS_NAME$Repository $$LOWERCASE_CLASS_NAME$)
{
parent::__construct();
$this->$LOWERCASE_CLASS_NAME$ = $$LOWERCASE_CLASS_NAME$;
}
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index()
{
//$$PLURAL_LOWERCASE_CLASS_NAME$ = $this->$LOWERCASE_CLASS_NAME$->all();
return view('$LOWERCASE_MODULE_NAME$::admin.$PLURAL_LOWERCASE_CLASS_NAME$.index', compact(''));
}
/**
* Show the form for creating a new resource.
*
* @return Response
*/
public function create()
{
return view('$LOWERCASE_MODULE_NAME$::admin.$PLURAL_LOWERCASE_CLASS_NAME$.create');
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$this->$LOWERCASE_CLASS_NAME$->create($request->all());
return redirect()->route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.index')
->withSuccess(trans('core::core.messages.resource created', ['name' => trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.$PLURAL_LOWERCASE_CLASS_NAME$')]));
}
/**
* Show the form for editing the specified resource.
*
* @param $CLASS_NAME$ $$LOWERCASE_CLASS_NAME$
* @return Response
*/
public function edit($CLASS_NAME$ $$LOWERCASE_CLASS_NAME$)
{
return view('$LOWERCASE_MODULE_NAME$::admin.$PLURAL_LOWERCASE_CLASS_NAME$.edit', compact('$LOWERCASE_CLASS_NAME$'));
}
/**
* Update the specified resource in storage.
*
* @param $CLASS_NAME$ $$LOWERCASE_CLASS_NAME$
* @param Request $request
* @return Response
*/
public function update($CLASS_NAME$ $$LOWERCASE_CLASS_NAME$, Request $request)
{
$this->$LOWERCASE_CLASS_NAME$->update($$LOWERCASE_CLASS_NAME$, $request->all());
return redirect()->route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.index')
->withSuccess(trans('core::core.messages.resource updated', ['name' => trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.$PLURAL_LOWERCASE_CLASS_NAME$')]));
}
/**
* Remove the specified resource from storage.
*
* @param $CLASS_NAME$ $$LOWERCASE_CLASS_NAME$
* @return Response
*/
public function destroy($CLASS_NAME$ $$LOWERCASE_CLASS_NAME$)
{
$this->$LOWERCASE_CLASS_NAME$->destroy($$LOWERCASE_CLASS_NAME$);
return redirect()->route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.index')
->withSuccess(trans('core::core.messages.resource deleted', ['name' => trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.$PLURAL_LOWERCASE_CLASS_NAME$')]));
}
}
$item->item(trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.$PLURAL_LOWERCASE_CLASS_NAME$'), function (Item $item) {
$item->icon('fa fa-copy');
$item->weight(0);
$item->append('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.create');
$item->route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.index');
$item->authorize(
$this->auth->hasAccess('$LOWERCASE_MODULE_NAME$.$PLURAL_LOWERCASE_CLASS_NAME$.index')
);
});
// append
$this->app->bind(
'Modules\$MODULE_NAME$\Repositories\$CLASS_NAME$Repository',
function () {
$repository = new \Modules\$MODULE_NAME$\Repositories\$ENTITY_TYPE$\$ENTITY_TYPE$$CLASS_NAME$Repository(new \Modules\$MODULE_NAME$\Entities\$CLASS_NAME$());
if (! config('app.cache')) {
return $repository;
}
return new \Modules\$MODULE_NAME$\Repositories\Cache\Cache$CLASS_NAME$Decorator($repository);
}
);
// add bindings
<?php
namespace Modules\$MODULE_NAME$\Repositories\Cache;
use Modules\$MODULE_NAME$\Repositories\$CLASS_NAME$Repository;
use Modules\Core\Repositories\Cache\BaseCacheDecorator;
class Cache$CLASS_NAME$Decorator extends BaseCacheDecorator implements $CLASS_NAME$Repository
{
public function __construct($CLASS_NAME$Repository $$LOWERCASE_CLASS_NAME$)
{
parent::__construct();
$this->entityName = '$LOWERCASE_MODULE_NAME$.$PLURAL_LOWERCASE_CLASS_NAME$';
$this->repository = $$LOWERCASE_CLASS_NAME$;
}
}
<div class="box-body">
<p>
Your fields //
</p>
</div>
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class Create$MODULE_NAME$$PLURAL_CLASS_NAME$Table extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('$LOWERCASE_MODULE_NAME$__$PLURAL_LOWERCASE_CLASS_NAME$', function(Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
// Your fields
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('$LOWERCASE_MODULE_NAME$__$PLURAL_LOWERCASE_CLASS_NAME$');
}
}
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class Create$MODULE_NAME$$CLASS_NAME$TranslationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('$LOWERCASE_MODULE_NAME$__$LOWERCASE_CLASS_NAME$_translations', function(Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
// Your translatable fields
$table->integer('$LOWERCASE_CLASS_NAME$_id')->unsigned();
$table->string('locale')->index();
$table->unique(['$LOWERCASE_CLASS_NAME$_id', 'locale']);
$table->foreign('$LOWERCASE_CLASS_NAME$_id')->references('id')->on('$LOWERCASE_MODULE_NAME$__$PLURAL_LOWERCASE_CLASS_NAME$')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('$LOWERCASE_MODULE_NAME$__$LOWERCASE_CLASS_NAME$_translations');
}
}
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.create $LOWERCASE_CLASS_NAME$') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('core::core.breadcrumb.home') }}</a></li>
<li><a href="{{ route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.index') }}">{{ trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.$PLURAL_LOWERCASE_CLASS_NAME$') }}</a></li>
<li class="active">{{ trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.create $LOWERCASE_CLASS_NAME$') }}</li>
</ol>
@stop
@section('styles')
{!! Theme::script('js/vendor/ckeditor/ckeditor.js') !!}
@stop
@section('content')
{!! Form::open(['route' => ['admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.store'], 'method' => 'post']) !!}
<div class="row">
<div class="col-md-12">
<div class="nav-tabs-custom">
@include('partials.form-tab-headers')
<div class="tab-content">
<?php $i = 0; ?>
@foreach (LaravelLocalization::getSupportedLocales() as $locale => $language)
<?php $i++; ?>
<div class="tab-pane {{ locale() == $locale ? 'active' : '' }}" id="tab_{{ $i }}">
@include('$LOWERCASE_MODULE_NAME$::admin.$PLURAL_LOWERCASE_CLASS_NAME$.partials.create-fields', ['lang' => $locale])
</div>
@endforeach
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat">{{ trans('core::core.button.create') }}</button>
<button class="btn btn-default btn-flat" name="button" type="reset">{{ trans('core::core.button.reset') }}</button>
<a class="btn btn-danger pull-right btn-flat" href="{{ route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.index')}}"><i class="fa fa-times"></i> {{ trans('core::core.button.cancel') }}</a>
</div>
</div>
</div> {{-- end nav-tabs-custom --}}
</div>
</div>
{!! Form::close() !!}
@stop
@section('footer')
<a data-toggle="modal" data-target="#keyboardShortcutsModal"><i class="fa fa-keyboard-o"></i></a> &nbsp;
@stop
@section('shortcuts')
<dl class="dl-horizontal">
<dt><code>b</code></dt>
<dd>{{ trans('core::core.back to index') }}</dd>
</dl>
@stop
@section('scripts')
<script type="text/javascript">
$( document ).ready(function() {
$(document).keypressAction({
actions: [
{ key: 'b', route: "<?= route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.index') ?>" }
]
});
});
</script>
<script>
$( document ).ready(function() {
$('input[type="checkbox"].flat-blue, input[type="radio"].flat-blue').iCheck({
checkboxClass: 'icheckbox_flat-blue',
radioClass: 'iradio_flat-blue'
});
});
</script>
@stop
<?php
namespace Modules\$MODULE_NAME$\Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="$LOWERCASE_CLASS_NAME$_translations")
*/
class $CLASS_NAME$Translation
{
/** @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*/
private $id;
/** @ORM\ManyToOne(targetEntity="Modules\$MODULE_NAME$\Entities\$CLASS_NAME$", inversedBy="translation") */
private $$LOWERCASE_CLASS_NAME$;
/** @ORM\Column(length=2) */
private $locale;
public function __construct()
{
$this->$LOWERCASE_CLASS_NAME$ = new ArrayCollection();
}
public function __get($name)
{
return $this->$name;
}
public function __set($key, $value)
{
$this->$key = $value;
}
}
<?php
namespace Modules\$MODULE_NAME$\Repositories\Doctrine;
use Modules\$MODULE_NAME$\Repositories\$CLASS_NAME$Repository;
use Modules\Core\Repositories\Doctrine\DoctrineBaseRepository;
class Doctrine$CLASS_NAME$Repository extends DoctrineBaseRepository implements $CLASS_NAME$Repository
{
}
<div class="box-body">
<p>
Your fields //
</p>
</div>
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.edit $LOWERCASE_CLASS_NAME$') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('core::core.breadcrumb.home') }}</a></li>
<li><a href="{{ route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.index') }}">{{ trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.$PLURAL_LOWERCASE_CLASS_NAME$') }}</a></li>
<li class="active">{{ trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.edit $LOWERCASE_CLASS_NAME$') }}</li>
</ol>
@stop
@section('styles')
{!! Theme::script('js/vendor/ckeditor/ckeditor.js') !!}
@stop
@section('content')
{!! Form::open(['route' => ['admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.update', $$LOWERCASE_CLASS_NAME$->id], 'method' => 'put']) !!}
<div class="row">
<div class="col-md-12">
<div class="nav-tabs-custom">
@include('partials.form-tab-headers')
<div class="tab-content">
<?php $i = 0; ?>
@foreach (LaravelLocalization::getSupportedLocales() as $locale => $language)
<?php $i++; ?>
<div class="tab-pane {{ locale() == $locale ? 'active' : '' }}" id="tab_{{ $i }}">
@include('$LOWERCASE_MODULE_NAME$::admin.$PLURAL_LOWERCASE_CLASS_NAME$.partials.edit-fields', ['lang' => $locale])
</div>
@endforeach
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat">{{ trans('core::core.button.update') }}</button>
<button class="btn btn-default btn-flat" name="button" type="reset">{{ trans('core::core.button.reset') }}</button>
<a class="btn btn-danger pull-right btn-flat" href="{{ route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.index')}}"><i class="fa fa-times"></i> {{ trans('core::core.button.cancel') }}</a>
</div>
</div>
</div> {{-- end nav-tabs-custom --}}
</div>
</div>
{!! Form::close() !!}
@stop
@section('footer')
<a data-toggle="modal" data-target="#keyboardShortcutsModal"><i class="fa fa-keyboard-o"></i></a> &nbsp;
@stop
@section('shortcuts')
<dl class="dl-horizontal">
<dt><code>b</code></dt>
<dd>{{ trans('core::core.back to index') }}</dd>
</dl>
@stop
@section('scripts')
<script type="text/javascript">
$( document ).ready(function() {
$(document).keypressAction({
actions: [
{ key: 'b', route: "<?= route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.index') ?>" }
]
});
});
</script>
<script>
$( document ).ready(function() {
$('input[type="checkbox"].flat-blue, input[type="radio"].flat-blue').iCheck({
checkboxClass: 'icheckbox_flat-blue',
radioClass: 'iradio_flat-blue'
});
});
</script>
@stop
<?php
namespace Modules\$MODULE_NAME$\Entities;
use Illuminate\Database\Eloquent\Model;
class $CLASS_NAME$Translation extends Model
{
public $timestamps = false;
protected $fillable = [];
protected $table = '$LOWERCASE_MODULE_NAME$__$LOWERCASE_CLASS_NAME$_translations';
}
<?php
namespace Modules\$MODULE_NAME$\Repositories\Eloquent;
use Modules\$MODULE_NAME$\Repositories\$CLASS_NAME$Repository;
use Modules\Core\Repositories\Eloquent\EloquentBaseRepository;
class Eloquent$CLASS_NAME$Repository extends EloquentBaseRepository implements $CLASS_NAME$Repository
{
}
<?php
namespace Modules\$MODULE_NAME$\Sidebar;
use Maatwebsite\Sidebar\Group;
use Maatwebsite\Sidebar\Item;
use Maatwebsite\Sidebar\Menu;
use Modules\User\Contracts\Authentication;
class SidebarExtender implements \Maatwebsite\Sidebar\SidebarExtender
{
/**
* @var Authentication
*/
protected $auth;
/**
* @param Authentication $auth
*
* @internal param Guard $guard
*/
public function __construct(Authentication $auth)
{
$this->auth = $auth;
}
/**
* @param Menu $menu
*
* @return Menu
*/
public function extendWith(Menu $menu)
{
return $menu;
}
}
<?php
namespace Modules\$MODULE_NAME$\Entities;
use Doctrine\ORM\Mapping AS ORM;
use Modules\Blog\Traits\Translatable;
use Mitch\LaravelDoctrine\Traits\Timestamps;
use Doctrine\Common\Collections\ArrayCollection;
/**
* @ORM\Entity
* @ORM\Table(name="$PLURAL_LOWERCASE_CLASS_NAME$")
* @ORM\HasLifecycleCallbacks()
*/
class $CLASS_NAME$
{
use Timestamps, Translatable;
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\OneToMany(targetEntity="Modules\$MODULE_NAME$\Entities\$CLASS_NAME$Translation", mappedBy="$LOWERCASE_CLASS_NAME$", cascade={"persist", "remove"})
*/
private $translation;
public function __construct()
{
// Implement stub method
}
}
<?php
namespace Modules\$MODULE_NAME$\Entities;
use Dimsav\Translatable\Translatable;
use Illuminate\Database\Eloquent\Model;
class $CLASS_NAME$ extends Model
{
use Translatable;
protected $table = '$LOWERCASE_MODULE_NAME$__$PLURAL_LOWERCASE_CLASS_NAME$';
public $translatedAttributes = [];
protected $fillable = [];
}
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.$PLURAL_LOWERCASE_CLASS_NAME$') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('core::core.breadcrumb.home') }}</a></li>
<li class="active">{{ trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.$PLURAL_LOWERCASE_CLASS_NAME$') }}</li>
</ol>
@stop
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="row">
<div class="btn-group pull-right" style="margin: 0 15px 15px 0;">
<a href="{{ route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.create') }}" class="btn btn-primary btn-flat" style="padding: 4px 10px;">
<i class="fa fa-pencil"></i> {{ trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.button.create $LOWERCASE_CLASS_NAME$') }}
</a>
</div>
</div>
<div class="box box-primary">
<div class="box-header">
</div>
<!-- /.box-header -->
<div class="box-body">
<table class="data-table table table-bordered table-hover">
<thead>
<tr>
<th>{{ trans('core::core.table.created at') }}</th>
<th data-sortable="false">{{ trans('core::core.table.actions') }}</th>
</tr>
</thead>
<tbody>
<?php if (isset($$PLURAL_LOWERCASE_CLASS_NAME$)): ?>
<?php foreach ($$PLURAL_LOWERCASE_CLASS_NAME$ as $$LOWERCASE_CLASS_NAME$): ?>
<tr>
<td>
<a href="{{ route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.edit', [$$LOWERCASE_CLASS_NAME$->id]) }}">
{{ $$LOWERCASE_CLASS_NAME$->created_at }}
</a>
</td>
<td>
<div class="btn-group">
<a href="{{ route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.edit', [$$LOWERCASE_CLASS_NAME$->id]) }}" class="btn btn-default btn-flat"><i class="fa fa-pencil"></i></a>
<button class="btn btn-danger btn-flat" data-toggle="modal" data-target="#modal-delete-confirmation" data-action-target="{{ route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.destroy', [$$LOWERCASE_CLASS_NAME$->id]) }}"><i class="fa fa-trash"></i></button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
<tfoot>
<tr>
<th>{{ trans('core::core.table.created at') }}</th>
<th>{{ trans('core::core.table.actions') }}</th>
</tr>
</tfoot>
</table>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
</div>
</div>
@include('core::partials.delete-modal')
@stop
@section('footer')
<a data-toggle="modal" data-target="#keyboardShortcutsModal"><i class="fa fa-keyboard-o"></i></a> &nbsp;
@stop
@section('shortcuts')
<dl class="dl-horizontal">
<dt><code>c</code></dt>
<dd>{{ trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.create $LOWERCASE_CLASS_NAME$') }}</dd>
</dl>
@stop
@section('scripts')
<script type="text/javascript">
$( document ).ready(function() {
$(document).keypressAction({
actions: [
{ key: 'c', route: "<?= route('admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.create') ?>" }
]
});
});
</script>
<?php $locale = locale(); ?>
<script type="text/javascript">
$(function () {
$('.data-table').dataTable({
"paginate": true,
"lengthChange": true,
"filter": true,
"sort": true,
"info": true,
"autoWidth": true,
"order": [[ 0, "desc" ]],
"language": {
"url": '<?php echo Module::asset("core:js/vendor/datatables/{$locale}.json") ?>'
}
});
});
</script>
@stop
<?php
return [
'list resource' => 'List $PLURAL_LOWERCASE_CLASS_NAME$',
'create resource' => 'Create $PLURAL_LOWERCASE_CLASS_NAME$',
'edit resource' => 'Edit $PLURAL_LOWERCASE_CLASS_NAME$',
'destroy resource' => 'Destroy $PLURAL_LOWERCASE_CLASS_NAME$',
'title' => [
'$PLURAL_LOWERCASE_CLASS_NAME$' => '$CLASS_NAME$',
'create $LOWERCASE_CLASS_NAME$' => 'Create a $LOWERCASE_CLASS_NAME$',
'edit $LOWERCASE_CLASS_NAME$' => 'Edit a $LOWERCASE_CLASS_NAME$',
],
'button' => [
'create $LOWERCASE_CLASS_NAME$' => 'Create a $LOWERCASE_CLASS_NAME$',
],
'table' => [
],
'form' => [
],
'messages' => [
],
'validation' => [
],
];
<?php
namespace Modules\$MODULE$\Providers;
use Illuminate\Support\ServiceProvider;
use Modules\Core\Traits\CanPublishConfiguration;
class $MODULE$ServiceProvider extends ServiceProvider
{
use CanPublishConfiguration;
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->registerBindings();
}
public function boot()
{
$this->publishConfig('$LOWERCASE_MODULE$', 'permissions');
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return array();
}
private function registerBindings()
{
// add bindings
}
}
'$LOWERCASE_MODULE_NAME$.$PLURAL_LOWERCASE_CLASS_NAME$' => [
'index' => '$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.list resource',
'create' => '$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.create resource',
'edit' => '$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.edit resource',
'destroy' => '$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.destroy resource',
],
// append
<?php
namespace Modules\$MODULE_NAME$\Repositories;
use Modules\Core\Repositories\BaseRepository;
interface $CLASS_NAME$Repository extends BaseRepository
{
}
<?php
namespace Modules\$MODULE$\Providers;
use Modules\Core\Providers\RoutingServiceProvider as CoreRoutingServiceProvider;
class RouteServiceProvider extends CoreRoutingServiceProvider
{
/**
* The root namespace to assume when generating URLs to actions.
* @var string
*/
protected $namespace = 'Modules\$MODULE$\Http\Controllers';
/**
* @return string
*/
protected function getFrontendRoute()
{
return false;
}
/**
* @return string
*/
protected function getBackendRoute()
{
return __DIR__ . '/../Http/backendRoutes.php';
}
/**
* @return string
*/
protected function getApiRoute()
{
return false;
}
}
$router->bind('$LOWERCASE_CLASS_NAME$', function ($id) {
return app('Modules\$MODULE_NAME$\Repositories\$CLASS_NAME$Repository')->find($id);
});
$router->get('$PLURAL_LOWERCASE_CLASS_NAME$', [
'as' => 'admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.index',
'uses' => '$CLASS_NAME$Controller@index',
'middleware' => 'can:$LOWERCASE_MODULE_NAME$.$PLURAL_LOWERCASE_CLASS_NAME$.index'
]);
$router->get('$PLURAL_LOWERCASE_CLASS_NAME$/create', [
'as' => 'admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.create',
'uses' => '$CLASS_NAME$Controller@create',
'middleware' => 'can:$LOWERCASE_MODULE_NAME$.$PLURAL_LOWERCASE_CLASS_NAME$.create'
]);
$router->post('$PLURAL_LOWERCASE_CLASS_NAME$', [
'as' => 'admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.store',
'uses' => '$CLASS_NAME$Controller@store',
'middleware' => 'can:$LOWERCASE_MODULE_NAME$.$PLURAL_LOWERCASE_CLASS_NAME$.create'
]);
$router->get('$PLURAL_LOWERCASE_CLASS_NAME$/{$LOWERCASE_CLASS_NAME$}/edit', [
'as' => 'admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.edit',
'uses' => '$CLASS_NAME$Controller@edit',
'middleware' => 'can:$LOWERCASE_MODULE_NAME$.$PLURAL_LOWERCASE_CLASS_NAME$.edit'
]);
$router->put('$PLURAL_LOWERCASE_CLASS_NAME$/{$LOWERCASE_CLASS_NAME$}', [
'as' => 'admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.update',
'uses' => '$CLASS_NAME$Controller@update',
'middleware' => 'can:$LOWERCASE_MODULE_NAME$.$PLURAL_LOWERCASE_CLASS_NAME$.edit'
]);
$router->delete('$PLURAL_LOWERCASE_CLASS_NAME$/{$LOWERCASE_CLASS_NAME$}', [
'as' => 'admin.$LOWERCASE_MODULE_NAME$.$LOWERCASE_CLASS_NAME$.destroy',
'uses' => '$CLASS_NAME$Controller@destroy',
'middleware' => 'can:$LOWERCASE_MODULE_NAME$.$PLURAL_LOWERCASE_CLASS_NAME$.destroy'
]);
// append
<?php
use Illuminate\Routing\Router;
/** @var Router $router */
$router->group(['prefix' =>'/$LOWERCASE_MODULE$'], function (Router $router) {
// append
});
<?php
namespace Modules\$MODULE_NAME$\Sidebar;
use Maatwebsite\Sidebar\Group;
use Maatwebsite\Sidebar\Item;
use Maatwebsite\Sidebar\Menu;
use Modules\User\Contracts\Authentication;
class SidebarExtender implements \Maatwebsite\Sidebar\SidebarExtender
{
/**
* @var Authentication
*/
protected $auth;
/**
* @param Authentication $auth
*
* @internal param Guard $guard
*/
public function __construct(Authentication $auth)
{
$this->auth = $auth;
}
/**
* @param Menu $menu
*
* @return Menu
*/
public function extendWith(Menu $menu)
{
$menu->group(trans('core::sidebar.content'), function (Group $group) {
$group->item(trans('$LOWERCASE_MODULE_NAME$::$PLURAL_LOWERCASE_CLASS_NAME$.title.$PLURAL_LOWERCASE_CLASS_NAME$'), function (Item $item) {
$item->icon('fa fa-copy');
$item->weight(10);
$item->authorize(
/* append */
);
// append
});
});
return $menu;
}
}
<?php
namespace Modules\$MODULE_NAME$\ValueObjects;
use Doctrine\ORM\Mapping as ORM;
/** @ORM\Embeddable */
class $CLASS_NAME$
{
public function __construct()
{
// Implement stub method
}
public function __toString()
{
return ''; // Implement stub method
}
}
<?php
namespace Modules\Workshop\Scaffold\Theme\Exceptions;
class FileTypeNotFoundException extends \Exception
{
}
<?php
namespace Modules\Workshop\Scaffold\Theme\Exceptions;
class ThemeExistsException extends \Exception
{
}
<?php
namespace Modules\Workshop\Scaffold\Theme\FileTypes;
use Modules\Workshop\Scaffold\Theme\Traits\FindsThemePath;
class AssetsFolder extends BaseFileType implements FileType
{
use FindsThemePath;
/**
* Generate the current file type
* @return string
*/
public function generate()
{
$stub = $this->finder->get(__DIR__ . '/../stubs/gitignore.stub');
$this->finder->makeDirectory($this->themePathForFile($this->options['name'], '/assets/css'), 0755, true);
$this->finder->makeDirectory($this->themePathForFile($this->options['name'], '/assets/js'), 0755, true);
$this->finder->makeDirectory($this->themePathForFile($this->options['name'], '/assets/images'), 0755, true);
$this->finder->put($this->themePathForFile($this->options['name'], '/assets/.gitignore'), $stub);
$this->finder->put($this->themePathForFile($this->options['name'], '/assets/css/.gitignore'), $stub);
$this->finder->put($this->themePathForFile($this->options['name'], '/assets/js/.gitignore'), $stub);
$this->finder->put($this->themePathForFile($this->options['name'], '/assets/images/.gitignore'), $stub);
}
}
<?php
namespace Modules\Workshop\Scaffold\Theme\FileTypes;
abstract class BaseFileType
{
/**
* @var \Illuminate\Filesystem\Filesystem
*/
protected $finder;
/**
* @var array
*/
protected $options;
public function __construct(array $options)
{
$this->finder = app('Illuminate\Filesystem\Filesystem');
$this->options = $options;
}
}
<?php
namespace Modules\Workshop\Scaffold\Theme\FileTypes;
use Modules\Workshop\Scaffold\Theme\Traits\FindsThemePath;
class BasicView extends BaseFileType implements FileType
{
use FindsThemePath;
/**
* Generate the current file type
* @return string
*/
public function generate()
{
$stub = $this->finder->get(__DIR__ . '/../stubs/index.blade.stub');
$this->finder->put($this->themePathForFile($this->options['name'], '/views/default.blade.php'), $stub);
}
}
<?php
namespace Modules\Workshop\Scaffold\Theme\FileTypes;
use Modules\Workshop\Scaffold\Theme\Traits\FindsThemePath;
class ComposerJson extends BaseFileType implements FileType
{
use FindsThemePath;
/**
* Generate the current file type
* @return string
*/
public function generate()
{
$stub = $this->finder->get(__DIR__ . '/../stubs/composerJson.stub');
$stub = $this->replaceContentInStub($stub);
$this->finder->put($this->themePathForFile($this->options['name'], 'composer.json'), $stub);
}
public function replaceContentInStub($stub)
{
return str_replace(
[
'{{theme-name}}',
'{{vendor}}',
],
[
$this->options['name'],
$this->options['vendor'],
],
$stub
);
}
}
<?php
namespace Modules\Workshop\Scaffold\Theme\FileTypes;
interface FileType
{
/**
* Generate the current file type
* @return string
*/
public function generate();
}
<?php
namespace Modules\Workshop\Scaffold\Theme\FileTypes;
use Modules\Workshop\Scaffold\Theme\Traits\FindsThemePath;
class MasterBladeLayout extends BaseFileType implements FileType
{
use FindsThemePath;
/**
* Generate the current file type
* @return string
*/
public function generate()
{
$stub = $this->finder->get(__DIR__ . '/../stubs/masterBladeLayout.stub');
$this->finder->makeDirectory($this->themePathForFile($this->options['name'], '/views/layouts'), 0755, true);
$this->finder->put($this->themePathForFile($this->options['name'], '/views/layouts/master.blade.php'), $stub);
}
}
<?php
namespace Modules\Workshop\Scaffold\Theme\FileTypes;
use Modules\Workshop\Scaffold\Theme\Traits\FindsThemePath;
class ResourcesFolder extends BaseFileType implements FileType
{
use FindsThemePath;
/**
* Generate the current file type
* @return string
*/
public function generate()
{
$stub = $this->finder->get(__DIR__ . '/../stubs/gitignore.stub');
$this->finder->makeDirectory($this->themePathForFile($this->options['name'], '/resources/css'), 0755, true);
$this->finder->makeDirectory($this->themePathForFile($this->options['name'], '/resources/js'), 0755, true);
$this->finder->makeDirectory($this->themePathForFile($this->options['name'], '/resources/images'), 0755, true);
$this->finder->put($this->themePathForFile($this->options['name'], '/resources/.gitignore'), $stub);
$this->finder->put($this->themePathForFile($this->options['name'], '/resources/css/.gitignore'), $stub);
$this->finder->put($this->themePathForFile($this->options['name'], '/resources/js/.gitignore'), $stub);
$this->finder->put($this->themePathForFile($this->options['name'], '/resources/images/.gitignore'), $stub);
}
}
<?php
namespace Modules\Workshop\Scaffold\Theme\FileTypes;
use Modules\Workshop\Scaffold\Theme\Traits\FindsThemePath;
class ThemeJson extends BaseFileType implements FileType
{
use FindsThemePath;
/**
* Generate the current file type
* @return string
*/
public function generate()
{
$stub = $this->finder->get(__DIR__ . '/../stubs/themeJson.stub');
$stub = $this->replaceContentInStub($stub);
$this->finder->put($this->themePathForFile($this->options['name'], 'theme.json'), $stub);
}
public function replaceContentInStub($stub)
{
return str_replace(
[
'{{theme-name}}',
'{{type}}',
],
[
$this->options['name'],
$this->options['type'],
],
$stub
);
}
}
<?php
namespace Modules\Workshop\Scaffold\Theme;
use Modules\Workshop\Scaffold\Theme\Exceptions\FileTypeNotFoundException;
use Modules\Workshop\Scaffold\Theme\FileTypes\FileType;
class ThemeGeneratorFactory
{
/**
* @param string $file
* @param array $options
* @return FileType
* @throws FileTypeNotFoundException
*/
public function make($file, array $options)
{
$class = 'Modules\Workshop\Scaffold\Theme\FileTypes\\' . ucfirst($file);
if (! class_exists($class)) {
throw new FileTypeNotFoundException();
}
return new $class($options);
}
}
<?php
namespace Modules\Workshop\Scaffold\Theme;
use Illuminate\Filesystem\Filesystem;
use Modules\Workshop\Scaffold\Theme\Exceptions\ThemeExistsException;
use Modules\Workshop\Scaffold\Theme\Traits\FindsThemePath;
class ThemeScaffold
{
use FindsThemePath;
/**
* @var array
*/
protected $files = [
'themeJson',
'composerJson',
'masterBladeLayout',
'basicView',
'resourcesFolder',
'assetsFolder',
];
/**
* Options array containing:
* - name
* - type
* @var array
*/
protected $options;
/**
* @var ThemeGeneratorFactory
*/
private $themeGeneratorFactory;
/**
* @var \Illuminate\Filesystem\Filesystem
*/
private $finder;
public function __construct(ThemeGeneratorFactory $themeGeneratorFactory, Filesystem $finder)
{
$this->themeGeneratorFactory = $themeGeneratorFactory;
$this->finder = $finder;
}
/**
* @throws Exceptions\FileTypeNotFoundException
* @throws ThemeExistsException
*/
public function generate()
{
if ($this->finder->isDirectory($this->themePath($this->options['name']))) {
throw new ThemeExistsException("The theme [{$this->options['name']}] already exists");
}
$this->finder->makeDirectory($this->themePath($this->options['name']));
foreach ($this->files as $file) {
$this->themeGeneratorFactory->make($file, $this->options)->generate();
}
$this->addThemeToIgnoredExceptions();
}
/**
* @param $name
* @return $this
*/
public function setName($name)
{
if (empty($name)) {
throw new \InvalidArgumentException('You must provide a name');
}
$this->options['name'] = $name;
return $this;
}
/**
* @param string $type
* @return $this
*/
public function forType($type)
{
if (empty($type)) {
throw new \InvalidArgumentException('You must provide a type');
}
$this->options['type'] = $type;
return $this;
}
public function setVendor($vendor)
{
if (empty($vendor)) {
throw new \InvalidArgumentException('You must provide a vendor name');
}
$this->options['vendor'] = $vendor;
return $this;
}
/**
* Set the files array on the class
* @param array $files
*/
public function setFiles(array $files)
{
$this->files = $files;
}
/**
* Adding the theme name to the .gitignore file so that it can be committed
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
private function addThemeToIgnoredExceptions()
{
$themePath = config('asgard.core.core.themes_path');
if ($this->finder->exists($themePath . '/.gitignore') === false) {
return;
}
$moduleGitIgnore = $this->finder->get($themePath . '/.gitignore');
$moduleGitIgnore .= '!' . $this->options['name'] . PHP_EOL;
$this->finder->put($themePath . '/.gitignore', $moduleGitIgnore);
}
}
<?php
namespace Modules\Workshop\Scaffold\Theme\Traits;
trait FindsThemePath
{
/**
* Get the theme location path
* @param string $name
* @return string
*/
public function themePath($name = '')
{
return config('asgard.core.core.themes_path') . "/$name";
}
/**
* Get the theme location path
* @param string $name
* @param string $file
* @return string
*/
public function themePathForFile($name, $file)
{
return config('asgard.core.core.themes_path') . "/$name/$file";
}
}
{
"name": "{{vendor}}/{{theme-name}}-theme",
"type": "asgard-theme",
"description": "",
"keywords": [
"asgard-cms"
],
"license": "MIT",
"require": {
"php": ">=5.5",
"composer/installers": "~1.0"
},
"scripts": {
"post-install-cmd": [
"php artisan stylist:publish"
],
"post-update-cmd": [
"php artisan stylist:publish"
]
},
"minimum-stability": "dev"
}
@extends('layouts.master')
@section('content')
<h1>Hello World!</h1>
@stop
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
@yield('content')
</body>
</html>
{
"name": "{{theme-name}}",
"description": "",
"type": "{{type}}"
}
<?php
namespace Modules\Workshop\Sidebar;
use Maatwebsite\Sidebar\Group;
use Maatwebsite\Sidebar\Item;
use Maatwebsite\Sidebar\Menu;
use Modules\User\Contracts\Authentication;
class SidebarExtender implements \Maatwebsite\Sidebar\SidebarExtender
{
/**
* @var Authentication
*/
protected $auth;
/**
* @param Authentication $auth
*
* @internal param Guard $guard
*/
public function __construct(Authentication $auth)
{
$this->auth = $auth;
}
/**
* @param Menu $menu
*
* @return Menu
*/
public function extendWith(Menu $menu)
{
$menu->group(trans('workshop::workshop.title'), function (Group $group) {
$group->weight(100);
$group->item(trans('workshop::workshop.modules'), function (Item $item) {
$item->icon('fa fa-cogs');
$item->weight(100);
$item->route('admin.workshop.modules.index');
$item->authorize(
$this->auth->hasAccess('workshop.modules.index')
);
});
$group->item(trans('workshop::workshop.themes'), function (Item $item) {
$item->icon('fa fa-cogs');
$item->weight(101);
$item->route('admin.workshop.themes.index');
$item->authorize(
$this->auth->hasAccess('workshop.themes.index')
);
});
$group->authorize(
$this->auth->hasAccess('workshop.*') or $this->auth->hasAccess('user.*') or $this->auth->hasAccess('setting.*')
);
});
return $menu;
}
}
<?php
namespace Modules\Workshop\Tests;
use Collective\Html\FormFacade;
use Collective\Html\HtmlFacade;
use Collective\Html\HtmlServiceProvider;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
use Mcamara\LaravelLocalization\LaravelLocalizationServiceProvider;
use Modules\Workshop\Providers\WorkshopServiceProvider;
use Nwidart\Modules\LaravelModulesServiceProvider;
use Orchestra\Testbench\TestCase;
abstract class BaseTestCase extends TestCase
{
public function setUp()
{
parent::setUp();
}
protected function getPackageProviders($app)
{
return [
LaravelLocalizationServiceProvider::class,
WorkshopServiceProvider::class,
LaravelModulesServiceProvider::class,
HtmlServiceProvider::class,
];
}
protected function getPackageAliases($app)
{
return [
'LaravelLocalization' => LaravelLocalization::class,
'Form' => FormFacade::class,
'Html' => HtmlFacade::class,
];
}
protected function getEnvironmentSetUp($app)
{
$app['config']->set('modules.namespace', 'Modules');
$app['config']->set('app.locale', 'en');
$app['config']->set('modules.paths.generator', [
'assets' => 'Assets',
'config' => 'Config',
'command' => 'Console',
'migration' => 'Database/Migrations',
'model' => 'Entities',
'repository' => 'Repositories',
'seeder' => 'Database/Seeders',
'controller' => 'Http/Controllers',
'filter' => 'Http/Middleware',
'request' => 'Http/Requests',
'provider' => 'Providers',
'lang' => 'Resources/lang',
'views' => 'Resources/views',
'test' => 'Tests',
]);
$app['config']->set('laravellocalization.supportedLocales', [
'en' => ['name' => 'English', 'script' => 'Latn', 'native' => 'English'],
'fr' => ['name' => 'French', 'script' => 'Latn', 'native' => 'français'],
]);
$app['config']->set('modules.paths.modules', realpath(__DIR__ . '/../Modules'));
$app['config']->set('stylist.themes.paths', [base_path('Themes')]);
$app['config']->set('asgard.core.core.themes_path', base_path('Themes'));
}
}
This diff is collapsed.
This diff is collapsed.
url: https://github.com/AsgardCms/Workshop
versions:
"2.0@unreleased":
added:
- Laravel 5.2 compatibility
- Ability to add unreleased items in module changelog, by suffixing version with <code>@unreleased</code>
- Protecting api routes with api admin middleware
changed:
- Using new more flexible way of handle permissions via middleware
- Module names can have a dash in them, for instance <code>module-name</code>
- Adding the scaffolded module name to the .gitignore file so that it can be committed
- Adding the scaffolded theme name to .gitignore file so that it can be committed
removed:
- Remove laracasts/flash dependency from controller stub
"1.16.0":
changed:
- Use <code>$router</code> variable in routes file
- Use <code>$router</code> variable in routes stub file for module scaffolding
"1.15.1":
changed:
- Use singular class name for model bindings in routes scaffolding
"1.15.0":
added:
- Adding symfony/yaml dependency
changed:
- Use new middleware for permissions on module resource route scaffold stub
"1.14.0":
changed:
- Remove unneeded icheck toggles for hidden input on view stubs
- Removed 'More Coming Soon' message
- Removed the inclusing of old iCheck styles
"1.13.1":
changed:
- Renaming the basic generated view to default
"1.13.0":
added:
- Reset button on the create and edit view stubs
"1.12.0":
changed:
- Removed language files, they are now in the translation module
"1.11.0":
changed:
- Using the new index template in scaffold
- Using manual routes over the resource helper in scaffold
"1.10.0":
changed:
- Removed Menu module as a dependency
"1.9.0":
added:
- Added Russian translations
changed:
- Fixing case sensitive issue
"1.8.0":
added:
- Chinese Simplified language
"1.7.0":
added:
- Dutch and Portuguese language
"1.6.1":
changed:
- Update eloquent-entity-translation.stub, fixing the translations table name in model
"1.6.0":
changed:
- Using the core messages rather than generating for every module
"1.5.0":
changed:
- Added sidebar title as translatable text in generated modules
- Remove facade usage in generated modules
"1.4.2":
changed:
- Use the <code>anticipate()</code> method instead of <code>choice()</code>
"1.4.1":
changed:
- Hardcoded the entity type to Eloquent. Due to broken Question class from Symfony
"1.4.0":
added:
- Theme can now be managed from UI
- Theme assets can be be published from UI
"1.3.0":
changed:
- Set module order to 1 in <code>module.json</code>
- Refactor to only read & write <code>module.json</code> once
"1.2.1":
changed:
- Return empty menu instance on generation of module with no entities
"1.2.0":
changed:
- Remove the v from the version column to sort properly
"1.1.0":
changed:
- Generated migrations now have the table engine specified
"1.0.11":
changed:
- Fixed case sensitive issue
"1.0.10":
added:
- Adding a theme scaffold command, you can use it with <code>php artisan asgard:theme:scaffold</code>
changed:
- Added protection on the module scaffold command for the vendor/name structure
"1.0.9":
changed:
- Use the new Laravel-Sidebar api on the generated modules
"1.0.8":
added:
- Using new sidebar extender class
changed:
- Updating the module generator to generate a SidebarExtender class
removed:
- Old SidebarViewComposer
"1.0.7":
changed:
- Use cleaner version getter in module show view
- Check if changelog isn't an empty array on the module show view
"1.0.6":
changed:
- Fixing alignements in composer.json file
- Fixing alignements in module.json file
- Module scaffold now requires stable version of core module
- Module scaffold now requires version 3.1 of orchestra/testbench
"1.0.5":
changed:
- Add the module version in a separate column so that it can be ordered & filtered
- Use the <code>route</code> function helpers
"1.0.4":
changed:
- No escaping on the changelog lines to display code blocks
"1.0.3":
changed:
- Limit the amount of versions displayed to 5 last versions
"1.0.2":
changed:
- Worked around the empty value not being alowed in Laravel 5.1
- Fixing the bug of migration files having the same timestamp
- Cleaning up the questions display in CLI based on new Command class in Laravel 5.1
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment