diff --git a/app/Classes/Repositories/PageRepository.php b/app/Classes/Repositories/PageRepository.php index eeb4aa48..ff419984 100644 --- a/app/Classes/Repositories/PageRepository.php +++ b/app/Classes/Repositories/PageRepository.php @@ -10,8 +10,8 @@ use App\Model\Page; use Illuminate\Support\Collection; -use App\Plugins\Pages\Model\PageTypes; -use App\Plugins\Pages\Model\PageOptions; +use App\Modules\Pages\Model\PageTypes; +use App\Modules\Pages\Model\PageOptions; use Illuminate\Database\Eloquent\Builder; /** @@ -53,9 +53,9 @@ public function allNormalPages() * * @return $collection */ - public function allPluginPages() + public function allModulePages() { - $bitmask = PageTypes::TYPE_PLUGIN; + $bitmask = PageTypes::TYPE_MODULE; return $this->model->whereRaw('`type` & '.$bitmask.'='.$bitmask)->get(); } diff --git a/app/Console/Commands/JuneUpdateOne.php b/app/Console/Commands/JuneUpdateOne.php index a90dfd4a..2d7760fa 100644 --- a/app/Console/Commands/JuneUpdateOne.php +++ b/app/Console/Commands/JuneUpdateOne.php @@ -4,8 +4,8 @@ use App\Model\Page; use Illuminate\Console\Command; -use App\Plugins\Pages\Model\PageTypes; -use App\Plugins\Pages\Model\PageOptions; +use App\Modules\Pages\Model\PageTypes; +use App\Modules\Pages\Model\PageOptions; use App\Classes\Repositories\PageRepository; class JuneUpdateOne extends Command @@ -59,9 +59,6 @@ public function handle() if (! $page->editable && ! $page->special) { $page->type = (PageTypes::TYPE_FRAMEWORK | PageTypes::TYPE_STANDARD); $page->option = PageOptions::OPTION_PUBLIC | PageOptions::OPTION_SITEMAP; - } elseif ($page->plugin) { - $page->type = PageTypes::TYPE_PLUGIN; - $page->option = PageOptions::OPTION_DEFAULT; } elseif ($page->identifier == 'newsletter.success') { $page->type = PageTypes::TYPE_PLUGIN; $page->option = PageOptions::OPTION_PUBLIC; diff --git a/app/Helpers/Global.php b/app/Helpers/Global.php index ee26b3e7..900a1c0f 100644 --- a/app/Helpers/Global.php +++ b/app/Helpers/Global.php @@ -149,3 +149,11 @@ function formSelect($value, $matches) { return $value == $matches ? 'selected' : ''; } + + /** + * @return \App\Modules\ModuleManager + */ + function modules() + { + return app(\App\Modules\ModuleManager::class); + } diff --git a/app/Model/Article.php b/app/Model/Article.php index 4c103096..7a09bc1a 100644 --- a/app/Model/Article.php +++ b/app/Model/Article.php @@ -218,7 +218,7 @@ public function getPageAttribute() */ public function path() { - return url($this->page->path().'/'.$this->category->slug.'/'.$this->slug); + return url(config('modules.articles.route').'/'.$this->category->slug.'/'.$this->slug); } /** diff --git a/app/Model/Page.php b/app/Model/Page.php index 253e5512..f6b28053 100644 --- a/app/Model/Page.php +++ b/app/Model/Page.php @@ -6,10 +6,11 @@ use Laravel\Scout\Searchable; use App\Model\Concerns\Publishers; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\DB; use App\Classes\Interfaces\Linkable; use App\Model\Concerns\ActivityFeed; -use App\Plugins\Pages\Model\PageTypes; -use App\Plugins\Pages\Model\PageOptions; +use App\Modules\Pages\Model\PageTypes; +use App\Modules\Pages\Model\PageOptions; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -31,6 +32,7 @@ * @property string $heading * @property string $description * @property string $keywords + * @property string $module * @property int $views * @property int $type * @property int $option @@ -266,7 +268,7 @@ public function getKeywordsAttribute() /** * Return the status if the page has the options specified. * - * @param PageOptions $options Values of the required options + * @param string $options Values of the required options * * @return bool the returned condition */ @@ -278,7 +280,7 @@ public function hasOption($options) $constant = constant(sprintf('%s::OPTION_%s', PageOptions::class, strtoupper($option))); if ($this->option & $constant) { - return $constant; + return true; } } @@ -288,7 +290,7 @@ public function hasOption($options) /** * Return if the current page type matches the condition giving. * - * @param PageTypes $type The type required for the condition + * @param string $type The type required for the condition * * @return bool The condition of the function. */ @@ -298,4 +300,29 @@ public function isType(string $type) return $this->type & $constant; } + + /** + * Toggle the disability of all the module pages. + * (Enable, Disable);. + * + * If one page fails, do not toggle. + * + * @param string $module + * + * @return mixed + */ + public static function toggleModuleDisability(string $module, bool $active) + { + DB::transaction(function () use ($module, $active) { + if ($active == true) { + foreach (self::whereModule($module)->get() as $page) { + $page->update(['option' => $page->option & ~PageOptions::OPTION_DISABLED]); + } + } else { + foreach (self::whereModule($module)->get() as $page) { + $page->update(['option' => $page->option | PageOptions::OPTION_DISABLED]); + } + } + }); + } } diff --git a/app/Model/Plugin.php b/app/Model/Plugin.php deleted file mode 100644 index 172a40e5..00000000 --- a/app/Model/Plugin.php +++ /dev/null @@ -1,299 +0,0 @@ - 'boolean', 'required' => 'boolean']; - - /** - * Undocumented function. - * - * @return void - */ - public function getRouteKeyName() - { - return 'name'; - } - - /** - * Toggle the enabled status of the plugin. - * - * @return bool - */ - public function toggle() - { - return $this->update(['enabled' => ! $this->enabled]); - } - - /** - * Return the namespace path to the controller of the plugin. - * - * @return string - */ - public function getControllerAttribute() - { - return app()->make(sprintf("App\Plugins\%s\%sController", $this->name, $this->name)); - } - - /** - * Call the install method on the plugins controller. - * - * @param string $plugin_name The plugin name to install - * @return Plugin The model instance of the installed plugin - */ - public static function install(string $plugin_name) - { - $plugin = self::whereName($plugin_name)->first(); - - $plugin->update(['enabled' => true]); - - $plugin->controller->install(); - - return $plugin; - } - - /** - * @deprecated - * @return PluginHandler - */ - protected function getHandlerAttribute() - { - return app(sprintf("%s\%sController", $this->dirNamespace(), ucfirst($this->name))); - } - - /** - * ==========================================================. - * - * GET THE ATTRIBUTES OF THE MODEL - * - * ========================================================== - */ - public function icon() - { - return $this->controller->icon(); - } - - public function name() - { - return $this->name; - } - - public function version() - { - return $this->controller->version(); - } - - public function isEnabled() - { - return $this->getAttribute('enabled') == true; - } - - public function isDisabled() - { - return $this->getAttribute('enabled') == false; - } - - public function isFrontEnd() - { - return $this->getAttribute('is_frontend'); - } - - public function isBackEnd() - { - return $this->getAttribute('is_backend'); - } - - public function getCreatedAt() - { - return $this->getAttribute('created_at'); - } - - public function getUpdatedAt() - { - return $this->getAttribute('updated_at'); - } - - public function setEnabled($boolean) - { - if ($boolean == true) { - return $this->enable(); - } - - return $this->disable(); - } - - /** - * Enable the product. - * - * @return $this - */ - public function enable() - { - $this->setAttribute('enabled', true); - - return $this; - } - - /** - * Disable the product. - * - * @return $this - */ - public function disable() - { - $this->setAttribute('enabled', false); - - return $this; - } - - public function setInstalled(bool $boolean) - { - $this->setAttribute('installed', $boolean ? 1 : 0); - - return $this; - } - - /** - * @return mixed - */ - public function isInstalled() - { - return $this->getAttribute('installed') ? true : false; - } - - public function setName($string) - { - $this->setAttribute('name', $string); - - return $this; - } - - public function setVersion($integer) - { - $this->setAttribute('version', $integer); - - return $this; - } - - public function setIcon($string) - { - $this->setAttribute('icon', $string); - - return $this; - } - - public function adminUrl() - { - if ($this->isBackEnd()) { - return route("admin.{$this->name}.index"); - } - - throw new \Exception('This is not a backend enabled plugin and should not be used here.'); - } - - public function userUrl() - { - if ($this->isFrontEnd()) { - return url($this->name()); - } - - throw new \Exception('This is not a frontend enabled plugin and should not be used here.'); - } - - public function isHidden() - { - return $this->getAttribute('hidden') ? true : false; - } - - public function setHide(bool $boolean) - { - $this->setAttribute('hidden', $boolean ? 1 : 0); - - return $this; - } - - public function setRequired($boolean) - { - $this->setAttribute('required', $boolean); - - return $this; - } - - public function setFrontEnd($boolean) - { - $this->setAttribute('is_frontend', $boolean); - - return $this; - } - - public function setBackEnd($boolean) - { - $this->setAttribute('is_backend', $boolean); - - return $this; - } - - /** - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function options() - { - return $this->hasMany(PluginOption::class, 'plugin_id', 'id'); - } - - public function option($key) - { - return $this->options->where('key', $key)->first()->value(); - } - - public function feeds() - { - return $this->hasMany(PluginFeed::class, 'plugin_id', 'id'); - } -} diff --git a/app/Plugins/Articles/BackendController.php b/app/Modules/Articles/BackendController.php similarity index 96% rename from app/Plugins/Articles/BackendController.php rename to app/Modules/Articles/BackendController.php index e442d49f..b894085d 100644 --- a/app/Plugins/Articles/BackendController.php +++ b/app/Modules/Articles/BackendController.php @@ -1,25 +1,19 @@ uses('BackendController@categories')->name('admin.articles.categories.index'); Route::post('admin/articles/categories')->uses('BackendController@categories_store')->name('admin.articles.categories.store'); - Route::resource('admin/articles', 'BackendController', ['as' => 'admin']); diff --git a/app/Modules/Articles/Routes/frontend.php b/app/Modules/Articles/Routes/frontend.php new file mode 100644 index 00000000..738bb112 --- /dev/null +++ b/app/Modules/Articles/Routes/frontend.php @@ -0,0 +1,26 @@ +uses('FrontendController@allArticles')->name('articles.all'); + Route::get(config('modules.articles.route').'/search')->uses('FrontendController@searchArticles')->name('search.articles'); + Route::get(config('modules.articles.route').'/{category}')->uses('FrontendController@categoryArticles')->name('category.articles'); + Route::get(config('modules.articles.route').'/creator/{account}')->uses('FrontendController@allCreatorsArticles')->name('creator.articles'); + Route::get(config('modules.articles.route').'/{category}/{article}')->uses('FrontendController@viewArticle')->name('article.view'); diff --git a/app/Modules/Configs/Blade/index.blade.php b/app/Modules/Configs/Blade/index.blade.php new file mode 100644 index 00000000..32ce310d --- /dev/null +++ b/app/Modules/Configs/Blade/index.blade.php @@ -0,0 +1,192 @@ +@extends('dashboard::frame') + +@section('title') + Settings +@endsection + +@section('information') + Your application can be modified with straight forward, powerful options that you can change to match your needs. +@endsection + +@section('content') + + + + + +
+ + {{ csrf_field() }} + +
+ +
+
+
+

Application Settings

+

Modify the settings of your CMS Application..

+
+
+ +
+
+ +
+
+ + + {{ App\Model\Configuration::describe('app.name') }} +
+
+ + + {{ App\Model\Configuration::describe('app.mode') }} +
+
+ +
+
+ + + + +
+
+ {{ App\Model\Configuration::describe('app.logo') }} +
+
+ +
+ +
+ +
+
+
+

HTML Tag Settings

+

Control the settings that make up the html tag of your website.

+
+
+ +
+
+ +
+
+ + + {{ App\Model\Configuration::describe('website.tag.title.text') }} +
+ +
+ + + {{ App\Model\Configuration::describe('website.tag.keywords.default') }} +
+ +
+ + + {{ App\Model\Configuration::describe('website.tag.description.default') }} +
+ +
+ + + {{ App\Model\Configuration::describe('website.tag.title.position') }} +
+ +
+ + + {{ App\Model\Configuration::describe('website.tag.title.separator') }} +
+
+
+ +
+ +
+
+
+

Website Contact Details

+

Define the details on your website that allows visitors to get in touch.

+
+
+ +
+
+ +
+
+ + + {{ App\Model\Configuration::describe('website.contact.address') }} +
+ +
+ + + {{ App\Model\Configuration::describe('website.contact.phone') }} +
+ +
+ + + {{ App\Model\Configuration::describe('website.contact.fax') }} +
+ +
+ + + {{ App\Model\Configuration::describe('website.contact.email') }} +
+
+
+ +
+ +
+
+
+

Third Party Services

+

Integrate interaction from third party services such as google, facebook etc.

+
+
+ +
+
+ +
+
+ + + {{ App\Model\Configuration::describe('website.webmaster.google.tracking') }} +
+
+
+ +
+ +
+ + + +
+ + Cancel +
+
+ +
+@endsection diff --git a/app/Modules/Configs/Controller.php b/app/Modules/Configs/Controller.php new file mode 100644 index 00000000..c8645f60 --- /dev/null +++ b/app/Modules/Configs/Controller.php @@ -0,0 +1,55 @@ +middleware(['role:administrator']); + } + + /** + * Settings are already loaded within the application + * best to use the loaded settings from that instead. + * + * Helper function (Settings()) + * + * @return mixed + */ + public function index() + { + return $this->make('index'); + } + + /** + * Save the changes for settings. + * + * @param Request $request + * @return \Illuminate\Http\RedirectResponse + */ + public function update(Request $request) + { + foreach ($request['setting'] as $key => $value) { + Configuration::set($key, $value); + } + + return redirect()->intended(route('admin.settings.index')); + } +} diff --git a/app/Plugins/Products/Routes/backend.php b/app/Modules/Configs/Routes/backend.php similarity index 59% rename from app/Plugins/Products/Routes/backend.php rename to app/Modules/Configs/Routes/backend.php index e95d6a45..33979e66 100644 --- a/app/Plugins/Products/Routes/backend.php +++ b/app/Modules/Configs/Routes/backend.php @@ -15,10 +15,8 @@ // Get Requests. // ================================================================================== - Route::get('/admin/products/index', 'BackendController@index')->name('admin.products.index'); + Route::get('/admin/settings')->uses('Controller@index')->name('admin.settings.index'); - /* - * Login for plugins. - */ - Route::get('/admin/products/install/{plugin}')->uses('BackendController@install')->name('products.install'); - Route::get('/admin/products/uninstall/{plugin}')->uses('BackendController@uninstall')->name('products.uninstall'); + // Post Requests. + // ================================================================================== + Route::post('/admin/settings/update')->uses('Controller@update')->name('admin.settings.update'); diff --git a/app/Plugins/Menus/Routes/frontend.php b/app/Modules/Configs/Routes/frontend.php similarity index 100% rename from app/Plugins/Menus/Routes/frontend.php rename to app/Modules/Configs/Routes/frontend.php diff --git a/app/Modules/ModuleManager.php b/app/Modules/ModuleManager.php new file mode 100644 index 00000000..dfd66cbb --- /dev/null +++ b/app/Modules/ModuleManager.php @@ -0,0 +1,132 @@ +repository = $repository; + } + + /** + * Enable a module using the modules.php configuration. + * + * @param string $module + * + * @return void + * + * @throws ModuleNotFoundException + */ + public function enable(string $module) + { + $this->repository->set("{$module}.enabled", true)->save(); + + $this->updatePageBelongingTo($module, $this->repository->get("{$module}.enabled")); + } + + /** + * @param string $module + * @return void + * @throws ModuleNotFoundException + */ + public function disable(string $module) + { + $this->repository->set("{$module}.enabled", false)->save(); + + $this->updatePageBelongingTo($module, $this->repository->get("{$module}.enabled")); + } + + /** + * Toggle the status of a module in the configuration. + * + * @param string $module + * + * @return $this + * @throws \App\Modules\ModuleNotFoundException + */ + public function toggle(string $module) + { + $this->repository->set("{$module}.enabled", ! $this->repository->get("{$module}.enabled"))->save(); + + $this->updatePageBelongingTo($module, $this->repository->get("{$module}.enabled")); + } + + /** + * Get the status of a module from the module repository, including the new values if it was set. + * + * @param string $module + * + * @return mixed + */ + public function status(string $module) + { + return $this->repository->get("{$module}.enabled"); + } + + /** + * Update the route of a module configuration. + * + * @param string $module + * @param string $newRoute + * + * @return bool + * @throws ModuleNotFoundException + */ + public function route(string $module, string $newRoute) + { + return $this->repository->set("{$module}.route", $newRoute)->save(); + } + + /** + * Return the array collection of all enabled modules. + * + * @return array + */ + public function getActive() + { + return array_where($this->repository->all(), function ($value) { + return $value['enabled'] == true; + }); + } + + /** + * Return the array collection of all disabled modules. + * + * @return array + */ + public function getInactive() + { + return array_where($this->repository->all(), function ($value) { + return $value['enabled'] == false; + }); + } + + /** + * Sync the module pages to load with the condig status. + * + * @param $module + * @param bool $status + * + * @return void + */ + private function updatePageBelongingTo($module, bool $status) + { + Page::toggleModuleDisability($module, $status); + } +} diff --git a/app/Modules/ModuleNotFoundException.php b/app/Modules/ModuleNotFoundException.php new file mode 100644 index 00000000..eda19389 --- /dev/null +++ b/app/Modules/ModuleNotFoundException.php @@ -0,0 +1,10 @@ +module = $module; + } + + /** + * Handle pages that have been updated. + * + * @param Page $page + * + * @return void + * @throws ModuleNotFoundException + */ + public function updated(Page $page) + { + $this->updateModuleRoute($page); + } + + /** + * Handle pages that have been created. + * + * @param Page $page + * + * @throws ModuleNotFoundException + */ + public function created(Page $page) + { + $this->updateModuleRoute($page); + } + + /** + * Check if the page is type router. + * + * @param Page $page + * + * @return int + */ + private function isTypeRouter(Page $page): int + { + return $page->type & PageTypes::TYPE_ROUTER; + } + + /** + * Update the modules route on the configuration file. + * + * @param Page $page + * @throws ModuleNotFoundException + */ + public function updateModuleRoute(Page $page) + { + if ($this->isTypeRouter($page)) { + $this->module->route($page->module, $page->slug); + } + } +} diff --git a/app/Modules/ModuleServiceProvider.php b/app/Modules/ModuleServiceProvider.php new file mode 100644 index 00000000..dfef2407 --- /dev/null +++ b/app/Modules/ModuleServiceProvider.php @@ -0,0 +1,60 @@ +app->singleton(ModuleManager::class, function () { + return new ModuleManager(app(ModuleRepository::class)); + }); + } + + /** + * Register the route mapping of the modules. + * + * @return void. + */ + public static function map() + { + foreach (config('modules') as $key => $module) { + $namespace = sprintf('App\Modules\%s', $module['title']); + + if (config("modules.{$key}.enabled")) { + $backendRoute = base_path(sprintf('app/Modules/%s/Routes/backend.php', $module['title'])); + if (file_exists($backendRoute)) { + Route::middleware(['web', 'auth', 'gateway'])->namespace($namespace)->group($backendRoute); + } + + $frontendRoute = base_path(sprintf('app/Modules/%s/Routes/frontend.php', $module['title'])); + if (file_exists($frontendRoute)) { + Route::middleware(['web'])->namespace($namespace)->group($frontendRoute); + } + } + } + } +} diff --git a/app/Plugins/Menus/BackendController.php b/app/Modules/Navigation/BackendController.php similarity index 96% rename from app/Plugins/Menus/BackendController.php rename to app/Modules/Navigation/BackendController.php index 0879d518..626effe0 100644 --- a/app/Plugins/Menus/BackendController.php +++ b/app/Modules/Navigation/BackendController.php @@ -6,13 +6,13 @@ * Time: 14:55. */ -namespace App\Plugins\Menus; +namespace App\Modules\Navigation; use DB; use App\Model\Link; use App\Model\Menu; use Illuminate\Http\Request; -use App\Plugins\PluginEngine; +use App\Modules\ModuleEngine; use Illuminate\Validation\Rule; use App\Classes\Repositories\LinkRepository; use App\Classes\Repositories\MenuRepository; @@ -21,7 +21,7 @@ /** * Class Controller. */ -class BackendController extends PluginEngine +class BackendController extends ModuleEngine { /** * @var MenuRepository @@ -89,7 +89,7 @@ public function store(Request $request, Menu $menu) $this->save($request, $menu); - return redirect()->route('admin.menus.index'); + return redirect()->route('admin.navigation.index'); } /** @@ -131,7 +131,7 @@ public function update(Request $request, $id) $this->save($request, $menu); - return redirect(route('admin.menus.index')); + return redirect(route('admin.navigation.index')); } /** @@ -156,7 +156,7 @@ public function destroy($id) $menu->delete(); - return redirect(route('admin.menus.index')); + return redirect(route('admin.navigation.index')); } /** diff --git a/app/Plugins/Menus/Blade/create.blade.php b/app/Modules/Navigation/Blade/create.blade.php similarity index 96% rename from app/Plugins/Menus/Blade/create.blade.php rename to app/Modules/Navigation/Blade/create.blade.php index bf990fe6..207f82b0 100644 --- a/app/Plugins/Menus/Blade/create.blade.php +++ b/app/Modules/Navigation/Blade/create.blade.php @@ -12,7 +12,7 @@ @include('dashboard::structure.validation') -
+ {{ csrf_field() }} @@ -100,7 +100,7 @@
- Cancel + Cancel
diff --git a/app/Plugins/Menus/Blade/edit.blade.php b/app/Modules/Navigation/Blade/edit.blade.php similarity index 96% rename from app/Plugins/Menus/Blade/edit.blade.php rename to app/Modules/Navigation/Blade/edit.blade.php index c734f6a1..b8c2819b 100644 --- a/app/Plugins/Menus/Blade/edit.blade.php +++ b/app/Modules/Navigation/Blade/edit.blade.php @@ -14,7 +14,7 @@ - + @@ -106,7 +106,7 @@
- Cancel + Cancel
diff --git a/app/Plugins/Menus/Blade/index.blade.php b/app/Modules/Navigation/Blade/index.blade.php similarity index 83% rename from app/Plugins/Menus/Blade/index.blade.php rename to app/Modules/Navigation/Blade/index.blade.php index c9a05817..3de303a9 100644 --- a/app/Plugins/Menus/Blade/index.blade.php +++ b/app/Modules/Navigation/Blade/index.blade.php @@ -19,7 +19,7 @@
  • - $menu->id]) }}" class="{{ Request::segment(4) == $menu->id ? 'active' : null }}"> + $menu->id]) }}" class="{{ Request::segment(4) == $menu->id ? 'active' : null }}"> {{ $menu->title }} {{ count($menu->children->toArray()) }} @@ -43,7 +43,7 @@
  • @@ -70,8 +70,8 @@
    diff --git a/app/Modules/Navigation/FrontendController.php b/app/Modules/Navigation/FrontendController.php new file mode 100644 index 00000000..93f82db6 --- /dev/null +++ b/app/Modules/Navigation/FrontendController.php @@ -0,0 +1,11 @@ + 'admin']); + Route::resource('admin/navigation', 'BackendController', ['as' => 'admin']); // Ajax reorder. - Route::post('/admin/menus/reorder/')->uses('BackendController@reorder')->name('admin.menus.reorder'); + Route::post('/admin/menus/reorder/')->uses('BackendController@reorder')->name('admin.navigation.reorder'); // Allows groupings on the index. - Route::get('/admin/menus/group/{group_id}')->uses('BackendController@index')->name('admin.menus.group'); + Route::get('/admin/menus/group/{group_id}')->uses('BackendController@index')->name('admin.navigation.group'); // Post Requests. // ================================================================================== diff --git a/app/Plugins/Products/Routes/frontend.php b/app/Modules/Navigation/Routes/frontend.php similarity index 100% rename from app/Plugins/Products/Routes/frontend.php rename to app/Modules/Navigation/Routes/frontend.php diff --git a/app/Plugins/Newsletters/BackendController.php b/app/Modules/Newsletters/BackendController.php similarity index 86% rename from app/Plugins/Newsletters/BackendController.php rename to app/Modules/Newsletters/BackendController.php index 0c1389c8..7b19361b 100644 --- a/app/Plugins/Newsletters/BackendController.php +++ b/app/Modules/Newsletters/BackendController.php @@ -1,21 +1,15 @@ uses('App\Plugins\Newsletters\FrontendController@joinNewsletter')->name('newsletter.join'); + Route::post('/newsletter/join')->uses('FrontendController@joinNewsletter')->name('newsletter.join'); - Route::get('/newsletter/complete')->uses('App\Plugins\Newsletters\FrontendController@completedNewsletter')->name('newsletter.complete'); - Route::get('/newsletter/failure')->uses('App\Plugins\Newsletters\FrontendController@completedNewsletter')->name('newsletter.failure'); + Route::get('/newsletter/complete')->uses('FrontendController@completedNewsletter')->name('newsletter.complete'); + Route::get('/newsletter/failure')->uses('FrontendController@completedNewsletter')->name('newsletter.failure'); diff --git a/app/Plugins/Pages/BackendController.php b/app/Modules/Pages/BackendController.php similarity index 95% rename from app/Plugins/Pages/BackendController.php rename to app/Modules/Pages/BackendController.php index 86343ee3..2f86f57f 100644 --- a/app/Plugins/Pages/BackendController.php +++ b/app/Modules/Pages/BackendController.php @@ -6,21 +6,20 @@ * Time: 20:30. */ -namespace App\Plugins\Pages; +namespace App\Modules\Pages; -use App\Model\Link; use App\Model\Page; use Illuminate\Http\Request; -use App\Plugins\PluginEngine; +use App\Modules\ModuleEngine; use Illuminate\Validation\Rule; -use App\Plugins\Pages\Model\PageTypes; -use App\Plugins\Pages\Model\PageOptions; +use App\Modules\Pages\Model\PageTypes; +use App\Modules\Pages\Model\PageOptions; use App\Classes\Repositories\PageRepository; /** * Class Controller. */ -class BackendController extends PluginEngine +class BackendController extends ModuleEngine { /** * @var PageRepository @@ -46,7 +45,7 @@ public function index() public function indexPlugin() { - return $this->make('index')->with('pages', $this->repository->allPluginPages()); + return $this->make('index')->with('pages', $this->repository->allModulePages()); } /** diff --git a/app/Plugins/Pages/Blade/create.blade.php b/app/Modules/Pages/Blade/create.blade.php similarity index 100% rename from app/Plugins/Pages/Blade/create.blade.php rename to app/Modules/Pages/Blade/create.blade.php diff --git a/app/Plugins/Pages/Blade/edit.blade.php b/app/Modules/Pages/Blade/edit.blade.php similarity index 100% rename from app/Plugins/Pages/Blade/edit.blade.php rename to app/Modules/Pages/Blade/edit.blade.php diff --git a/app/Plugins/Pages/Blade/index.blade.php b/app/Modules/Pages/Blade/index.blade.php similarity index 98% rename from app/Plugins/Pages/Blade/index.blade.php rename to app/Modules/Pages/Blade/index.blade.php index 33d22af8..d608b83a 100644 --- a/app/Plugins/Pages/Blade/index.blade.php +++ b/app/Modules/Pages/Blade/index.blade.php @@ -1,7 +1,5 @@ @extends('dashboard::frame') - - @section('title') Website Pages @endsection diff --git a/app/Modules/Pages/FrontendController.php b/app/Modules/Pages/FrontendController.php new file mode 100644 index 00000000..e6a74c27 --- /dev/null +++ b/app/Modules/Pages/FrontendController.php @@ -0,0 +1,80 @@ +currentPage = $pages->whereName(currentURI()); + } + + /** + * Redirects must use a controller to handle the parameter, as they require a specified target. + * @return mixed + */ + public function redirect() + { + return redirect($this->currentPage->redirect->to(), 302); + } + + /** + * Standard page views are once that use the URL as the designated target. + * + * @return mixed + * @throws \Exception + * @internal param FrontPageLoader $pageLoader + */ + public function index() + { + IncrementViews::dispatch($this->currentPage); + + return Frontpage::build($this->currentPage); + } + + /** + * The sitemap function allows plugins to quickly and effectively + * create and store new content for the SEO Sitemap Controller. + * + * @param SitemapGenerator $sitemap + * @return SitemapGenerator + */ + public function sitemap(SitemapGenerator $sitemap) + { + /** @var PageRepository $repository */ + $repository = app(PageRepository::class); + + /** @var Page $page */ + foreach ($repository->whereSitemap() as $page) { + $sitemap->store(url($page->route()), $page->updated_at, 'bi-weekly', '1.0'); + } + + return $sitemap; + } +} diff --git a/app/Plugins/Pages/Model/PageOptions.php b/app/Modules/Pages/Model/PageOptions.php similarity index 88% rename from app/Plugins/Pages/Model/PageOptions.php rename to app/Modules/Pages/Model/PageOptions.php index 159953ea..9a7609fe 100644 --- a/app/Plugins/Pages/Model/PageOptions.php +++ b/app/Modules/Pages/Model/PageOptions.php @@ -1,6 +1,6 @@ frontendPageCollection() as $page) { - if (! $page->redirect && ! $page->plugin) { - Route::get($page->route())->uses('App\Http\Controllers\PageController@index'); + if (! $page->redirect && ! $page->module) { + Route::get($page->route())->uses('FrontendController@index'); } } diff --git a/app/Modules/Products/BackendController.php b/app/Modules/Products/BackendController.php new file mode 100644 index 00000000..7dd0d92d --- /dev/null +++ b/app/Modules/Products/BackendController.php @@ -0,0 +1,45 @@ +middleware(['role:developer']); + } + + /** + * Display a list of products available and disable, enable option for super admins. + */ + public function index() + { + return $this->make('index'); + } + + /** + * Toggle the enable or disable of a module. + * + * @param string $module + * @param ModuleManager $modules + * + * @return \Illuminate\Http\RedirectResponse + * + * @throws \App\Modules\ModuleNotFoundException + */ + public function toggle(string $module, ModuleManager $modules) + { + $modules->toggle(strtolower($module)); + + return response()->redirectToRoute('admin.products.index'); + } +} diff --git a/app/Modules/Products/Blade/index.blade.php b/app/Modules/Products/Blade/index.blade.php new file mode 100644 index 00000000..c728bd20 --- /dev/null +++ b/app/Modules/Products/Blade/index.blade.php @@ -0,0 +1,56 @@ +@extends('dashboard::frame') + +@section('title') + Product Overview +@endsection + +@section('information') + Products are interfaces which can be switched on and off, allowing complete control of your system. +@endsection + +@section('content') + +
    + + @foreach(config('modules') as $module) +
    + +
    +
    + {{ ucwords($module['title']) }} +
    +
    + Version {{ $module['version'] }} +
    +
    + +
    +
      + + @if (account()->hasRole($module['role'])) + @if ($module['enabled'] == true) +
    • Uninstall
    • + @else +
    • Install
    • + @endif + @else + @if ($module['enabled']) +
    • Currently Active
    • + @else +
    • Disabled
    • + @endif + @endif +
    +
    + +
    +
    + {!! css()->status->check($module['enabled']) !!} +
    +
    +
    + @endforeach + +
    + +@endsection \ No newline at end of file diff --git a/app/Modules/Products/Routes/backend.php b/app/Modules/Products/Routes/backend.php new file mode 100644 index 00000000..f3f790a4 --- /dev/null +++ b/app/Modules/Products/Routes/backend.php @@ -0,0 +1,19 @@ +name('admin.products.index'); + Route::get('/admin/products/{module}/toggle', 'BackendController@toggle')->name('admin.products.toggle'); diff --git a/app/Plugins/Redirects/BackendController.php b/app/Modules/Redirects/BackendController.php similarity index 95% rename from app/Plugins/Redirects/BackendController.php rename to app/Modules/Redirects/BackendController.php index b3ed8ddc..8f755dc0 100644 --- a/app/Plugins/Redirects/BackendController.php +++ b/app/Modules/Redirects/BackendController.php @@ -1,24 +1,17 @@ sitemap = $sitemap; - - $this->plugins = $plugins; - } - - /** - * @return View - * @internal param PluginRepository $plugins - */ - public function iframe() - { - // this is located on the dashboard. - // set it up as a dashboard controller. - $dashboard = app(DashboardController::class); - - $this->loadPluginSitemaps($this->plugins->allWhereActive()); - // get and create an array of all the sitemaps to be loaded - // send to the view for display. - - return $this->make('index')->with('sitemaps', $this->sitemap->generateArray()); - } - - /** - * @param PluginRepository $plugins - * @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response - */ - public function all() - { - $this->loadPluginSitemaps($this->plugins->allWhereActive()); - - return response($this->sitemap->generateXML(), 200, ['Content-Type' => 'text/xml;charset=utf-8']); - } - - private function loadPluginSitemaps(Collection $plugins) - { - - /** @var Plugin $plugin */ - foreach ($plugins as $plugin) { - $classDir = 'App\Plugins\\'.ucfirst($plugin->name).'\\FrontendController'; - - if (class_exists($classDir)) { - $class = app($classDir); - - if ($class instanceof Sitemap) { - $this->sitemap($class); - } - } - } - } - - /** - * Get the plugin sitemap function and its contents. - * - * @param Sitemap $plugin - * @return bool|mixed - */ - private function sitemap(Sitemap $plugin) - { - return $plugin->sitemap($this->sitemap); } } diff --git a/app/Modules/Sitemap/Routes/backend.php b/app/Modules/Sitemap/Routes/backend.php index ddab0be9..acb65b02 100644 --- a/app/Modules/Sitemap/Routes/backend.php +++ b/app/Modules/Sitemap/Routes/backend.php @@ -15,7 +15,7 @@ // Get Requests. // ================================================================================== - Route::get('/admin/sitemap')->uses('Controller@iframe')->name('sitemap.iframe'); + //Route::get('/admin/sitemap')->uses('Controller@sitemap')->name('sitemap.xml'); // Post Requests. // ================================================================================== diff --git a/app/Modules/Sitemap/Routes/frontend.php b/app/Modules/Sitemap/Routes/frontend.php index 5254a718..962988fd 100644 --- a/app/Modules/Sitemap/Routes/frontend.php +++ b/app/Modules/Sitemap/Routes/frontend.php @@ -15,7 +15,7 @@ // Get Requests. // ================================================================================== - Route::get('/sitemap.xml')->uses('App\Modules\Sitemap\Controller@all')->name('sitemap'); + //Route::get('/sitemap.xml')->uses('Controller@all')->name('sitemap'); // Post Requests. // ================================================================================== diff --git a/app/Plugins/Articles/ArticlesController.php b/app/Plugins/Articles/ArticlesController.php deleted file mode 100644 index 4de5d925..00000000 --- a/app/Plugins/Articles/ArticlesController.php +++ /dev/null @@ -1,76 +0,0 @@ - 'Articles', - 'identifier' => 'articles', - 'slug' => 'articles', - 'type' => PageTypes::TYPE_PLUGIN, - 'option' => PageOptions::OPTION_PUBLIC, - ]); - - return $page; - } - - /** - * The steps required for this plugin product to fully - * remove itself from the webservice. - * - * @return bool - * @throws \Exception - */ - public function uninstall() - { - /** @var PageRepository $repository */ - $pages = app(PageRepository::class); - - /** @var Page $page */ - $page = $pages->whereIdentifier('articles'); - - // status of the operation. - return $page->forceDelete(); - } -} diff --git a/app/Plugins/Articles/Routes/frontend.php b/app/Plugins/Articles/Routes/frontend.php deleted file mode 100644 index 52717cd3..00000000 --- a/app/Plugins/Articles/Routes/frontend.php +++ /dev/null @@ -1,29 +0,0 @@ -path())->uses('App\Plugins\Articles\FrontendController@allArticles')->name('articles.all'); - Route::get($page->path().'/search')->uses('App\Plugins\Articles\FrontendController@searchArticles')->name('search.articles'); - Route::get($page->path().'/{category}')->uses('App\Plugins\Articles\FrontendController@categoryArticles')->name('category.articles'); - Route::get($page->path().'/creator/{account}')->uses('App\Plugins\Articles\FrontendController@allCreatorsArticles')->name('creator.articles'); - Route::get($page->path().'/{category}/{article}')->uses('App\Plugins\Articles\FrontendController@viewArticle')->name('article.view'); diff --git a/app/Plugins/Menus/FrontendController.php b/app/Plugins/Menus/FrontendController.php deleted file mode 100644 index 9623c6cf..00000000 --- a/app/Plugins/Menus/FrontendController.php +++ /dev/null @@ -1,19 +0,0 @@ -whereSitemap() as $page) { - $sitemap->store(url($page->route()), $page->updated_at, 'bi-weekly', '1.0'); - } - - return $sitemap; - } -} diff --git a/app/Plugins/Pages/PagesController.php b/app/Plugins/Pages/PagesController.php deleted file mode 100644 index 5d650211..00000000 --- a/app/Plugins/Pages/PagesController.php +++ /dev/null @@ -1,33 +0,0 @@ -make(sprintf('plugins::%s.Blade.%s', $this->pluginName(), $blade_template)); - } - - /** - * Get the module name by checking the class name location. - * - * @todo class_basname() ? - * - * @return mixed - */ - protected function pluginName() - { - if ($this->pluginName) { - return $this->pluginName; - } - - return $this->pluginName = explode('\\', get_class($this))[2]; - } - - /** - * Redirect to a plugin view location. - * For ajax return calls. - * - * @param $blade_template - * @return \Illuminate\Http\RedirectResponse - */ - protected function redirect($blade_template) - { - return redirect()->intended($this->pluginName().'::'.$blade_template); - } - - /** - * @todo what does this do and document it. - * - * @return Plugin|array|\stdClass - */ - protected function pluginData() - { - return (new PluginRepository(new Plugin))->whereName($this->pluginName()); - } -} diff --git a/app/Plugins/PluginHandler.php b/app/Plugins/PluginHandler.php deleted file mode 100644 index dc9018f7..00000000 --- a/app/Plugins/PluginHandler.php +++ /dev/null @@ -1,29 +0,0 @@ -plugins = $plugins; - - $this->middleware(['role:developer']); - } - - /** - * Display a list of products available and disable, enable option for super admins. - */ - public function index() - { - return $this->make('index')->with('products', $this->plugins->all()); - } - - /** - * Steps required for the application install. - * Usually defined for logging & new sql entries. - * - * @param string $plugin_name - * @return mixed - */ - public function install(Plugin $plugin) - { - if ($plugin->controller instanceof Installable) { - $plugin->controller->install(); - - $plugin->toggle(); - - return response()->redirectToRoute('admin.products.index'); - } - - throw new PluginNotInstanceOfInstallable; - } - - /** - * Steps required for the application uninstall. - * Usually defined for logging & new sql entries. - * - * @param string $plugin_name - * @return mixed - */ - public function uninstall(Plugin $plugin) - { - if ($plugin->controller instanceof Installable) { - $plugin->controller->uninstall(); - - $plugin->toggle(); - - return response()->redirectToRoute('admin.products.index'); - } - - throw new PluginNotInstanceOfInstallable; - } -} diff --git a/app/Plugins/Products/Blade/index.blade.php b/app/Plugins/Products/Blade/index.blade.php deleted file mode 100644 index d0c93cef..00000000 --- a/app/Plugins/Products/Blade/index.blade.php +++ /dev/null @@ -1,68 +0,0 @@ -@extends('dashboard::frame') - -@section('title') - Product Overview -@endsection - -@section('information') - Products are interfaces which can be switched on and off, allowing complete control of your system. -@endsection - -@section('content') - -
    - - - - @foreach($products as $product) -
    - -
    -
    - {{ ucfirst($product->name()) }} -
    -
    - Version {{ $product->version() }} -
    -
    - -
    -
      - - @role('developer') - @if($product->required == false) - @if ($product->enabled) -
    • Uninstall
    • - @else -
    • Install
    • - @endif - @else -
    • Framework Requirement
    • - @endif - @else - @if ($product->enabled) -
    • Currently Active
    • - @else -
    • Disabled
    • - @endif - @endrole - - {{--
    • {!! css()->status->installed($product->isEnabled()) !!}
    • --}} - {{--
    • {!! css()->link->edit(route('admin.pages.edit', ["name"=>$page->slug])) !!}
    • --}} - {{--
    • {!! css()->status->sitemap($page->sitemap) !!}
    • --}} - {{--
    • {!! css()->status->status($page->enabled) !!}
    • --}} - {{--
    • {!! css()->link->view(makeUrl($page)) !!}
    • --}} -
    -
    - -
    -
    - {!! css()->status->check($product->enabled) !!} -
    -
    -
    - @endforeach - -
    - -@endsection \ No newline at end of file diff --git a/app/Plugins/Products/Exceptions/PluginNotInstanceOfInstallable.php b/app/Plugins/Products/Exceptions/PluginNotInstanceOfInstallable.php deleted file mode 100644 index 99638d22..00000000 --- a/app/Plugins/Products/Exceptions/PluginNotInstanceOfInstallable.php +++ /dev/null @@ -1,10 +0,0 @@ -bootBladeDirectives(); - Schema::defaultStringLength(191); } @@ -29,16 +26,4 @@ public function register() { // } - - /** - * Boot blade directives. - * - * @return void - */ - private function bootBladeDirectives() - { - Blade::if('role', function ($role) { - return account()->hasRole($role); - }); - } } diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 2c948711..9a0847d9 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -2,8 +2,8 @@ namespace App\Providers; -use App\Model\Plugin; use Illuminate\Support\Facades\Route; +use App\Modules\ModuleServiceProvider; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; /** @@ -20,16 +20,6 @@ class RouteServiceProvider extends ServiceProvider */ protected $namespace = 'App\Http\Controllers'; - /** - * Define your route model bindings, pattern filters, etc. - * - * @return void - */ - public function boot() - { - parent::boot(); - } - /** * Define the routes for the application. * @@ -37,99 +27,10 @@ public function boot() */ public function map() { - $this->mapApiRoutes(); - - $this->mapWebRoutes(); - - $this->mapPluginRoutes(); - - $this->mapModulesRoutes(); - - $this->mapVendorRoutes(); - } - - /** - * Define the "web" routes for the application. - * - * These routes all receive session state, CSRF protection, etc. - * - * @return void - */ - protected function mapWebRoutes() - { - $tom = []; - - Route::middleware('web')->namespace($this->namespace)->group(base_path('routes/web.php')); - } + ModuleServiceProvider::map(); - /** - * Define the "api" routes for the application. - * - * These routes are typically stateless. - * - * @return void - */ - protected function mapApiRoutes() - { - Route::prefix('api')->middleware('api')->namespace($this->namespace)->group(base_path('routes/api.php')); - } + Route::middleware('web')->group(base_path('routes/web.php')); - /** - * Define the backend routes for the application. - * - * Plugins have dynamic creation of web vs admin. - * - * " - * @return void - */ - protected function mapPluginRoutes() - { - /** @var Plugin $plugin */ - foreach (plugins()->enabled() as $plugin) { - $plugin_name = ucfirst($plugin->name()); - - $namespace = sprintf('App\Plugins\%s', $plugin_name); - - $frontendRoute = base_path(sprintf('app/Plugins/%s/Routes/frontend.php', $plugin_name)); - Route::middleware(['web'])->group($frontendRoute); - - $backendRoute = base_path(sprintf('app/Plugins/%s/Routes/backend.php', $plugin_name)); - Route::middleware(['web', 'auth', 'gateway'])->namespace($namespace)->group($backendRoute); - } - } - - /** - * Define the backend routes for the application.0. - * - * Modules are loaded as modularity. - * - * @return void - */ - protected function mapModulesRoutes() - { - foreach (config('modules') as $module) { - $module_name = ucfirst($module['title']); - - $namespace = sprintf('App\Modules\%s', $module['title']); - - $backendRoute = base_path(sprintf('app/Modules/%s/Routes/backend.php', $module_name)); - - $frontendRoute = base_path(sprintf('app/Modules/%s/Routes/frontend.php', $module_name)); - - // Frontend are routes that can be accessed by visitors. - Route::middleware(['web'])->group($frontendRoute); - - // Backend are routes that can only be accessed to those with access. - Route::middleware(['web', 'auth', 'gateway'])->namespace($namespace)->group($backendRoute); - } - } - - /** - * Third party routes sometimes require auth, and sometimes not, but since we dont - * define a namespace, its best have it in its seperate folder for simplicity. - */ - protected function mapVendorRoutes() - { Route::middleware('web')->group(base_path('routes/vendor.php')); } } diff --git a/composer.json b/composer.json index 18014b4c..bfa16a3d 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,7 @@ "barryvdh/laravel-elfinder": "0.3.12", "doctrine/dbal": "2.7.1", "fideloper/proxy": "^4.0", + "larapack/config-writer": "1.*", "laravel/framework": "5.6.*", "laravel/scout": "^4.0", "laravel/tinker": "^1.0", diff --git a/composer.lock b/composer.lock index 52ea9c07..e4d51ef5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "fd8abe0eec4a907e7ac2a50b67ec43da", + "content-hash": "7c022288640d73a9f4e3d3b06b707d22", "packages": [ { "name": "anahkiasen/underscore-php", @@ -1108,6 +1108,41 @@ ], "time": "2015-04-20T18:58:01+00:00" }, + { + "name": "larapack/config-writer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/larapack/config-writer.git", + "reference": "b36a716833240fe9e8b4b4d007b68ef9e91a1124" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/larapack/config-writer/zipball/b36a716833240fe9e8b4b4d007b68ef9e91a1124", + "reference": "b36a716833240fe9e8b4b4d007b68ef9e91a1124", + "shasum": "" + }, + "type": "package", + "autoload": { + "psr-4": { + "": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Save changes to the configuration file in script.", + "keywords": [ + "config", + "larapack", + "laravel", + "package", + "save", + "writer" + ], + "time": "2016-04-28T12:45:27+00:00" + }, { "name": "laravel/framework", "version": "v5.6.26", diff --git a/config/app.php b/config/app.php index 353fd7bd..c3b9acc1 100644 --- a/config/app.php +++ b/config/app.php @@ -176,14 +176,11 @@ /* * Application Service Providers... */ + App\Modules\ModuleServiceProvider::class, App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, - // App\Providers\BroadcastServiceProvider::class, + App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, - - /* - * Webshelf Service Providers... - */ App\Providers\ConfigurationServiceProvider::class, App\Providers\ComposerServiceProvider::class, App\Providers\InstanceServiceProvider::class, @@ -212,7 +209,7 @@ 'Broadcast' => Illuminate\Support\Facades\Broadcast::class, 'Bus' => Illuminate\Support\Facades\Bus::class, 'Cache' => Illuminate\Support\Facades\Cache::class, - 'Config' => Illuminate\Support\Facades\Config::class, + 'Config' => Larapack\ConfigWriter\Facade::class, 'Cookie' => Illuminate\Support\Facades\Cookie::class, 'Crypt' => Illuminate\Support\Facades\Crypt::class, 'DB' => Illuminate\Support\Facades\DB::class, diff --git a/config/modules.php b/config/modules.php index b703c169..3b2ec063 100644 --- a/config/modules.php +++ b/config/modules.php @@ -2,47 +2,104 @@ return [ - /* - |-------------------------------------------------------------------------- - | Core application menus that are used on the dashboard menu. - |-------------------------------------------------------------------------- - | - | From here you can enable or disable a menu and edit the configuration - | this will allow the changes to be worked all throughout the dashboard. - */ - - [ + 'products' => [ + 'title' => 'Products', + 'enabled' => true, + 'icon' => 'fas fa-box', + 'version' => 1.0, + 'url' => 'admin/products', + 'role' => 'publisher', + ], + + 'navigation' => [ + 'title' => 'Navigation', + 'enabled' => true, + 'icon' => 'fas fa-compass', + 'version' => 1.0, + 'url' => 'admin/navigation', + 'role' => 'publisher', + ], + + 'pages' => [ + 'title' => 'Pages', + 'enabled' => true, + 'icon' => 'fab fa-readme', + 'version' => 1.0, + 'url' => 'admin/pages', + 'role' => 'publisher', + ], + + 'redirects' => [ + 'title' => 'Redirects', + 'enabled' => false, + 'icon' => 'fas fa-exchange-alt', + 'version' => 1.0, + 'url' => 'admin/redirects', + 'role' => 'publisher', + ], + + 'articles' => [ + 'title' => 'Articles', + 'enabled' => true, + 'icon' => 'fas fa-glasses', + 'version' => 1.0, + 'url' => 'admin/articles', + 'role' => 'publisher', + 'route' => 'articles', + ], + + 'newsletters' => [ + 'title' => 'Newsletters', + 'enabled' => false, + 'icon' => 'fa-book', + 'version' => 1.0, + 'url' => 'admin/newsletter', + 'role' => 'publisher', + ], + + 'settings' => [ 'title' => 'Settings', + 'enabled' => true, 'icon' => 'fas fa-wrench', + 'version' => 1.0, 'url' => 'admin/settings', 'role' => 'administrator', ], - [ + 'accounts' => [ 'title' => 'Accounts', + 'enabled' => true, 'icon' => 'fa fa-users', + 'version' => 1.0, 'url' => 'admin/accounts', 'role' => 'administrator', ], - [ + 'filemanager' => [ 'title' => 'Filemanager', + 'enabled' => true, 'icon' => 'fas fa-toolbox', + 'version' => 1.0, 'url' => 'admin/filemanager', 'role' => 'publisher', ], - [ + 'updates' => [ 'title' => 'Updates', + 'enabled' => true, 'icon' => 'fas fa-code', + 'version' => 1.0, 'url' => 'admin/updates', 'role' => 'developer', ], - [ + 'sitemap' => [ 'title' => 'Sitemap', + 'enabled' => true, 'icon' => 'fas fa-globe', + 'version' => 1.0, 'url' => 'admin/sitemap', 'role' => 'developer', ], + ]; diff --git a/config/sentry.php b/config/sentry.php index 63e8c378..779b9801 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -4,7 +4,7 @@ 'dsn' => env('SENTRY_LARAVEL_DSN', 'https://b8aa8a46e793436885598609749d6876:803fa5f399be4309926bafb0b9bc9a2f@sentry.io/1221084'), // capture release as git sha - 'release' => trim(exec('git log --pretty="%h" -n1 HEAD')), + 'release' => '', //trim(exec('git log --pretty="%h" -n1 HEAD')), // Capture bindings on SQL queries 'breadcrumbs.sql_bindings' => true, diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 0ea1930b..a224241c 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -2,8 +2,8 @@ use Faker\Generator as Faker; use App\Classes\Roles\Administrator; -use App\Plugins\Pages\Model\PageTypes; -use App\Plugins\Pages\Model\PageOptions; +use App\Modules\Pages\Model\PageTypes; +use App\Modules\Pages\Model\PageOptions; /* |-------------------------------------------------------------------------- @@ -38,7 +38,7 @@ return [ 'seo_title' => $title, 'seo_keywords' => $faker->paragraph(1), - 'seo_description' => $faker->paragraph(2), + 'seo_description' => $faker->paragraph(1), 'prefix' => $faker->word, 'slug' => str_slug($title), 'views' => $faker->numberBetween(75, 900), diff --git a/database/migrations/2016_12_14_225300_create_plugins_table.php b/database/migrations/2016_12_14_225300_create_plugins_table.php index 8185f37e..baf2095b 100644 --- a/database/migrations/2016_12_14_225300_create_plugins_table.php +++ b/database/migrations/2016_12_14_225300_create_plugins_table.php @@ -1,6 +1,5 @@ tinyInteger('is_backend')->nullable(); $table->timestamps(); }); - - /* - * DATA REQUIRED FOR APPLICATION CORE LOADING. - */ - $plugin = new Plugin; - $plugin->setName('products'); - $plugin->setVersion('1.0'); - $plugin->setIcon('fa-book'); - $plugin->setEnabled(true); - $plugin->setRequired(true); - $plugin->setFrontEnd(false); - $plugin->setBackEnd(true); - $plugin->save(); - - $plugin = new Plugin; - $plugin->setName('menus'); - $plugin->setVersion('1.0'); - $plugin->setIcon('fa-bars'); - $plugin->setEnabled(true); - $plugin->setFrontEnd(false); - $plugin->setBackEnd(true); - $plugin->save(); - - $plugin = new Plugin; - $plugin->setName('pages'); - $plugin->setVersion('1.0'); - $plugin->setIcon('fa-paperclip'); - $plugin->setEnabled(true); - $plugin->setFrontEnd(true); - $plugin->setBackEnd(true); - $plugin->save(); - - $plugin = new Plugin; - $plugin->setName('news'); - $plugin->setVersion('1.0'); - $plugin->setIcon('fa-newspaper-o'); - $plugin->setFrontEnd(true); - $plugin->setBackEnd(true); - $plugin->save(); - - $plugin = new Plugin; - $plugin->setName('redirects'); - $plugin->setVersion('1.0'); - $plugin->setIcon('fa-magic'); - $plugin->setFrontEnd(true); - $plugin->setBackEnd(true); - $plugin->save(); - - $plugin = new Plugin; - $plugin->setName('facebook'); - $plugin->setVersion('2.8'); - $plugin->setIcon('fa-facebook-official'); - $plugin->setHide(true); - $plugin->setFrontEnd(true); - $plugin->setBackEnd(true); - $plugin->save(); } /** diff --git a/database/migrations/2016_12_21_154441_edit_facebook_plugin_data.php b/database/migrations/2016_12_21_154441_edit_facebook_plugin_data.php index dd63c9b9..7994751f 100644 --- a/database/migrations/2016_12_21_154441_edit_facebook_plugin_data.php +++ b/database/migrations/2016_12_21_154441_edit_facebook_plugin_data.php @@ -1,8 +1,6 @@ whereName('facebook'); - - $plugin->setHide(false)->save(); + // } /** @@ -26,9 +21,6 @@ public function up() */ public function down() { - /** @var Plugin $plugin */ - $plugin = app(PluginRepository::class)->whereName('facebook'); - - $plugin->setHide(true)->save(); + // } } diff --git a/database/migrations/2017_01_07_225206_create_carousels_table.php b/database/migrations/2017_01_07_225206_create_carousels_table.php index ed64bc73..0d07522f 100644 --- a/database/migrations/2017_01_07_225206_create_carousels_table.php +++ b/database/migrations/2017_01_07_225206_create_carousels_table.php @@ -1,9 +1,7 @@ softDeletes(); $table->timestamps(); }); - - /** @var Plugin $plugin */ - $plugin = new Plugin(); - $plugin->setName('carousels'); - $plugin->setVersion('1.0'); - $plugin->setIcon('fa-fast-forward'); - $plugin->setBackEnd(true); - $plugin->save(); } /** @@ -58,7 +48,5 @@ public function down() \Schema::drop('carousels'); \Schema::drop('carousel_slides'); - - app(PluginRepository::class)->whereName('carousels')->delete(); } } diff --git a/database/migrations/2018_01_03_150616_cleanup_plugins.php b/database/migrations/2018_01_03_150616_cleanup_plugins.php index 96dec62f..e7750b06 100644 --- a/database/migrations/2018_01_03_150616_cleanup_plugins.php +++ b/database/migrations/2018_01_03_150616_cleanup_plugins.php @@ -19,21 +19,6 @@ public function up() Schema::drop('articles'); Schema::drop('plugin_feeds'); Schema::drop('plugin_options'); - - $plugin = app(\App\Classes\Repositories\PluginRepository::class); - - $plugin->whereName('carousels')->delete(); - $plugin->whereName('news')->delete(); - $plugin->whereName('facebook')->delete(); - - $item = $plugin->whereName('menus'); - $item->setVersion('2.6')->setRequired(false)->save(); - $item = $plugin->whereName('pages'); - $item->setVersion('2.1')->setRequired(false)->save(); - $item = $plugin->whereName('redirects'); - $item->setVersion('1.9')->setEnabled(true)->setRequired(false)->save(); - $item = $plugin->whereName('products'); - $item->setVersion('1.3')->save(); } /** diff --git a/database/migrations/2018_02_25_004151_february_update_one.php b/database/migrations/2018_02_25_004151_february_update_one.php index dc3ea9bc..7d6c4ee7 100644 --- a/database/migrations/2018_02_25_004151_february_update_one.php +++ b/database/migrations/2018_02_25_004151_february_update_one.php @@ -1,8 +1,6 @@ setAttribute('name', 'articles'); - $plugin->setAttribute('version', '1.0'); - $plugin->setAttribute('icon', 'fa-book'); - $plugin->setAttribute('enabled', false); - $plugin->setAttribute('is_frontend', true); - $plugin->setAttribute('is_backend', true); - $plugin->setAttribute('required', false); - $plugin->save(); - - /** @var \App\Classes\Repositories\PluginRepository $pluginRepository */ - $pluginRepository = app(\App\Classes\Repositories\PluginRepository::class); - - $pluginRepository->whereName('menus')->setAttribute('required', true)->save(); - $pluginRepository->whereName('pages')->setAttribute('required', true)->save(); - Schema::table('plugins', function (Blueprint $table) { $table->dropColumn('version'); $table->dropColumn('icon'); @@ -62,11 +43,6 @@ public function up() $table->softDeletes(); $table->timestamps(); }); - - /* - * INDEX ALL NEW MATERIAL FOR SEARCHING. - */ - // Artisan::call('scout:mysql-index'); } /** diff --git a/database/migrations/2018_03_01_232041_march_update_one.php b/database/migrations/2018_03_01_232041_march_update_one.php index cf22105f..c151aae6 100644 --- a/database/migrations/2018_03_01_232041_march_update_one.php +++ b/database/migrations/2018_03_01_232041_march_update_one.php @@ -3,7 +3,7 @@ use App\Model\Page; use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; -use App\Plugins\Newsletters\Model\Newsletter; +use App\Modules\Newsletters\Model\Newsletter; use Illuminate\Database\Migrations\Migration; class MarchUpdateOne extends Migration @@ -15,15 +15,6 @@ class MarchUpdateOne extends Migration */ public function up() { - $plugin = new \App\Model\Plugin(); - - $plugin->setAttribute('name', 'newsletters'); - $plugin->setAttribute('enabled', false); - $plugin->setAttribute('is_frontend', true); - $plugin->setAttribute('is_backend', true); - $plugin->setAttribute('required', false); - $plugin->save(); - Schema::create('newsletters', function (Blueprint $table) { $table->increments('id'); $table->string('title'); diff --git a/database/migrations/2018_06_03_001436_june_update_one.php b/database/migrations/2018_06_03_001436_june_update_one.php index 94f416d5..d9877f42 100644 --- a/database/migrations/2018_06_03_001436_june_update_one.php +++ b/database/migrations/2018_06_03_001436_june_update_one.php @@ -1,9 +1,9 @@ string('module')->after('option')->nullable(); + }); + + Page::firstOrCreate([ + 'seo_title' => 'Articles', + 'identifier' => 'articles', + 'slug' => 'articles', + 'type' => PageTypes::TYPE_MODULE, + 'option' => PageOptions::OPTION_DISABLED, + ]); + + foreach (Page::all() as $page) { + if ($page->type & PageTypes::TYPE_PLUGIN) { + $page->type = $page->type & ~PageTypes::TYPE_PLUGIN; + $page->type = $page->type | PageTypes::TYPE_MODULE; + } + + if ($page->identifier == 'articles') { + $page->module = 'articles'; + $page->type = PageTypes::TYPE_MODULE | PageTypes::TYPE_ROUTER; + } + + if ($page->identifier == 'newsletter.success') { + $page->module = 'newsletters'; + $page->option = $page->option | PageOptions::OPTION_DISABLED; + } + + if ($page->identifier == 'error.404') { + $page->module = 'errors'; + } + + $page->save(); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('modules'); + } +} diff --git a/resources/admin/views/structure/navbar.blade.php b/resources/admin/views/structure/navbar.blade.php index e4d4e3fb..7db22470 100644 --- a/resources/admin/views/structure/navbar.blade.php +++ b/resources/admin/views/structure/navbar.blade.php @@ -19,7 +19,7 @@ diff --git a/resources/admin/views/structure/sidebar.blade.php b/resources/admin/views/structure/sidebar.blade.php index d7d2d14f..15402467 100644 --- a/resources/admin/views/structure/sidebar.blade.php +++ b/resources/admin/views/structure/sidebar.blade.php @@ -22,23 +22,10 @@ - @foreach(plugins()->viewable() as $plugin) - -
  • - - - - - {{ ucfirst($plugin->name()) }} - - -
  • - - @endforeach - @foreach(config('modules') as $module) - @role($module['role']) + @if($module['enabled'] && account()->hasRole($module['role'])) +
  • diff --git a/routes/web.php b/routes/web.php index 200190f4..2c85d8c8 100644 --- a/routes/web.php +++ b/routes/web.php @@ -14,7 +14,7 @@ | contains the "web" middleware group. Now create something great! | */ - Route::get('/')->uses('PageController@index')->name('index'); + Route::get('/')->uses('App\Modules\Pages\FrontendController@index')->name('index'); /* |-------------------------------------------------------------------------- @@ -26,7 +26,7 @@ | contains the "web" middleware group. Now create something great! | */ - Route::get('/admin')->uses('DashboardController@index')->name('dashboard')->middleware(['web', 'auth', 'gateway']); + Route::get('/admin')->uses('App\Http\Controllers\DashboardController@index')->name('dashboard')->middleware(['web', 'auth', 'gateway']); /* |-------------------------------------------------------------------------- @@ -38,7 +38,7 @@ | contains the "web" middleware group. Now create something great! | */ - Route::get('/admin/login')->uses('AuthController@form')->name('login'); + Route::get('/admin/login')->uses('App\Http\Controllers\AuthController@form')->name('login'); /* |-------------------------------------------------------------------------- @@ -50,7 +50,7 @@ | contains the "web" middleware group. Now create something great! | */ - Route::post('/admin/login')->uses('AuthController@login')->name('AuthLogin'); + Route::post('/admin/login')->uses('App\Http\Controllers\AuthController@login')->name('AuthLogin'); /* |-------------------------------------------------------------------------- @@ -62,4 +62,4 @@ | contains the "web" middleware group. Now create something great! | */ - Route::get('/admin/logout')->uses('AuthController@logout')->name('AuthLogout'); + Route::get('/admin/logout')->uses('App\Http\Controllers\AuthController@logout')->name('AuthLogout'); diff --git a/tests/Article/ArticleInstallationTest.php b/tests/Article/ArticleInstallationTest.php deleted file mode 100644 index 56224aee..00000000 --- a/tests/Article/ArticleInstallationTest.php +++ /dev/null @@ -1,87 +0,0 @@ -first(); - - $this->assertEquals('articles', $plugin['name']); - } - - /** - * @test - */ - public function the_article_plugin_can_be_toggled() - { - $plugin = Plugin::whereName('articles')->first(); - - $plugin->toggle(); - - $this->assertTrue($plugin->enabled); - } - - /** - * @test - */ - public function the_article_plugin_has_a_controller_class() - { - $plugin = Plugin::whereName('articles')->first(); - - $this->assertInstanceOf(ArticlesController::class, $plugin->controller); - } - - /** - * @test - */ - public function the_article_plugin_exists_on_the_dashboard() - { - $this->signIn(['role_id' => Developer::$key]); - - $this->get('admin/products/index') - ->assertSee('Articles'); - } - - /** - * @test - */ - public function the_article_plugin_can_be_installed() - { - $this->signIn(['role_id' => Developer::$key]); - - $this->get('/admin/products/install/articles') - ->assertRedirect('/admin/products/index'); - - $this->assertDatabaseHas('plugins', ['name' => 'articles', 'enabled' => true]); - $this->assertDatabaseHas('pages', ['identifier' => 'articles']); - } - - /** - * @test - */ - public function the_article_plugin_can_be_uninstalled() - { - $this->signIn(['role_id' => Developer::$key]); - - $this->get('/admin/products/install/articles'); - - $this->get('/admin/products/uninstall/articles') - ->assertRedirect('/admin/products/index'); - - $this->assertDatabaseHas('plugins', ['name' => 'articles', 'enabled' => false]); - $this->assertDatabaseMissing('pages', ['identifier' => 'articles']); - } -} diff --git a/tests/Article/ArticleViewingTest.php b/tests/Article/ArticleViewingTest.php index 8d26141b..13a08cfe 100644 --- a/tests/Article/ArticleViewingTest.php +++ b/tests/Article/ArticleViewingTest.php @@ -3,7 +3,7 @@ namespace Tests\Article; use Tests\TestCase; -use App\Model\Plugin; +use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Route; use Illuminate\Foundation\Testing\WithFaker; use Illuminate\Foundation\Testing\RefreshDatabase; @@ -19,13 +19,6 @@ class ArticleViewingTest extends TestCase */ use RefreshDatabase; - /** - * The plugin model for articles. - * - * @var Article - */ - private $plugin; - /** * The Faker instance. * @@ -40,13 +33,9 @@ public function setUp() { parent::setUp(); - $this->plugin = Plugin::install('articles'); - - Route::middleware('web', 'auth', 'gateway')->group(base_path(sprintf('app/Plugins/%s/Routes/backend.php', 'Articles'))); - - Route::middleware('web')->group(base_path(sprintf('app/Plugins/%s/Routes/frontend.php', 'Articles'))); + Route::middleware('web', 'auth', 'gateway')->group(base_path('app/Modules/Articles/Routes/backend.php')); - // $this->app['router']->getRoutes()->refreshNameLookups(); + Route::middleware('web')->namespace('App\Modules\Articles')->group(base_path('app/Modules/Articles/Routes/frontend.php')); } /** diff --git a/tests/Module/ModuleManagerTest.php b/tests/Module/ModuleManagerTest.php new file mode 100644 index 00000000..6a9ba981 --- /dev/null +++ b/tests/Module/ModuleManagerTest.php @@ -0,0 +1,238 @@ +expectException(ModuleNotFoundException::class); + + // when + app(ModuleManager::class)->enable('unknown'); + } + + /** + * @test + * @throws ModuleNotFoundException + */ + public function it_can_disable_a_module() + { + Config::set('modules.config', ['enabled' => true]); + + $module = $this->mockModuleManager(); + + $module->disable('config'); + + $this->assertFalse($module->status('config')); + } + + /** + * @test + * @throws ModuleNotFoundException + */ + public function it_can_enable_a_module() + { + Config::set('modules.config', ['enabled' => false]); + + $module = $this->mockModuleManager(); + + $module->enable('config'); + + $this->assertTrue($module->status('config')); + } + + /** + * @test + * @throws ModuleNotFoundException + */ + public function it_can_toggle_a_module() + { + Config::set('modules.config', ['enabled' => false]); + + $module = $this->mockModuleManager(); + + $module->toggle('config'); + + $this->assertTrue($module->status('config')); + + $module->toggle('config'); + + $this->assertFalse($module->status('config')); + } + + /** + * @test + * @throws ModuleNotFoundException + */ + public function it_can_enable_pages_attached_to_module() + { + /** @var Page $page */ + $page = factory('App\Model\Page')->create(['module' => 'pictures', 'option' => PageOptions::OPTION_DISABLED]); + + Config::set('modules.pictures', ['title' => 'Pictures', 'enabled' => false]); + + $module = $this->mockModuleManager(); + + $module->enable('pictures'); + + $this->assertDatabaseHas('pages', ['id' => $page->id, 'option' => PageOptions::OPTION_DEFAULT]); + + $this->assertTrue($module->status('pictures')); + } + + /** + * @test + * + * @throws \App\Modules\ModuleNotFoundException + */ + public function it_can_disable_pages_attached_to_module() + { + /** @var Page $page */ + $page = factory('App\Model\Page')->create(['module' => 'pictures', 'option' => PageOptions::OPTION_PUBLIC]); + + Config::set('modules.pictures', ['title' => 'Pictures', 'enabled' => true]); + + $module = $this->mockModuleManager(); + + $module->disable('pictures'); + + $this->assertDatabaseHas('pages', ['id' => $page->id, 'option' => PageOptions::OPTION_PUBLIC | PageOptions::OPTION_DISABLED]); + + $this->assertFalse($module->status('pictures')); + } + + /** + * @test + * @throws ModuleNotFoundException + */ + public function it_can_enable_multiple_pages_attached_to_module() + { + /** @var Page $page */ + $pages = factory('App\Model\Page', 4)->create(['module' => 'unit-testing', 'option' => PageOptions::OPTION_DISABLED]); + + Config::set('modules.unit-testing', ['title' => 'Unit Testing', 'enabled' => false]); + + $module = $this->mockModuleManager(); + + $module->enable('unit-testing'); + + foreach ($pages as $page) { + $this->assertDatabaseHas('pages', ['id' => $page->id, 'option' => PageOptions::OPTION_DEFAULT]); + } + + $this->assertTrue($module->status('unit-testing')); + } + + /** + * @test + * @throws ModuleNotFoundException + */ + public function it_can_disable_multiple_pages_attached_to_module() + { + /** @var Page $page */ + $pages = factory('App\Model\Page', 4)->create(['module' => 'unit-testing', 'option' => PageOptions::OPTION_DEFAULT]); + + Config::set('modules.unit-testing', ['title' => 'Unit Testing', 'enabled' => true]); + + $module = $this->mockModuleManager(); + + $module->disable('unit-testing'); + + foreach ($pages as $page) { + $this->assertDatabaseHas('pages', ['id' => $page->id, 'option' => PageOptions::OPTION_DISABLED]); + } + + $this->assertFalse($module->status('unit-testing')); + } + + /** + * @test + */ + public function it_can_return_all_modules_currently_enabled() + { + $this->mockConfiguration([ + 'foo' => ['title' => 'foo', 'enabled' => true], + 'bar' => ['title' => 'bar', 'enabled' => true], + 'zor' => ['title' => 'zor', 'enabled' => false], + ]); + + $modules = $this->mockModuleManager(); + + $this->assertCount(2, $modules->getActive()); + } + + /** + * @test + */ + public function it_can_return_all_modules_currently_disabled() + { + $this->mockConfiguration([ + 'san' => ['title' => 'foo', 'enabled' => false], + 'zap' => ['title' => 'bar', 'enabled' => true], + 'lae' => ['title' => 'zor', 'enabled' => false], + ]); + + $modules = $this->mockModuleManager(); + + $this->assertCount(2, $modules->getInactive()); + } + + /** + * Mock the configuration file data. + * + * @param $array + */ + private function mockConfiguration($array) + { + return config()->set('modules', $array); + } + + /** + * Mock the module repository on its own. + * + * @return \PHPUnit\Framework\MockObject\MockBuilder + */ + private function mockModuleRepository() + { + return $this->getMockBuilder(ModuleRepository::class)->enableOriginalConstructor(); + } + + /** + * Mock the module manager. + * + * @return ModuleManager + */ + private function mockModuleManager() + { + // mock everything + $mockedClass = $this->mockModuleRepository()->setMethodsExcept(['set', 'get', 'all'])->getMock(); + + // save method will return + $mockedClass->method('save')->willReturn(true); + + /* @var ModuleRepository $mockedClass */ + return new ModuleManager($mockedClass); + } +} diff --git a/tests/Products/ProductDashboardTest.php b/tests/Products/ProductDashboardTest.php new file mode 100644 index 00000000..2c11230d --- /dev/null +++ b/tests/Products/ProductDashboardTest.php @@ -0,0 +1,31 @@ +signIn(); + + $response = $this->get('admin/products'); + + foreach (config('modules') as $module) { + $response->assertSee($module['title']); + } + + $response->assertOk(); + } +} diff --git a/tests/Products/ProductInstallationTest.php b/tests/Products/ProductInstallationTest.php new file mode 100644 index 00000000..8548ac0d --- /dev/null +++ b/tests/Products/ProductInstallationTest.php @@ -0,0 +1,31 @@ +shouldReceive('toggle')->andReturn(true)->getMock(); + + $this->app->instance(ModuleManager::class, $mocked); + + $response = $this->get('/admin/products/test/toggle'); + + $response->assertRedirect('/admin/products')->assertSee('test'); + } +}