diff --git a/.env.example b/.env.example index 69a303d4..dba86cf7 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,8 @@ -APP_ENV=testing -APP_DEBUG=true -APP_KEY=ZKZAzM23wQ6BPji6zffBjaTzZ3yW7zKQ +APP_KEY= +APP_ENV= +APP_DEBUG= -DB_HOST=localhost +DB_HOST= DB_DATABASE= DB_USERNAME= DB_PASSWORD= @@ -16,5 +16,4 @@ MAIL_PASSWORD= SITE_URL=/ UPLOAD_URL=/public/uploads -THUMBNAILS_URL=/uploads/thumbnails -THEME= \ No newline at end of file +THUMBNAILS_URL=/uploads/thumbnails \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9c93cf6e..1bfe88c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ # published files from modules -/resources/views/modules /public/modules -/resources/views/vendor /public/assets +/resources/views/modules +/resources/views/visitor +/resources/views/vendor # directories with custom templates **/views/**/custom/* diff --git a/app/Console/Commands/InstallModule.php b/app/Console/Commands/InstallModule.php index 2a9a0c7c..ad5fffc5 100644 --- a/app/Console/Commands/InstallModule.php +++ b/app/Console/Commands/InstallModule.php @@ -40,8 +40,6 @@ class InstallModule extends Command /** * Execute the console command. - * - * @return mixed */ public function handle() { @@ -56,6 +54,9 @@ class InstallModule extends Command // get config $config = $this->loadConfig(); + if (empty($config)) + return; + // check if it is valid $this->checkMandatoryFields($config); @@ -78,6 +79,9 @@ class InstallModule extends Command $basePath = $this->modulesPath . DIRECTORY_SEPARATOR . $this->moduleName . DIRECTORY_SEPARATOR; $path = $basePath . 'Config' . DIRECTORY_SEPARATOR . 'config.php'; + if (basename($basePath) == 'Database') + return []; + // check if module has Config/config.php file if (!file_exists($path)) { $path = $basePath . 'info.php'; diff --git a/app/Console/Commands/SetTemplate.php b/app/Console/Commands/SetTemplate.php new file mode 100644 index 00000000..22901daa --- /dev/null +++ b/app/Console/Commands/SetTemplate.php @@ -0,0 +1,78 @@ +argument('template'); + $publish = $this->argument('publish') !== 'no-publish'; + + $templatePath = app_path('../templates/' . $template); + if (!is_dir($templatePath) || !file_exists($templatePath . '/config.php')) { + throw new NotFoundHttpException('Invalid template ' . $template . '!'); + } + + // try to find currently installed template + $current = Module::where('is_template', true) + ->first(); + + // install template + $config = @include $templatePath . '/config.php'; + $config['staticName'] = $template; + + if ($current) { + $info = json_decode($current->module_settings); + + $this->line('Template is already installed. Currently using template ' . trans($current->name) . ' located in /templates/' . $info->staticName.'.'); + $confirm = $this->ask('Uninstall it and install *' . trans($config['name']) . '*? All data associated with installed template will be lost! [y/n]'); + + if ($confirm === 'y') { + $this->line('Uninstalling ' . $current->name . '.'); + $current->uninstall(); + } else { + $this->line('Aborting installation.'); + return; + } + } + + try { + Module::install($config['name'], $config['alias'], $config['description'], $config['handler'], false, $config); + } catch (\Exception $e) { + $this->error('Cannot install template ' . $template . '. Message: ' . $e->getMessage()); + return; + } + + // theme written + $this->line('Template successfully set up.'); + + if ($publish) { + $this->line(''); + $this->line(' ** PUBLISHING ASSETS AND VIEWS **'); + // publish assets and views for module and theme + $this->call('vendor:publish'); + $this->call('vendor:publish', ['--tag' => ['public-assets'], '--force' => true]); + $this->call('vendor:publish', ['--tag' => ['module-views'], '--force' => true]); + } + } +} diff --git a/app/Console/Commands/SetTheme.php b/app/Console/Commands/SetTheme.php deleted file mode 100644 index dd788a79..00000000 --- a/app/Console/Commands/SetTheme.php +++ /dev/null @@ -1,70 +0,0 @@ -argument('theme_name'); - - if (!is_dir(resource_path('views/visitor/' . $theme))) { - throw new NotFoundHttpException('Invalid theme ' . $theme . '!'); - } - - // load .env file - $envFile = base_path('.env'); - $env = file_get_contents($envFile); - - if (!Str::contains($env, 'THEME=')) { - $env = sprintf("%s\nTHEME=%s\n", $env, $theme); - } else { - $env = preg_replace('/THEME=(.*)/i', 'THEME=' . $theme, $env, -1, $cnt); - } - - // try to write content - $bytesWritten = file_put_contents($envFile, $env); - - // check if write was successfull - if (!$bytesWritten) { - throw new \Exception('Cannot write to ' . base_path('.env') . '. Add THEME=' . $theme . ' to your .env file manualy.'); - } - - // theme written - $this->line('Theme successfully setup.'); - $this->line(''); - $this->line(' ** PUBLISHING ASSETS AND VIEWS **'); - // publish assets and views for module and theme - $this->call('vendor:publish', ['--tag' => ['public-assets'], '--force' => true]); - $this->call('vendor:publish', ['--tag' => ['module-views'], '--force' => true]); - $this->call('vendor:publish'); - } -} diff --git a/app/Console/Commands/Translations.php b/app/Console/Commands/Translations.php index aca0693c..333cb971 100644 --- a/app/Console/Commands/Translations.php +++ b/app/Console/Commands/Translations.php @@ -38,6 +38,15 @@ class Translations extends Command if (!is_dir($defaultLocalesDir)) { // this is the place for checking for submodules + + $modulePath = dirname(dirname($dir)); + + if (file_exists($modulePath . '/info.php')) { + $modules = File::directories($modulePath); + foreach ($modules as $module) { + $this->call('translate:dir', ['dir' => $module . '/Resources/lang']); + } + } return; } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 395f776d..962299bc 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -21,7 +21,7 @@ class Kernel extends ConsoleKernel Commands\Translations::class, Commands\TranslateAll::class, Commands\InitDatabaseSchema::class, - Commands\SetTheme::class, + Commands\SetTemplate::class, AddLanguage::class ]; diff --git a/app/Handlers/Contracts/BaseModuleHandler.php b/app/Handlers/Contracts/BaseModuleHandler.php index c66e8a4e..dab8a282 100644 --- a/app/Handlers/Contracts/BaseModuleHandler.php +++ b/app/Handlers/Contracts/BaseModuleHandler.php @@ -5,7 +5,7 @@ use App\Models\User; interface BaseModuleHandler { - public static function getModuleId(); +// public static function getModuleId(); /** * Register admin menu (or multiple ones) diff --git a/app/Handlers/Contracts/TemplateHandler.php b/app/Handlers/Contracts/TemplateHandler.php new file mode 100644 index 00000000..8be4abda --- /dev/null +++ b/app/Handlers/Contracts/TemplateHandler.php @@ -0,0 +1,64 @@ + 'Article', 'templateName' => 'article::template.name'] + * ]; + * + * When no "namespace" is specified, value strtolower(module) is used instaed. + * + * @return array + */ + public static function getPublicTemplatePaths(); + + /** + * Prepare Content object to be rendered using Blade template + * + * @param Content $content + */ + public static function beforeRender(Content & $content); + + /** + * Modify input data into format which can be understood by edit template + * + * @param Content $content + */ + public static function beforePopulatingEditForm(Content & $content); + + /** + * Slightly modify Content object before saving into database. + * + * This way we can do almost whatever we want - even in template + * + * @param Content $content + * @param Request $request + */ + public static function beforeSave(Content & $content, Request $request); +} \ No newline at end of file diff --git a/app/Handlers/Registrators/TemplateRegistrator.php b/app/Handlers/Registrators/TemplateRegistrator.php index 6788df67..1d0f5257 100644 --- a/app/Handlers/Registrators/TemplateRegistrator.php +++ b/app/Handlers/Registrators/TemplateRegistrator.php @@ -187,7 +187,6 @@ class TemplateRegistrator $registeredTemplates = Templates::getWithTranslations(); if (!in_array($templateName, $registeredTemplates)) { - $params = [ 'template_name' => $templateName, 'name' => $templateName, @@ -197,8 +196,6 @@ class TemplateRegistrator 'editable' => 1 ]; - echo 'ukladam do db '.$templateName."\n"; - // save template into database $newTemplate = Template::create($params); diff --git a/app/Helpers/Template.php b/app/Helpers/Template.php index a085d384..fab3a803 100644 --- a/app/Helpers/Template.php +++ b/app/Helpers/Template.php @@ -13,7 +13,7 @@ class Template * * @return null|string */ - public static function name($template) + public static function name($template, $throwExceptions = true) { $namespace = null; if (strpos($template, '::')) { @@ -21,10 +21,8 @@ class Template $namespace .= '::'; } - $theme = config('app.theme'); - // list of prefixes - $prefixes = [$theme . '.custom.', $theme . '.', '']; + $prefixes = ['custom.', '']; $view = null; foreach ($prefixes as $prefix) { @@ -35,7 +33,11 @@ class Template } if (is_null($view)) { - throw new InvalidArgumentException('Cannot find template for ' . $namespace . $template); + if ($throwExceptions) { + throw new InvalidArgumentException('Cannot find template for ' . $namespace . $template); + } else { + return false; + } } return $view; @@ -59,6 +61,13 @@ class Template return config('app.upload_url') . '/' . $filename; } + /** + * Get path to given asset + * + * @param $path + * + * @return string + */ public static function asset($path) { if (env('APP_DEBUG')) { @@ -75,6 +84,14 @@ class Template return config('app.url') . '/assets/' . config('app.theme') . '/' . $path; } + /** + * Print date in configured format + * + * @param $datestamp + * @param bool $withTime + * + * @return false|string + */ public static function date($datestamp, $withTime = true) { $timestamp = strtotime($datestamp); diff --git a/app/Http/Controllers/Admin/ContentOverview.php b/app/Http/Controllers/Admin/ContentOverview.php index d6b95f39..f026f209 100644 --- a/app/Http/Controllers/Admin/ContentOverview.php +++ b/app/Http/Controllers/Admin/ContentOverview.php @@ -32,11 +32,16 @@ class ContentOverview extends Controller { $moduleFilterHandler = false; $perPage = Input::get('perPage') ? Input::get('perPage') : 20; + $pageHeading = 'admin/contents.overview'; // does user want to filter results using module? if (is_numeric($moduleId)) { $module = Module::findOrFail($moduleId); $moduleFilterHandler = $module->handler; + + if (is_callable([$module->handler, 'getOverviewTitle'])) { + $pageHeading = call_user_func([$module->handler, 'getOverviewTitle']); + } } // get pages for overview @@ -47,10 +52,11 @@ class ContentOverview extends Controller $homepage = $this->content->getHomepage(); return view('admin.content.overview', [ - 'pages' => $pages, - 'homepage' => $homepage, - 'statuses' => ContentStatuses::getWithTranslations(), - 'module' => isset($module) ? $module : null + 'pages' => $pages, + 'homepage' => $homepage, + 'pageHeading' => $pageHeading, + 'statuses' => ContentStatuses::getWithTranslations(), + 'module' => isset($module) ? $module : null ]); } } \ No newline at end of file diff --git a/app/Http/Controllers/Admin/CreateNewContent.php b/app/Http/Controllers/Admin/CreateNewContent.php index 1beee33b..6db38474 100644 --- a/app/Http/Controllers/Admin/CreateNewContent.php +++ b/app/Http/Controllers/Admin/CreateNewContent.php @@ -14,6 +14,7 @@ use Mcamara\LaravelLocalization\Facades\LaravelLocalization; use Modules\Categories\Entities\Category; use Pingpong\Modules\Facades\Module as ModuleInstance; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + class CreateNewContent extends Controller { /** @@ -87,6 +88,9 @@ class CreateNewContent extends Controller public function postNew(CreateNewContentRequest $request, $moduleId) { $page = new Content(); + $template = Module::where('is_template', true) + ->where('enabled', true) + ->first(); try { $module = Module::findOrFail($moduleId); } catch (NotFoundHttpException $e) { @@ -116,7 +120,13 @@ class CreateNewContent extends Controller } // upload file - if provided - $page->uploadTitlePhoto($request); + $file = $page->uploadTitlePhoto($request); + + $page->title_photo = $file ? $file['filename'] : null; + + // let template to modify request and content page + + call_user_func_array([$template->handler, 'beforeSave'], [&$page, $request]); // save module-specific stuff call_user_func_array([$module->handler, 'createNew'], [&$page, $request]); diff --git a/app/Http/Controllers/Admin/EditContent.php b/app/Http/Controllers/Admin/EditContent.php index cae69a9a..2fd85749 100644 --- a/app/Http/Controllers/Admin/EditContent.php +++ b/app/Http/Controllers/Admin/EditContent.php @@ -56,23 +56,32 @@ class EditContent extends Controller $page = Content::findOrFail($id); $page->module_settings = json_decode($page->module_settings); - $allModules = ModulesHandlerToId::getWithTranslations(); - $moduleId = $allModules[$page->module_handler]; - $module = Module::findOrFail($moduleId); + // select template + $template = Module::where('is_template', true) + ->where('enabled', true) + ->first(); + + // get module ID + $module = Module::where('handler', $page->module_handler)->firstOrFail(); - $templates = Template::where('module_id', $moduleId) + // get all Blade templates for rendering page + $templates = Template::where('module_id', $module->id) ->select(['template_name', 'name', 'description']) ->get(); + // get module for Categories - PingPong module $categoryModule = ModuleInstance::get('categories'); // add categories, if supported $categories = []; if ($module->support_categories && $categoryModule->enabled()) { - $categories = Category::where('module_id', $moduleId) + $categories = Category::where('module_id', $module->id) ->get(); } + // modify data for edit form according to installed template + call_user_func_array([$template->handler, 'beforePopulatingEditForm'], [&$page]); + $history = $this->history->getStructuredHistoryOf($page); // render view @@ -107,6 +116,10 @@ class EditContent extends Controller * @var $page Content */ $page = Content::findOrFail($id); + + $template = Module::where('is_template', true) + ->where('enabled', true) + ->first(); } catch (NotFoundHttpException $e) { // log attempt of invalid edit @@ -118,12 +131,17 @@ class EditContent extends Controller ->with('error', trans('admin/contents.invalidPage')); } + // upload file - if provided + $file = $page->uploadTitlePhoto($request); + + // let template to modify request and content page + call_user_func_array([$template->handler, 'beforeSave'], [&$page, $request]); + $request->url = Str::slug($request->url); $request->parentPage = $request->parentPage ? $request->parentPage : null; - $request->titlePhotoFilename = $page->title_photo; - // upload file - if provided - $page->uploadTitlePhoto($request); + $request->titlePhotoFilename = $file ? $file['filename'] : null; + // array of fields in database and fields from request $fields = [ @@ -142,7 +160,8 @@ class EditContent extends Controller ]; // save new values with history - $data = self::getFields($request, $fields); + $data = self::getFields($request, array_values($fields)); + list($revision, $changedColumns) = $page->updateWithHistory($fields, $data); $changedColumns += call_user_func_array([$page->module_handler, 'edit'], [&$page, $request, $revision]); diff --git a/app/Http/Controllers/Admin/EditTemplate.php b/app/Http/Controllers/Admin/EditTemplate.php index e7995dc5..d5256c01 100644 --- a/app/Http/Controllers/Admin/EditTemplate.php +++ b/app/Http/Controllers/Admin/EditTemplate.php @@ -89,7 +89,7 @@ class EditTemplate extends Controller } // save new content only if something was changed - if ($this->isContentDifferent($originalTemplate, $customTemplate, $request->templateContent)) { + if ($template->editable && $this->isContentDifferent($originalTemplate, $customTemplate, $request->templateContent)) { // create directory for custom updates if (!is_dir($customDir)) { diff --git a/app/Http/Controllers/Admin/EditWidget.php b/app/Http/Controllers/Admin/EditWidget.php index 61cf8fd6..200329f9 100644 --- a/app/Http/Controllers/Admin/EditWidget.php +++ b/app/Http/Controllers/Admin/EditWidget.php @@ -79,6 +79,13 @@ class EditWidget extends Controller // create new widget $widget = Widget::findOrFail($id); + // check if widget area is deletable + if (!$widget->deletable) { + return redirect() + ->back() + ->with('error', trans('admin/widgets.notDeletable')); + } + $widget->delete(); // redirect with success notification diff --git a/app/Http/Controllers/Admin/EditWidgetArea.php b/app/Http/Controllers/Admin/EditWidgetArea.php index b163308d..7f960984 100644 --- a/app/Http/Controllers/Admin/EditWidgetArea.php +++ b/app/Http/Controllers/Admin/EditWidgetArea.php @@ -3,7 +3,6 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; -use App\Http\Requests; use App\Http\Requests\EditWidgetAreaRequest; use App\Models\WidgetArea; use Illuminate\Database\Eloquent\ModelNotFoundException; @@ -61,6 +60,13 @@ class EditWidgetArea extends Controller return $this->invalidArea(); } + // check if widget area is deletable + if (!$area->deletable) { + return redirect() + ->back() + ->with('error', trans('admin/widgetAreas.notDeletableArea')); + } + // delete area $area->delete(); diff --git a/app/Http/Controllers/Visitor/ContentPage.php b/app/Http/Controllers/Visitor/ContentPage.php index 1ea86abb..4b66d127 100644 --- a/app/Http/Controllers/Visitor/ContentPage.php +++ b/app/Http/Controllers/Visitor/ContentPage.php @@ -5,6 +5,8 @@ namespace App\Http\Controllers\Visitor; use App\Http\Controllers\Controller; use App\Http\Requests; use App\Models\Content; +use App\Models\Module; +use App\Models\Template; use Illuminate\Support\Facades\Auth; class ContentPage extends Controller @@ -53,9 +55,17 @@ class ContentPage extends Controller // add one new "view" for page $page->view(); + // decode module settings + $page->module_settings = json_decode($page->module_settings); + // get handler $handler = new $page->module_handler; + $template = Module::where('is_template', true)->where('enabled', true)->first(); + + // do something template-specific + call_user_func_array([$template->handler, 'beforeRender'], [&$page]); + // render page return $handler->render($page, ''); } diff --git a/app/Http/Requests/EditTemplateRequest.php b/app/Http/Requests/EditTemplateRequest.php index 63b35b07..aedf05af 100644 --- a/app/Http/Requests/EditTemplateRequest.php +++ b/app/Http/Requests/EditTemplateRequest.php @@ -12,8 +12,7 @@ class EditTemplateRequest extends Request public function rules() { return [ - 'name' => 'required|max:128', - 'templateContent' => 'required' + 'name' => 'required|max:128', ]; } } diff --git a/app/Models/Content.php b/app/Models/Content.php index 19128cfe..6bd88997 100644 --- a/app/Models/Content.php +++ b/app/Models/Content.php @@ -293,10 +293,10 @@ class Content extends Model public function uploadTitlePhoto(Request $request) { if ($request->hasFile('titlePhoto')) { - $file = $this->file->upload($request->file('titlePhoto'), $request); - - $this->title_photo = $file['filename']; + return $this->file->upload($request->file('titlePhoto'), $request); } + + return null; } /** diff --git a/app/Models/Module.php b/app/Models/Module.php index d782c2d9..82cd681e 100644 --- a/app/Models/Module.php +++ b/app/Models/Module.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Handlers\Contracts\TemplateHandler; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\App; @@ -20,7 +21,8 @@ class Module extends Model 'module_settings', 'enabled', 'support_categories', - 'manages_content' + 'manages_content', + 'is_template' ]; protected $guarded = []; @@ -45,6 +47,7 @@ class Module extends Model } $managesContent = $handler ? in_array('App\Handlers\Contracts\AppHandler', class_implements($handler)) : false; + $isTemplate = $handler ? in_array(TemplateHandler::class, class_implements($handler)) : false; $supportCategories = $managesContent ? call_user_func([$handler, 'supportCategories']) : false; if ($parentModuleId) { @@ -62,8 +65,9 @@ class Module extends Model 'support_categories' => $supportCategories, 'alias' => $alias, 'module_settings' => json_encode($settings), - 'parent_id' => $parentModuleId, - 'enabled' => 1 + 'parent_id' => $parentModuleId ? $parentModuleId : null, + 'enabled' => 1, + 'is_template' => $isTemplate ]); // install something module-specific @@ -73,4 +77,51 @@ class Module extends Model return $module; } + + /** + * Delete module from database and uninstall its dependencies + * + * @throws \ErrorException + */ + public function uninstall() + { + // check if user does not want to install module in runtime + if (!App::runningInConsole()) { + throw new \ErrorException('Modules can be uninstalled only via Artisan!'); + } + + // delete everything in /resources/views/visitor + if ($this->is_template) { + $this->deleteDirectory(resource_path('views/visitor')); + $this->deleteDirectory(public_path('assets')); + + // delete all template paths + TemplatePath::getQuery()->delete(); + } + + if ($this->handler && is_callable([$this->handler, 'uninstall'])) { + call_user_func([$this->handler, 'uninstall'], $this); + } + + $this->delete(); + } + + /** + * Delete directory when uninstalling module. + * + * @param $directory + * + * @throws \ErrorException + */ + private function deleteDirectory($directory) + { + if (!is_dir($directory)) + return; + + $success = \File::deleteDirectory($directory, true); + + if (!$success) { + throw new \ErrorException('Cannot delete ' . $directory); + } + } } \ No newline at end of file diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index fe7104a6..20fb3f5c 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -30,8 +30,6 @@ class AppServiceProvider extends ServiceProvider { // ignore running from artisan if (App::runningInConsole()) { - $this->publishViewResources(); - return; } @@ -39,13 +37,6 @@ class AppServiceProvider extends ServiceProvider $this->registerTemplates(); $this->registerSettings(); $this->registerSearch(); - - // check if given template is valid - $theme = config('app.theme'); - $themeDir = resource_path('views/visitor/' . $theme); - if (!$theme || empty($theme) || !is_dir($themeDir)) { - throw new InvalidArgumentException('No or invalid theme set! Use artisan theme:set or edit .env file manually and include THEME option, please'); - } } /** @@ -120,10 +111,10 @@ class AppServiceProvider extends ServiceProvider /** * @var $registrator TemplateRegistrator */ - $registrator = $this->app->make('registerTemplateDir'); - $registrator->setBaseDirectory('resources/views/visitor'); +// $registrator = $this->app->make('registerTemplateDir'); +// $registrator->setBaseDirectory('resources/views/visitor'); - $registrator->register(config('app.theme'), 'admin/templates.core'); +// $registrator->register(config('app.theme'), 'admin/templates.core'); } /** @@ -150,37 +141,5 @@ class AppServiceProvider extends ServiceProvider $registrator->register(new Files(), new AdminFiles(), SearchEngineRegistrator::ADMIN); } - /** - * Publish all public assets from all themes - */ - private function publishViewResources() - { - $theme = config('app.theme'); - $themeDir = resource_path('views/visitor/' . $theme); - // publish assets if exists for current theme - if (is_dir($themeDir . '/' . Template::ASSETS_DIRECTORY)) { - $this->publishes([$themeDir . '/' . Template::ASSETS_DIRECTORY => public_path('assets/' . $theme)], 'public-assets'); - } - - if (is_dir($themeDir . '/' . Template::MODULES_DIRECTORY)) { - $this->publishModuleViews($themeDir . '/' . Template::MODULES_DIRECTORY); - } - } - - private function publishModuleViews($themeModules) - { - $theme = config('app.theme'); - $modules = File::directories($themeModules); - - $moduleViews = []; - foreach ($modules as $module) { - $moduleName = strtolower(basename($module)); - $moduleViews[$module] = resource_path('views/modules/' . $moduleName . '/visitor/' . $theme); - } - - if (!empty($moduleViews)) { - $this->publishes($moduleViews, 'module-views'); - } - } } diff --git a/app/Providers/GlobalViewDataProvider.php b/app/Providers/GlobalViewDataProvider.php index c0c92845..3b7193b2 100644 --- a/app/Providers/GlobalViewDataProvider.php +++ b/app/Providers/GlobalViewDataProvider.php @@ -73,6 +73,14 @@ class GlobalViewDataProvider extends ServiceProvider return preg_replace($pattern, $code, $view); }); + // shortcut for displaying Gravatar + Blade::extend(function ($view) { + $pattern = $this->createPattern('gravatar'); + $code = ''; + + return preg_replace($pattern, $code, $view); + }); + // shortcut for assets (placed in /public/assets/ Blade::extend(function ($view) { $pattern = $this->createPattern('asset'); diff --git a/app/Providers/LaravelLocalizationServiceProvider.php b/app/Providers/LaravelLocalizationServiceProvider.php index 30b153dc..94f761fc 100644 --- a/app/Providers/LaravelLocalizationServiceProvider.php +++ b/app/Providers/LaravelLocalizationServiceProvider.php @@ -5,18 +5,13 @@ use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\DB; use Illuminate\Support\ServiceProvider; use Mcamara\LaravelLocalization\Facades\LaravelLocalization; -use Symfony\Component\Translation\Translator; class LaravelLocalizationServiceProvider extends ServiceProvider { public function register() { - try { - $languages = DB::table('languages') - ->get(); - } catch (\Exception $e) { - $languages = []; - } + $languages = DB::table('languages') + ->get(); $supportedLocales = []; if (empty($languages)) { @@ -28,11 +23,7 @@ class LaravelLocalizationServiceProvider extends ServiceProvider ]; // create default language - try { - Language::create($supportedLocales[config('app.locale')]); - } catch (\Exception $e) { - // ignore, table languages probably does not exist. - } + Language::create($supportedLocales[config('app.locale')]); } foreach ($languages as $language) { diff --git a/app/Providers/ModuleManager.php b/app/Providers/ModuleManager.php index dff7e0be..f251d23e 100644 --- a/app/Providers/ModuleManager.php +++ b/app/Providers/ModuleManager.php @@ -15,14 +15,15 @@ class ModuleManager extends ServiceProvider */ public function boot() { - // ignore running from artisan - if (App::runningInConsole()) { + // this service provider needs published config - config is merged right after this service provider + try { + $modules = Module::where('is_template', false) + ->get(); + } catch (\Exception $e) { + // ignore error return; } - // this service provider needs published config - config is merged right after this service provider - $modules = Module::get(); - // iterate over all modules and enable or disable them foreach ($modules as $module) { $this->createModulesJson($module); @@ -58,7 +59,7 @@ class ModuleManager extends ServiceProvider // check if module config is writable if (!is_writable(dirname($path))) { - Log::warning('Cannot write to '.$path); + Log::warning('Cannot write to ' . $path); return; } diff --git a/app/Providers/TemplateServiceProvider.php b/app/Providers/TemplateServiceProvider.php new file mode 100644 index 00000000..d4ff4457 --- /dev/null +++ b/app/Providers/TemplateServiceProvider.php @@ -0,0 +1,122 @@ +where('enabled', true) + ->first(); + + if ($template) { + $template->settings = json_decode($template->module_settings); + } + } catch (\Exception $e) { + return null; + } + + return $template; + } + + public function boot() + { + // ignore running from artisan + if (App::runningInConsole()) { + $this->publishViewResources(); + + return; + } + + $template = $this->getTemplate(); + + if (!$template) { + throw new \ErrorException('Template is not set, cannot continue'); + } + + // include files + $files = isset($template->settings->files) ? $template->settings->files : []; + + foreach ($files as $file) { + include_once $file; + } + + // register templates + $publicTemplates = call_user_func([$template->handler, 'getPublicTemplatePaths']); + + if (!is_array($publicTemplates)) { + throw new \ErrorException($template->handler . '::getPublicTemplatePaths() must return array!'); + } + + if (empty($publicTemplates)) { + return; + } + + /** + * @var $registrator TemplateRegistrator + */ + $registrator = $this->app->make('registerTemplateDir'); + foreach ($publicTemplates as $template) { + // setup some global variables + $module = strtolower($template['module']); + $namespace = isset($template['namespace']) ? $template['namespace'] : strtolower($template['module']); + try { + $registrator->setBaseDirectory('resources/views/modules/' . $module); + $registrator->setNamespace($namespace); + $registrator->setModuleId(call_user_func([config($module . '.handler'), 'getModuleId'])); + + // register path with current template name + $registrator->register('visitor', $template['templateName']); + } catch (InvalidArgumentException $e) { + // ignore this exception + } + } + } + + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + } + + /** + * Publish all public assets from all themes + */ + private function publishViewResources() + { + $template = $this->getTemplate(); + if (!$template) { + return; + } + + $templateDir = app_path('../templates/' . $template->settings->staticName . '/Resources'); + $assetsDir = $templateDir . '/' . Template::ASSETS_DIRECTORY; + $viewsDir = $templateDir . '/views'; + + // publish assets if exists for current theme + if (is_dir($assetsDir)) { + $this->publishes([$assetsDir => public_path('assets/visitor')], 'public-assets'); + } + + // publish base visitors views + $this->publishes([$viewsDir . '/visitor' => resource_path('views/visitor')], 'module-views'); + + // publish admin views + if (is_dir($viewsDir . '/modules')) { + $this->publishes([$viewsDir . '/modules' => resource_path('views/modules')], 'module-views'); + } + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index 69031af8..2a77208a 100644 --- a/composer.json +++ b/composer.json @@ -30,16 +30,11 @@ ], "psr-4": { "App\\": "app/", - "Modules\\": "modules/" + "Modules\\": "modules/", + "Templates\\": "templates/" } }, "scripts": { - "post-root-package-install": [ - "php -r \"copy('.env.example', '.env');\"" - ], - "post-create-project-cmd": [ - "php artisan key:generate" - ], "post-install-cmd": [ "php artisan clear-compiled", "php artisan optimize" diff --git a/config/app.php b/config/app.php index dd7ce301..49291806 100644 --- a/config/app.php +++ b/config/app.php @@ -129,49 +129,49 @@ return [ 'providers' => [ - /* - * Laravel Framework Service Providers... - */ - Illuminate\Auth\AuthServiceProvider::class, - Illuminate\Broadcasting\BroadcastServiceProvider::class, - Illuminate\Bus\BusServiceProvider::class, - Illuminate\Cache\CacheServiceProvider::class, - Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, - Illuminate\Cookie\CookieServiceProvider::class, - Illuminate\Database\DatabaseServiceProvider::class, - Illuminate\Encryption\EncryptionServiceProvider::class, - Illuminate\Filesystem\FilesystemServiceProvider::class, - Illuminate\Foundation\Providers\FoundationServiceProvider::class, - Illuminate\Hashing\HashServiceProvider::class, - Illuminate\Mail\MailServiceProvider::class, - Illuminate\Pagination\PaginationServiceProvider::class, - Illuminate\Pipeline\PipelineServiceProvider::class, - Illuminate\Queue\QueueServiceProvider::class, - Illuminate\Redis\RedisServiceProvider::class, - Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, - Illuminate\Session\SessionServiceProvider::class, - Illuminate\Translation\TranslationServiceProvider::class, - Illuminate\Validation\ValidationServiceProvider::class, - Illuminate\View\ViewServiceProvider::class, - Mcamara\LaravelLocalization\LaravelLocalizationServiceProvider::class, - // this class must be before ModulesServiceProvider from PingPong, because it handles files module.json - App\Providers\ModuleManager::class, - Pingpong\Modules\ModulesServiceProvider::class, - Pingpong\Menus\MenusServiceProvider::class, - App\Providers\SubmodulesManager::class, + /* + * Laravel Framework Service Providers... + */ + Illuminate\Auth\AuthServiceProvider::class, + Illuminate\Broadcasting\BroadcastServiceProvider::class, + Illuminate\Bus\BusServiceProvider::class, + Illuminate\Cache\CacheServiceProvider::class, + Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, + Illuminate\Cookie\CookieServiceProvider::class, + Illuminate\Database\DatabaseServiceProvider::class, + Illuminate\Encryption\EncryptionServiceProvider::class, + Illuminate\Filesystem\FilesystemServiceProvider::class, + Illuminate\Foundation\Providers\FoundationServiceProvider::class, + Illuminate\Hashing\HashServiceProvider::class, + Illuminate\Mail\MailServiceProvider::class, + Illuminate\Pagination\PaginationServiceProvider::class, + Illuminate\Pipeline\PipelineServiceProvider::class, + Illuminate\Queue\QueueServiceProvider::class, + Illuminate\Redis\RedisServiceProvider::class, + Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, + Illuminate\Session\SessionServiceProvider::class, + Illuminate\Translation\TranslationServiceProvider::class, + Illuminate\Validation\ValidationServiceProvider::class, + Illuminate\View\ViewServiceProvider::class, + Mcamara\LaravelLocalization\LaravelLocalizationServiceProvider::class, + // this class must be before ModulesServiceProvider from PingPong, because it handles files module.json + App\Providers\ModuleManager::class, + Pingpong\Modules\ModulesServiceProvider::class, + Pingpong\Menus\MenusServiceProvider::class, + App\Providers\SubmodulesManager::class, + Franzose\ClosureTable\ClosureTableServiceProvider::class, + \Watson\Sitemap\SitemapServiceProvider::class, + /* + * Application Service Providers... + */ + App\Providers\AppServiceProvider::class, + App\Providers\AuthServiceProvider::class, + App\Providers\EventServiceProvider::class, + App\Providers\RouteServiceProvider::class, + App\Providers\GlobalViewDataProvider::class, + \App\Providers\TemplateServiceProvider::class, - Franzose\ClosureTable\ClosureTableServiceProvider::class, - \Watson\Sitemap\SitemapServiceProvider::class, - /* - * Application Service Providers... - */ - App\Providers\AppServiceProvider::class, - App\Providers\AuthServiceProvider::class, - App\Providers\EventServiceProvider::class, - App\Providers\RouteServiceProvider::class, - App\Providers\GlobalViewDataProvider::class, - -], + ], /* |-------------------------------------------------------------------------- | Class Aliases @@ -185,41 +185,41 @@ return [ 'aliases' => [ - 'App' => Illuminate\Support\Facades\App::class, - 'Artisan' => Illuminate\Support\Facades\Artisan::class, - 'Auth' => Illuminate\Support\Facades\Auth::class, - 'Blade' => Illuminate\Support\Facades\Blade::class, - 'Cache' => Illuminate\Support\Facades\Cache::class, - 'Config' => Illuminate\Support\Facades\Config::class, - 'Cookie' => Illuminate\Support\Facades\Cookie::class, - 'Crypt' => Illuminate\Support\Facades\Crypt::class, - 'DB' => Illuminate\Support\Facades\DB::class, - 'Eloquent' => Illuminate\Database\Eloquent\Model::class, - 'Event' => Illuminate\Support\Facades\Event::class, - 'File' => Illuminate\Support\Facades\File::class, - 'Gate' => Illuminate\Support\Facades\Gate::class, - 'Hash' => Illuminate\Support\Facades\Hash::class, - 'Lang' => Illuminate\Support\Facades\Lang::class, - 'Log' => Illuminate\Support\Facades\Log::class, - 'Mail' => Illuminate\Support\Facades\Mail::class, - 'Menu' => Pingpong\Menus\MenuFacade::class, - 'Module' => Pingpong\Modules\Facades\Module::class, - 'Password' => Illuminate\Support\Facades\Password::class, - 'Queue' => Illuminate\Support\Facades\Queue::class, - 'Redirect' => Illuminate\Support\Facades\Redirect::class, - 'Redis' => Illuminate\Support\Facades\Redis::class, - 'Request' => Illuminate\Support\Facades\Request::class, - 'Response' => Illuminate\Support\Facades\Response::class, - 'Route' => Illuminate\Support\Facades\Route::class, - 'Schema' => Illuminate\Support\Facades\Schema::class, - 'Session' => Illuminate\Support\Facades\Session::class, - 'Sitemap' => Watson\Sitemap\Facades\Sitemap::class, - 'Storage' => Illuminate\Support\Facades\Storage::class, - 'URL' => Illuminate\Support\Facades\URL::class, - 'Validator' => Illuminate\Support\Facades\Validator::class, - 'LaravelLocalization' => Mcamara\LaravelLocalization\Facades\LaravelLocalization::class, - 'View' => Illuminate\Support\Facades\View::class, + 'App' => Illuminate\Support\Facades\App::class, + 'Artisan' => Illuminate\Support\Facades\Artisan::class, + 'Auth' => Illuminate\Support\Facades\Auth::class, + 'Blade' => Illuminate\Support\Facades\Blade::class, + 'Cache' => Illuminate\Support\Facades\Cache::class, + 'Config' => Illuminate\Support\Facades\Config::class, + 'Cookie' => Illuminate\Support\Facades\Cookie::class, + 'Crypt' => Illuminate\Support\Facades\Crypt::class, + 'DB' => Illuminate\Support\Facades\DB::class, + 'Eloquent' => Illuminate\Database\Eloquent\Model::class, + 'Event' => Illuminate\Support\Facades\Event::class, + 'File' => Illuminate\Support\Facades\File::class, + 'Gate' => Illuminate\Support\Facades\Gate::class, + 'Hash' => Illuminate\Support\Facades\Hash::class, + 'Lang' => Illuminate\Support\Facades\Lang::class, + 'Log' => Illuminate\Support\Facades\Log::class, + 'Mail' => Illuminate\Support\Facades\Mail::class, + 'Menu' => Pingpong\Menus\MenuFacade::class, + 'Module' => Pingpong\Modules\Facades\Module::class, + 'Password' => Illuminate\Support\Facades\Password::class, + 'Queue' => Illuminate\Support\Facades\Queue::class, + 'Redirect' => Illuminate\Support\Facades\Redirect::class, + 'Redis' => Illuminate\Support\Facades\Redis::class, + 'Request' => Illuminate\Support\Facades\Request::class, + 'Response' => Illuminate\Support\Facades\Response::class, + 'Route' => Illuminate\Support\Facades\Route::class, + 'Schema' => Illuminate\Support\Facades\Schema::class, + 'Session' => Illuminate\Support\Facades\Session::class, + 'Sitemap' => Watson\Sitemap\Facades\Sitemap::class, + 'Storage' => Illuminate\Support\Facades\Storage::class, + 'URL' => Illuminate\Support\Facades\URL::class, + 'Validator' => Illuminate\Support\Facades\Validator::class, + 'LaravelLocalization' => Mcamara\LaravelLocalization\Facades\LaravelLocalization::class, + 'View' => Illuminate\Support\Facades\View::class, -], + ], ]; diff --git a/database/migrations/2016_09_19_192132_SaveTemplatesIntoModulesTable.php b/database/migrations/2016_09_19_192132_SaveTemplatesIntoModulesTable.php new file mode 100644 index 00000000..2b5b484b --- /dev/null +++ b/database/migrations/2016_09_19_192132_SaveTemplatesIntoModulesTable.php @@ -0,0 +1,32 @@ +boolean('is_template')->default(false)->comment('Templates can be treated as modules.'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('modules', function (Blueprint $table){ + $table->dropColumn('is_template'); + }); + } +} diff --git a/database/migrations/2016_09_20_113806_AddTemplateSupportToWidgets.php b/database/migrations/2016_09_20_113806_AddTemplateSupportToWidgets.php new file mode 100644 index 00000000..ff448074 --- /dev/null +++ b/database/migrations/2016_09_20_113806_AddTemplateSupportToWidgets.php @@ -0,0 +1,55 @@ +integer('template_id') + ->comment('Commes this widget with template?') + ->after('handler') + ->nullable(true) + ->default(null); + $table->integer('module_id') + ->comment('Commes this widget with some module?') + ->after('template_id') + ->nullable(true) + ->default(null); + + $table->foreign('template_id') + ->references('id') + ->on('modules') + ->onDelete('cascade') + ->onUpdate('cascade'); + + $table->foreign('module_id') + ->references('id') + ->on('modules') + ->onDelete('cascade') + ->onUpdate('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('widget_types', function (Blueprint $table) { + $table->dropForeign('widget_types_module_id_foreign'); + $table->dropForeign('widget_types_template_id_foreign'); + $table->dropColumn(['template_id', 'module_id']); + }); + } +} diff --git a/database/migrations/2016_09_20_114930_MakeWidgetsAndWidgetAreasNotDeletable.php b/database/migrations/2016_09_20_114930_MakeWidgetsAndWidgetAreasNotDeletable.php new file mode 100644 index 00000000..b68636cf --- /dev/null +++ b/database/migrations/2016_09_20_114930_MakeWidgetsAndWidgetAreasNotDeletable.php @@ -0,0 +1,44 @@ +boolean('deletable') + ->default(true) + ->comment('Can this widget be deleted?'); + }); + + Schema::table('widget_areas', function (Blueprint $table) { + $table->boolean('deletable') + ->default(true) + ->comment('Can this widget area be deleted?'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('widgets', function (Blueprint $table) { + $table->dropColumn('deletable'); + }); + + Schema::table('widget_areas', function (Blueprint $table) { + $table->dropColumn('deletable'); + }); + } +} diff --git a/database/schema.sql b/database/schema.sql index 46ad3ef6..1f04812a 100644 --- a/database/schema.sql +++ b/database/schema.sql @@ -1,364 +1,712 @@ --- MySQL Script generated by MySQL Workbench --- Út 26. leden 2016, 22:44:34 CET --- Model: New Model Version: 1.0 --- MySQL Workbench Forward Engineering +-- MySQL dump 10.16 Distrib 10.1.17-MariaDB, for Linux (x86_64) +-- +-- Host: localhost Database: minicms +-- ------------------------------------------------------ +-- Server version 10.1.17-MariaDB -SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +SET NAMES utf8; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; -SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES'; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; --- ----------------------------------------------------- --- Schema minicms --- ----------------------------------------------------- +-- +-- Table structure for table `action_log` +-- --- ----------------------------------------------------- --- Table `users` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `users` ( - `id` INT NOT NULL AUTO_INCREMENT, - `email` VARCHAR(64) NOT NULL COMMENT 'E-mail is used as a contact and also as a username', - `username` VARCHAR(64) NOT NULL COMMENT 'Username can be used also for login. Default value is users email.', - `password` VARCHAR(128) NOT NULL COMMENT 'Password hash', - `role` ENUM('visitor', 'editor', 'admin') NOT NULL DEFAULT 'visitor' COMMENT 'User\'s role determines his permissions in the system.', - `remember_token` VARCHAR(128) NULL DEFAULT NULL COMMENT 'Token used for \"remember me\" feature', - `name` VARCHAR(128) NULL DEFAULT NULL, - `status` ENUM('deleted', 'blocked', 'active') NOT NULL DEFAULT 'active', +DROP TABLE IF EXISTS `action_log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `action_log` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `type_id` int(11) NOT NULL, + `user_id` int(11) NOT NULL, + `ip` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'User''s IP Address', + `user_agent` mediumtext COLLATE utf8_unicode_ci NOT NULL COMMENT 'Browser string of client, who performed some action.', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `parameters` mediumtext COLLATE utf8_unicode_ci COMMENT 'JSON serialized array/object passed into translation.', PRIMARY KEY (`id`), - UNIQUE INDEX `email_UNIQUE` (`email` ASC)) -ENGINE = InnoDB -COMMENT = 'Table with users who are allowed to log into system'; + KEY `fk_action_log_action_log_type1_idx` (`type_id`), + KEY `fk_action_log_user1_idx` (`user_id`), + CONSTRAINT `fk_action_log_action_log_type1` FOREIGN KEY (`type_id`) REFERENCES `action_log_types` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT `fk_action_log_user1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION +) ENGINE=InnoDB AUTO_INCREMENT=284 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='This table contains log of all actions, which happened in administration.'; +/*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `action_log_types` +-- --- ----------------------------------------------------- --- Table `modules` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `modules` ( - `handler` VARCHAR(255) NOT NULL, - `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `name` VARCHAR(64) NOT NULL, - `description` TEXT NULL DEFAULT NULL, - PRIMARY KEY (`handler`)) -ENGINE = InnoDB; - - --- ----------------------------------------------------- --- Table `files` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `files` ( - `id` INT NOT NULL AUTO_INCREMENT, - `user_id` INT NOT NULL COMMENT 'User, who uploaded this file', - `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `filename` VARCHAR(128) NOT NULL, - `sha256_hash` VARCHAR(64) NOT NULL COMMENT 'SHA hash of file content.', - `mime_type` VARCHAR(64) NOT NULL COMMENT 'MIME type of uploaded file', +DROP TABLE IF EXISTS `action_log_types`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `action_log_types` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `string_id` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT 'String ID used for better readability in loggers', + `name` varchar(128) COLLATE utf8_unicode_ci NOT NULL, + `value` varchar(128) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Log content - represented as translation string.', PRIMARY KEY (`id`), - INDEX `fk_file_user1_idx` (`user_id` ASC), - CONSTRAINT `fk_file_user1` - FOREIGN KEY (`user_id`) - REFERENCES `users` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB -COMMENT = 'Table containing files which were uploaded into system.'; + UNIQUE KEY `action_log_types_string_id_unique` (`string_id`) +) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='List of all types which can be logged.'; +/*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `categories` +-- --- ----------------------------------------------------- --- Table `contents` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `contents` ( - `id` INT NOT NULL AUTO_INCREMENT, - `user_id` INT NOT NULL, - `module_handler` VARCHAR(255) NOT NULL, - `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `published_from` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, - `published_to` TIMESTAMP NULL DEFAULT NULL, - `title_photo` INT NULL DEFAULT NULL, - `title` VARCHAR(64) NOT NULL, - `url` VARCHAR(64) NOT NULL, - `meta_keywords` VARCHAR(128) NULL DEFAULT NULL, - `meta_description` MEDIUMTEXT NULL DEFAULT NULL, - `content` TEXT NULL DEFAULT NULL, - `module_settings` TEXT NULL COMMENT 'JSON serialized object passed as settings to the module.', - `status` ENUM('deleted', 'draft', 'protected', 'public') NULL DEFAULT 'draft' COMMENT 'This photo is supposed to be displayed e.g. on list of all pages.', +DROP TABLE IF EXISTS `categories`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `categories` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `deleted_at` timestamp NULL DEFAULT NULL, + `parent_id` int(10) unsigned DEFAULT NULL, + `position` int(10) unsigned NOT NULL, + `real_depth` int(10) unsigned NOT NULL, + `user_id` int(11) NOT NULL COMMENT 'Author of this category', + `module_id` int(11) NOT NULL COMMENT 'ID of module, where this category is used', + `name` varchar(255) NOT NULL COMMENT 'Human readable name of category', + `url` varchar(255) NOT NULL COMMENT 'URL slug of category', + `description` text NOT NULL, PRIMARY KEY (`id`), - INDEX `fk_content_user1_idx` (`user_id` ASC), - INDEX `url` (`url` ASC), - FULLTEXT INDEX `content_fulltext` (`content` ASC), - UNIQUE INDEX `url_UNIQUE` (`url` ASC), - INDEX `fk_content_module1_idx` (`module_handler` ASC), - INDEX `fk_content_file1_idx` (`title_photo` ASC), - CONSTRAINT `fk_content_user1` - FOREIGN KEY (`user_id`) - REFERENCES `users` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION, - CONSTRAINT `fk_content_module1` - FOREIGN KEY (`module_handler`) - REFERENCES `modules` (`handler`) - ON DELETE NO ACTION - ON UPDATE NO ACTION, - CONSTRAINT `fk_content_file1` - FOREIGN KEY (`title_photo`) - REFERENCES `files` (`id`) - ON DELETE SET NULL - ON UPDATE CASCADE) -ENGINE = InnoDB -COMMENT = 'Table with all content pages.'; + UNIQUE KEY `categories_url_module_id_unique` (`url`,`module_id`), + KEY `categories_url_index` (`url`), + KEY `categories_parent_id_foreign` (`parent_id`), + KEY `categories_user_id_foreign` (`user_id`), + KEY `categories_module_id_foreign` (`module_id`), + CONSTRAINT `categories_module_id_foreign` FOREIGN KEY (`module_id`) REFERENCES `modules` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `categories_parent_id_foreign` FOREIGN KEY (`parent_id`) REFERENCES `categories` (`id`) ON DELETE SET NULL, + CONSTRAINT `categories_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `category_closure` +-- --- ----------------------------------------------------- --- Table `content_history` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `content_history` ( - `id` INT NOT NULL AUTO_INCREMENT, - `user_id` INT NOT NULL, - `content_id` INT NOT NULL, - `changed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `edit_batch` VARCHAR(16) NOT NULL COMMENT 'This field holds identical values for all historical entries edited as one update.', - `column` VARCHAR(64) NOT NULL, - `new_value` TEXT NULL DEFAULT NULL, - `old_value` TEXT NULL DEFAULT NULL, +DROP TABLE IF EXISTS `category_closure`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `category_closure` ( + `closure_id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `ancestor` int(10) unsigned NOT NULL, + `descendant` int(10) unsigned NOT NULL, + `depth` int(10) unsigned NOT NULL, + PRIMARY KEY (`closure_id`), + KEY `category_closure_ancestor_foreign` (`ancestor`), + KEY `category_closure_descendant_foreign` (`descendant`), + CONSTRAINT `category_closure_ancestor_foreign` FOREIGN KEY (`ancestor`) REFERENCES `categories` (`id`) ON DELETE CASCADE, + CONSTRAINT `category_closure_descendant_foreign` FOREIGN KEY (`descendant`) REFERENCES `categories` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `comments` +-- + +DROP TABLE IF EXISTS `comments`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `comments` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `user_id` int(11) DEFAULT NULL COMMENT 'When comment was inserted by logged user, save his ID here', + `content_id` int(11) NOT NULL, + `email` varchar(255) NOT NULL, + `name` varchar(255) NOT NULL, + `content` text NOT NULL, + `approved` tinyint(1) NOT NULL, PRIMARY KEY (`id`), - INDEX `edit_batch` (`edit_batch` ASC), - INDEX `column` (`column` ASC), - INDEX `fk_content_history_users_idx` (`user_id` ASC), - INDEX `fk_content_history_content1_idx` (`content_id` ASC), - CONSTRAINT `fk_content_history_users` - FOREIGN KEY (`user_id`) - REFERENCES `users` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION, - CONSTRAINT `fk_content_history_content1` - FOREIGN KEY (`content_id`) - REFERENCES `contents` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB -COMMENT = 'Table for holding history of changes on some content.'; + KEY `comments_content_id_foreign` (`content_id`), + KEY `comments_user_id_foreign` (`user_id`), + CONSTRAINT `comments_content_id_foreign` FOREIGN KEY (`content_id`) REFERENCES `contents` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `comments_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `content_has_custom_fields` +-- --- ----------------------------------------------------- --- Table `widget_types` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `widget_types` ( - `handler` VARCHAR(255) NOT NULL COMMENT 'Class wich handles everything about this widget type', - `name` VARCHAR(64) NOT NULL COMMENT 'Name is used only in admin for better readability.', - `description` TEXT NULL DEFAULT NULL COMMENT 'This field also improves readability.', - PRIMARY KEY (`handler`)) -ENGINE = InnoDB -COMMENT = 'Widget type table contains information about all installed widgets. This is something like \"class\" to widgets.'; +DROP TABLE IF EXISTS `content_has_custom_fields`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `content_has_custom_fields` ( + `contents_id` int(11) NOT NULL, + `custom_field_id` int(11) NOT NULL, + `value` text COLLATE utf8_unicode_ci, + PRIMARY KEY (`contents_id`,`custom_field_id`), + KEY `fk_contents_has_custom_fields_custom_fields1_idx` (`custom_field_id`), + KEY `fk_contents_has_custom_fields_contents1_idx` (`contents_id`), + CONSTRAINT `fk_contents_has_custom_fields_contents1` FOREIGN KEY (`contents_id`) REFERENCES `contents` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT `fk_contents_has_custom_fields_custom_fields1` FOREIGN KEY (`custom_field_id`) REFERENCES `custom_fields` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `content_history` +-- --- ----------------------------------------------------- --- Table `widgets` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `widgets` ( - `id` INT NOT NULL AUTO_INCREMENT, - `widget_type_handler` VARCHAR(255) NOT NULL, - `user_id` INT NOT NULL COMMENT 'User ID who created this widget', - `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `name` VARCHAR(64) NOT NULL, - `settings` TEXT NOT NULL COMMENT 'JSON serialized values used for initializing given widget.', - `description` MEDIUMTEXT NULL, +DROP TABLE IF EXISTS `content_history`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `content_history` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `content_id` int(11) NOT NULL, + `changed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `edit_batch` varchar(16) COLLATE utf8_unicode_ci NOT NULL COMMENT 'This field holds identical values for all historical entries edited as one update.', + `column` varchar(64) COLLATE utf8_unicode_ci NOT NULL, + `new_value` text COLLATE utf8_unicode_ci, + `old_value` text COLLATE utf8_unicode_ci, PRIMARY KEY (`id`), - INDEX `fk_widget_user1_idx` (`user_id` ASC), - INDEX `fk_widget_widget_type1_idx` (`widget_type_handler` ASC), - CONSTRAINT `fk_widget_user1` - FOREIGN KEY (`user_id`) - REFERENCES `users` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION, - CONSTRAINT `fk_widget_widget_type1` - FOREIGN KEY (`widget_type_handler`) - REFERENCES `widget_types` (`handler`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB -COMMENT = 'Widget is a small piece of content, which can be inserted into widget area. Example of widgets: menu (with links), banner, some static html code. This table contains real \"instances\" of widgets.'; + KEY `edit_batch` (`edit_batch`), + KEY `column` (`column`), + KEY `fk_content_history_users_idx` (`user_id`), + KEY `fk_content_history_content1_idx` (`content_id`), + CONSTRAINT `fk_content_history_content1` FOREIGN KEY (`content_id`) REFERENCES `contents` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT `fk_content_history_users` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION +) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Table for holding history of changes on some content.'; +/*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `contents` +-- --- ----------------------------------------------------- --- Table `widget_areas` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `widget_areas` ( - `id` INT NOT NULL, - `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `name` VARCHAR(64) NOT NULL COMMENT 'Used for user overview.', - `code` VARCHAR(32) NOT NULL COMMENT 'Value of this field is used for replacing placeholder text in templates with content of real widget area.', - `description` TEXT NULL DEFAULT NULL, +DROP TABLE IF EXISTS `contents`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `contents` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `parent_id` int(11) DEFAULT NULL COMMENT 'ID of parent page', + `user_id` int(11) NOT NULL, + `module_handler` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `category_id` int(10) unsigned DEFAULT NULL COMMENT 'FK to table categories', + `template` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Template used for rendering this content page.', + `language` varchar(8) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'cs', + `is_homepage` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Is this content page the homepage?', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `viewed` int(11) NOT NULL COMMENT 'How many times has been this content page displayed?', + `published_from` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `published_to` timestamp NULL DEFAULT NULL, + `title_photo` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'This photo is supposed to be displayed e.g. on list of all pages.', + `title` varchar(64) COLLATE utf8_unicode_ci NOT NULL, + `url` varchar(64) COLLATE utf8_unicode_ci NOT NULL, + `meta_keywords` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `meta_description` mediumtext COLLATE utf8_unicode_ci, + `content` text COLLATE utf8_unicode_ci, + `module_settings` text COLLATE utf8_unicode_ci COMMENT 'JSON serialized object passed as settings to the module.', + `status` enum('draft','protected','public','deleted') COLLATE utf8_unicode_ci DEFAULT 'draft' COMMENT 'This photo is supposed to be displayed e.g. on list of all pages.', PRIMARY KEY (`id`), - UNIQUE INDEX `code_UNIQUE` (`code` ASC)) -ENGINE = InnoDB -COMMENT = 'Widget area is container for widgets. This container is inserted into templates/content and populated by widgets.'; + UNIQUE KEY `contents_language_url_unique` (`language`,`url`), + KEY `fk_content_user1_idx` (`user_id`), + KEY `url` (`url`), + KEY `fk_content_module1_idx` (`module_handler`), + KEY `fk_content_file1_idx` (`title_photo`), + KEY `contents_is_homepage_index` (`is_homepage`), + KEY `contents_template_index` (`template`), + KEY `contents_parent_id_index` (`parent_id`), + KEY `contents_language_index` (`language`), + KEY `contents_category_id_foreign` (`category_id`), + CONSTRAINT `contents_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT `contents_language_foreign` FOREIGN KEY (`language`) REFERENCES `languages` (`code`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `contents_module_handler_foreign` FOREIGN KEY (`module_handler`) REFERENCES `modules` (`handler`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `contents_parent_id_foreign` FOREIGN KEY (`parent_id`) REFERENCES `contents` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `contents_template_foreign` FOREIGN KEY (`template`) REFERENCES `templates` (`template_name`) ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT `contents_title_photo_foreign` FOREIGN KEY (`title_photo`) REFERENCES `files` (`filename`) ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT `fk_content_user1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION +) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Table with all content pages.'; +/*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `contents_has_files` +-- --- ----------------------------------------------------- --- Table `widget_in_widget_area` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `widget_in_widget_area` ( - `widget_id` INT NOT NULL, - `widget_area_id` INT NOT NULL, - `position` INT NOT NULL COMMENT 'Which position should the widget take?', - PRIMARY KEY (`widget_id`, `widget_area_id`), - INDEX `fk_widget_has_widget_area_widget_area1_idx` (`widget_area_id` ASC), - INDEX `fk_widget_has_widget_area_widget1_idx` (`widget_id` ASC), - INDEX `position` (`position` ASC), - CONSTRAINT `fk_widget_has_widget_area_widget1` - FOREIGN KEY (`widget_id`) - REFERENCES `widgets` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION, - CONSTRAINT `fk_widget_has_widget_area_widget_area1` - FOREIGN KEY (`widget_area_id`) - REFERENCES `widget_areas` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB; +DROP TABLE IF EXISTS `contents_has_files`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `contents_has_files` ( + `content_id` int(11) NOT NULL, + `file_id` int(11) NOT NULL, + PRIMARY KEY (`content_id`,`file_id`), + KEY `contents_has_files_file_id_foreign` (`file_id`), + CONSTRAINT `contents_has_files_content_id_foreign` FOREIGN KEY (`content_id`) REFERENCES `contents` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `contents_has_files_file_id_foreign` FOREIGN KEY (`file_id`) REFERENCES `files` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `custom_field_resource` +-- --- ----------------------------------------------------- --- Table `custom_field_types` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `custom_field_types` ( - `id` INT NOT NULL AUTO_INCREMENT, - `name` VARCHAR(64) NOT NULL COMMENT 'Name of custom field. Given as a translation string (e.g. as dir/section.translation)', - `handler` VARCHAR(255) NOT NULL COMMENT 'Class for handling this type of custom field', +DROP TABLE IF EXISTS `custom_field_resource`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `custom_field_resource` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Name of resource for custom fields. Saved as translation string.', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `custom_field_resources` +-- + +DROP TABLE IF EXISTS `custom_field_resources`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `custom_field_resources` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Name of resource for custom fields. Saved as translation string.', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `custom_field_types` +-- + +DROP TABLE IF EXISTS `custom_field_types`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `custom_field_types` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Name of custom field. Given as a translation string (e.g. as dir/section.translation)', + `handler` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Class for handling this type of custom field', PRIMARY KEY (`id`), - UNIQUE INDEX `name_UNIQUE` (`name` ASC), - UNIQUE INDEX `handler_UNIQUE` (`handler` ASC)) -ENGINE = InnoDB -COMMENT = 'List of all custom field types.'; + UNIQUE KEY `name_UNIQUE` (`name`), + UNIQUE KEY `handler_UNIQUE` (`handler`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='List of all custom field types.'; +/*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `custom_fields` +-- --- ----------------------------------------------------- --- Table `custom_field_resource` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `custom_field_resource` ( - `id` INT NOT NULL AUTO_INCREMENT, - `name` VARCHAR(64) NOT NULL COMMENT 'Name of resource for custom fields. Saved as translation string.', - PRIMARY KEY (`id`)) -ENGINE = InnoDB; - - --- ----------------------------------------------------- --- Table `custom_fields` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `custom_fields` ( - `id` INT NOT NULL AUTO_INCREMENT, - `name` VARCHAR(128) NOT NULL, - `type_id` INT NOT NULL COMMENT 'Foreign key to custom_field_types table', - `resource_id` INT NOT NULL, - `description` MEDIUMTEXT NULL DEFAULT NULL COMMENT 'Description used for detailed information about meaning of given custom field', - `default_value` TEXT NULL DEFAULT NULL, - `possible_values` TEXT NULL DEFAULT NULL COMMENT 'Possible values separated with pipeline', - `regexp` MEDIUMTEXT NULL DEFAULT NULL, - `min_length` INT(11) NULL DEFAULT NULL, - `max_length` INT(11) NULL DEFAULT NULL, - `required` TINYINT(1) NOT NULL DEFAULT 0, +DROP TABLE IF EXISTS `custom_fields`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `custom_fields` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(128) COLLATE utf8_unicode_ci NOT NULL, + `type_id` int(11) NOT NULL COMMENT 'Foreign key to custom_field_types table', + `resource_id` int(11) NOT NULL, + `description` mediumtext COLLATE utf8_unicode_ci COMMENT 'Description used for detailed information about meaning of given custom field', + `default_value` text COLLATE utf8_unicode_ci, + `possible_values` text COLLATE utf8_unicode_ci COMMENT 'Possible values separated with pipeline', + `regexp` mediumtext COLLATE utf8_unicode_ci, + `min_length` int(11) DEFAULT NULL, + `max_length` int(11) DEFAULT NULL, + `required` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), - INDEX `fk_custom_fields_custom_field_types1_idx` (`type_id` ASC), - INDEX `fk_custom_fields_custom_field_resource1_idx` (`resource_id` ASC), - CONSTRAINT `fk_custom_fields_custom_field_types1` - FOREIGN KEY (`type_id`) - REFERENCES `custom_field_types` (`id`) - ON DELETE CASCADE - ON UPDATE CASCADE, - CONSTRAINT `fk_custom_fields_custom_field_resource1` - FOREIGN KEY (`resource_id`) - REFERENCES `custom_field_resource` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB -COMMENT = 'List of all custom fields which may be used for extending some part of content on webpage'; + KEY `fk_custom_fields_custom_field_types1_idx` (`type_id`), + KEY `fk_custom_fields_custom_field_resource1_idx` (`resource_id`), + CONSTRAINT `fk_custom_fields_custom_field_resource1` FOREIGN KEY (`resource_id`) REFERENCES `custom_field_resources` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT `fk_custom_fields_custom_field_types1` FOREIGN KEY (`type_id`) REFERENCES `custom_field_types` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='List of all custom fields which may be used for extending some part of content on webpage'; +/*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `directories` +-- --- ----------------------------------------------------- --- Table `user_has_custom_field` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `user_has_custom_field` ( - `user_id` INT NOT NULL, - `custom_field_id` INT NOT NULL, - `value` TEXT NOT NULL, - PRIMARY KEY (`user_id`, `custom_field_id`), - INDEX `fk_user_has_custom_field_custom_field1_idx` (`custom_field_id` ASC), - INDEX `fk_user_has_custom_field_user1_idx` (`user_id` ASC), - CONSTRAINT `fk_user_has_custom_field_user1` - FOREIGN KEY (`user_id`) - REFERENCES `users` (`id`) - ON DELETE CASCADE - ON UPDATE CASCADE, - CONSTRAINT `fk_user_has_custom_field_custom_field1` - FOREIGN KEY (`custom_field_id`) - REFERENCES `custom_fields` (`id`) - ON DELETE CASCADE - ON UPDATE CASCADE) -ENGINE = InnoDB; - - --- ----------------------------------------------------- --- Table `action_log_types` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `action_log_types` ( - `id` INT NOT NULL AUTO_INCREMENT, - `name` VARCHAR(128) NOT NULL, - `value` VARCHAR(128) NOT NULL COMMENT 'Log content - represented as translation string.', - PRIMARY KEY (`id`)) -ENGINE = InnoDB -COMMENT = 'List of all types which can be logged.'; - - --- ----------------------------------------------------- --- Table `action_log` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `action_log` ( - `id` INT NOT NULL AUTO_INCREMENT, - `type_id` INT NOT NULL, - `user_id` INT NOT NULL, - `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - `parameters` MEDIUMTEXT NULL DEFAULT NULL COMMENT 'JSON serialized array/object passed into translation.', +DROP TABLE IF EXISTS `directories`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `directories` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `user_id` int(11) DEFAULT NULL COMMENT 'ID of user, who created this directory.', + `parent_id` int(11) DEFAULT NULL COMMENT 'ID of parent directory', + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Directory name used for better navigation', + `description` mediumtext COLLATE utf8_unicode_ci COMMENT 'Description of directory', + `hidden` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Hide this directory from view of all directories?', PRIMARY KEY (`id`), - INDEX `fk_action_log_action_log_type1_idx` (`type_id` ASC), - INDEX `fk_action_log_user1_idx` (`user_id` ASC), - CONSTRAINT `fk_action_log_action_log_type1` - FOREIGN KEY (`type_id`) - REFERENCES `action_log_types` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION, - CONSTRAINT `fk_action_log_user1` - FOREIGN KEY (`user_id`) - REFERENCES `users` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB -COMMENT = 'This table contains log of all actions, which happened in administration.'; + UNIQUE KEY `directories_parent_id_name_unique` (`parent_id`,`name`), + KEY `directories_user_id_index` (`user_id`), + KEY `directories_parent_id_index` (`parent_id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `files` +-- + +DROP TABLE IF EXISTS `files`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `files` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL COMMENT 'User, who uploaded this file', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT NULL, + `filename` varchar(128) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Filename of localy stored file', + `directory_id` int(11) DEFAULT NULL COMMENT 'FK to directories table', + `original_filename` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Original filename of uploaded file', + `file_hash` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT 'SHA hash of file content.', + `hash_algorithm` varchar(16) COLLATE utf8_unicode_ci NOT NULL, + `mime_type` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT 'MIME type of uploaded file', + `file_size` int(11) NOT NULL COMMENT 'Filesize given in bytes', + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Some human readable name of file. May be used in different ways', + `description` text COLLATE utf8_unicode_ci, + `keywords` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `fk_file_user1_idx` (`user_id`), + KEY `filename` (`filename`), + KEY `files_filename_index` (`filename`), + KEY `keywords_idx` (`keywords`), + KEY `files_directory_id_index` (`directory_id`), + CONSTRAINT `files_directory_id_foreign` FOREIGN KEY (`directory_id`) REFERENCES `directories` (`id`) ON UPDATE CASCADE, + CONSTRAINT `fk_file_user1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION +) ENGINE=InnoDB AUTO_INCREMENT=75 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Table containing files which were uploaded into system.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `languages` +-- + +DROP TABLE IF EXISTS `languages`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `languages` ( + `code` varchar(8) COLLATE utf8_unicode_ci NOT NULL, + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Translated name for given language', + `script` varchar(16) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'Latn' COMMENT 'ISO code for locale script (Latn, Arab, ...)', + `native` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Native name for language', + PRIMARY KEY (`code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `migrations` +-- + +DROP TABLE IF EXISTS `migrations`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `migrations` ( + `migration` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `batch` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `modules` +-- + +DROP TABLE IF EXISTS `modules`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `modules` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `parent_id` int(11) DEFAULT NULL COMMENT 'Parent module ID (when module is submodule)', + `handler` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `name` varchar(64) COLLATE utf8_unicode_ci NOT NULL, + `description` text COLLATE utf8_unicode_ci, + `alias` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `enabled` tinyint(1) NOT NULL COMMENT 'Is this module in use?', + `module_settings` text COLLATE utf8_unicode_ci COMMENT 'JSON serialized object with configuration of given module.', + `manages_content` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Does this module support content management?', + `support_categories` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Does this module support categories?', + `has_submodules` tinyint(1) NOT NULL DEFAULT '0', + `is_template` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Templates can be treated as modules.', + PRIMARY KEY (`id`), + UNIQUE KEY `modules_handler_unique` (`handler`), + KEY `modules_enabled_index` (`enabled`), + KEY `modules_parent_id_foreign` (`parent_id`), + CONSTRAINT `modules_parent_id_foreign` FOREIGN KEY (`parent_id`) REFERENCES `modules` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=45 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `password_resets` +-- + +DROP TABLE IF EXISTS `password_resets`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `password_resets` ( + `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `token` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + KEY `password_resets_email_index` (`email`), + KEY `password_resets_token_index` (`token`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `settings` +-- + +DROP TABLE IF EXISTS `settings`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `settings` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `language` varchar(8) COLLATE utf8_unicode_ci DEFAULT NULL, + `key` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT 'User readable key for accessing value of this config. Config will be available under this key also in views.', + `value` text COLLATE utf8_unicode_ci NOT NULL COMMENT 'Value of config.', + `load_into_views` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Use in views directly?', + PRIMARY KEY (`id`), + UNIQUE KEY `settings_key_language_unique` (`key`,`language`), + KEY `settings_load_into_views_index` (`load_into_views`), + KEY `settings_language_foreign` (`language`), + CONSTRAINT `settings_language_foreign` FOREIGN KEY (`language`) REFERENCES `languages` (`code`) ON DELETE SET NULL ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `template_paths` +-- + +DROP TABLE IF EXISTS `template_paths`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `template_paths` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `parent_id` int(11) DEFAULT NULL COMMENT 'ID of parent directory', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `path` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Path on filesystem', + `namespace` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Namespace used for all templates in this path', + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Human readable name', + PRIMARY KEY (`id`), + KEY `template_paths_parent_id_index` (`parent_id`), + CONSTRAINT `template_paths_parent_id_foreign` FOREIGN KEY (`parent_id`) REFERENCES `template_paths` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=46 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `templates` +-- + +DROP TABLE IF EXISTS `templates`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `templates` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `module_id` int(11) DEFAULT NULL COMMENT 'Template belongs to module specified with this ID.', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `template_name` varchar(128) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Template name used for rendering page. Use namespace::dir.template format.', + `path` int(11) NOT NULL COMMENT 'ID of template directory', + `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Human readable name of given template', + `description` mediumtext COLLATE utf8_unicode_ci NOT NULL COMMENT 'Description of this template.', + `editable` tinyint(1) NOT NULL COMMENT 'Can this template be edited?', + PRIMARY KEY (`id`), + KEY `templates_template_name_index` (`template_name`), + KEY `templates_path_index` (`path`), + KEY `templates_module_id_index` (`module_id`), + CONSTRAINT `templates_module_id_foreign` FOREIGN KEY (`module_id`) REFERENCES `modules` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `templates_path_foreign` FOREIGN KEY (`path`) REFERENCES `template_paths` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=111 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `user_has_custom_field` +-- + +DROP TABLE IF EXISTS `user_has_custom_field`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_has_custom_field` ( + `user_id` int(11) NOT NULL, + `custom_field_id` int(11) NOT NULL, + `value` text COLLATE utf8_unicode_ci NOT NULL, + PRIMARY KEY (`user_id`,`custom_field_id`), + KEY `fk_user_has_custom_field_custom_field1_idx` (`custom_field_id`), + KEY `fk_user_has_custom_field_user1_idx` (`user_id`), + CONSTRAINT `fk_user_has_custom_field_custom_field1` FOREIGN KEY (`custom_field_id`) REFERENCES `custom_fields` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `fk_user_has_custom_field_user1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `users` +-- + +DROP TABLE IF EXISTS `users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `email` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT 'E-mail is used as a contact and also as a username', + `username` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Username can be used also for login. Default value is users email.', + `password` varchar(128) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Password hash', + `role` enum('visitor','editor','admin') COLLATE utf8_unicode_ci NOT NULL DEFAULT 'visitor' COMMENT 'User''s role determines his permissions in the system.', + `remember_token` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Token used for "remember me" feature', + `name` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL, + `status` enum('deleted','blocked','active') COLLATE utf8_unicode_ci NOT NULL DEFAULT 'active', + PRIMARY KEY (`id`), + UNIQUE KEY `email_UNIQUE` (`email`) +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Table with users who are allowed to log into system'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `widget_areas` +-- + +DROP TABLE IF EXISTS `widget_areas`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `widget_areas` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `name` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Used for user overview.', + `code` varchar(32) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Value of this field is used for replacing placeholder text in templates with content of real widget area.', + `description` text COLLATE utf8_unicode_ci, + `deletable` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Can this widget area be deleted?', + PRIMARY KEY (`id`), + UNIQUE KEY `code_UNIQUE` (`code`) +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Widget area is container for widgets. This container is inserted into templates/content and populated by widgets.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `widget_in_widget_area` +-- + +DROP TABLE IF EXISTS `widget_in_widget_area`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `widget_in_widget_area` ( + `widget_id` int(11) NOT NULL, + `widget_area_id` int(11) NOT NULL, + `position` int(11) NOT NULL COMMENT 'Which position should the widget take?', + PRIMARY KEY (`widget_id`,`widget_area_id`), + KEY `fk_widget_has_widget_area_widget_area1_idx` (`widget_area_id`), + KEY `fk_widget_has_widget_area_widget1_idx` (`widget_id`), + KEY `position` (`position`), + CONSTRAINT `fk_widget_has_widget_area_widget1` FOREIGN KEY (`widget_id`) REFERENCES `widgets` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT `widget_in_widget_area_widget_area_id_foreign` FOREIGN KEY (`widget_area_id`) REFERENCES `widget_areas` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `widget_types` +-- + +DROP TABLE IF EXISTS `widget_types`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `widget_types` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `handler` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Class wich handles everything about this widget type', + `template_id` int(11) DEFAULT NULL COMMENT 'Commes this widget with template?', + `module_id` int(11) DEFAULT NULL COMMENT 'Commes this widget with some module?', + `name` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT 'Name is used only in admin for better readability.', + `description` text COLLATE utf8_unicode_ci COMMENT 'This field also improves readability.', + PRIMARY KEY (`id`), + UNIQUE KEY `widget_types_handler_unique` (`handler`), + KEY `widget_types_template_id_foreign` (`template_id`), + KEY `widget_types_module_id_foreign` (`module_id`), + CONSTRAINT `widget_types_module_id_foreign` FOREIGN KEY (`module_id`) REFERENCES `modules` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `widget_types_template_id_foreign` FOREIGN KEY (`template_id`) REFERENCES `modules` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Widget type table contains information about all installed widgets. This is something like "class" to widgets.'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `widgets` +-- + +DROP TABLE IF EXISTS `widgets`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `widgets` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `widget_type_handler` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `language` varchar(8) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'cs', + `user_id` int(11) NOT NULL COMMENT 'User ID who created this widget', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `name` varchar(64) COLLATE utf8_unicode_ci NOT NULL, + `settings` text COLLATE utf8_unicode_ci NOT NULL COMMENT 'JSON serialized values used for initializing given widget.', + `description` mediumtext COLLATE utf8_unicode_ci, + `deletable` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Can this widget be deleted?', + PRIMARY KEY (`id`), + KEY `fk_widget_user1_idx` (`user_id`), + KEY `fk_widget_widget_type1_idx` (`widget_type_handler`), + KEY `widgets_language_foreign` (`language`), + CONSTRAINT `fk_widget_user1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT `widgets_language_foreign` FOREIGN KEY (`language`) REFERENCES `languages` (`code`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `widgets_widget_type_handler_foreign` FOREIGN KEY (`widget_type_handler`) REFERENCES `widget_types` (`handler`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='Widget is a small piece of content, which can be inserted into widget area. Example of widgets: menu (with links), banner, some static html code. This table contains real "instances" of widgets.'; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; --- ----------------------------------------------------- --- Table `content_has_custom_fields` --- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS `content_has_custom_fields` ( - `contents_id` INT NOT NULL, - `custom_field_id` INT NOT NULL, - `value` TEXT NULL, - PRIMARY KEY (`contents_id`, `custom_field_id`), - INDEX `fk_contents_has_custom_fields_custom_fields1_idx` (`custom_field_id` ASC), - INDEX `fk_contents_has_custom_fields_contents1_idx` (`contents_id` ASC), - CONSTRAINT `fk_contents_has_custom_fields_contents1` - FOREIGN KEY (`contents_id`) - REFERENCES `contents` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION, - CONSTRAINT `fk_contents_has_custom_fields_custom_fields1` - FOREIGN KEY (`custom_field_id`) - REFERENCES `custom_fields` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) -ENGINE = InnoDB; + +INSERT INTO `languages` (`code`, `name`, `script`, `native`) VALUES + ('cs', 'common.lang.czech', 'Latn', 'Cestina'), + ('en', 'common.lang.english', 'Latn', 'English'); + +INSERT INTO `migrations` (`migration`, `batch`) VALUES + ('2014_10_12_100000_create_password_resets_table', 1), + ('2016_01_28_213204_AddStringIdForActionTypes', 2), + ('2016_02_08_105722_AddExtendedFileInfo', 3), + ('2016_02_08_134354_AddFileSizeTofiles', 4), + ('2016_02_08_234544_FixAutoIncrementForWidgetAreas', 5), + ('2016_02_14_142507_AddIdToWidgetTypes', 6), + ('2016_02_15_155126_AddHomepageMark', 7), + ('2016_02_17_131804_AddTemplateSupport', 8), + ('2016_02_18_100743_CreateTemplateDirectoriesTable', 9), + ('2016_02_18_122645_RemoveModuleIdFromTemplates', 10), + ('2016_02_18_132248_AddNameAndParentIdToTemplatePaths', 11), + ('2016_02_18_141703_AddNamespaceToTemplatePaths', 12), + ('2016_03_08_100656_AddModuleIdToTemplatesAgain', 13), + ('2016_03_13_131109_AddFileFirectoriesSupport', 14), + ('2016_03_13_225224_CreateSettingsTable', 15), + ('2016_03_14_000228_ExtendModulesInfo', 16), + ('2016_03_14_102343_AddModuleSettings', 17), + ('2016_03_31_175822_AddSubpages', 18), + ('2016_03_31_192238_ChangeContentTitleImageIdForFilename', 19), + ('2016_03_31_214730_MakeUserIdNotMandatoryInDirectories', 20), + ('2016_03_31_225203_AddNameForFiles', 21), + ('2016_04_02_233316_AddIpAndUserAgentIntoLog', 22), + ('2016_07_21_205626_MakeLangEnumerations', 23), + ('2016_07_21_221547_AddFieldsIntoLanguages', 24), + ('2016_07_22_095923_AddLanguageToContentPages', 25), + ('2016_07_22_130324_AddLanguageIntoWidgets', 26), + ('2016_07_22_145402_AddLanguageIntoSettings', 27), + ('2016_07_28_192556_assignFilesToPages', 28), + ('2016_08_01_211830_ChangeCollationOfTables', 29), + ('2016_08_08_204055_AddFKToContenctAndTemplates', 30), + ('2016_08_08_223700_MarkContentManagementModules', 31), + ('2016_08_08_225613_MarkCategorizableModules', 31), + ('2016_08_10_204045_create_categories_table', 32), + ('2016_08_10_204046_create_category_closures_table', 32), + ('2016_08_13_150322_AddCategorySupport', 33), + ('2016_08_14_165211_MakeModuleHandlerNullable', 34), + ('2016_08_15_183157_AddParentModuleIdToModules', 35), + ('2016_09_05_085644_create_comments', 36), + ('2016_09_19_192132_SaveTemplatesIntoModulesTable', 37), + ('2016_09_19_225527_AddNumberOfViewsOfPage', 38), + ('2016_09_20_113806_AddTemplateSupportToWidgets', 39), + ('2016_09_20_114930_MakeWidgetsAndWidgetAreasNotDeletable', 40), + ('2016_09_20_122700_AddTemplateIdIntoTemplates', 41); -SET SQL_MODE=@OLD_SQL_MODE; + + + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; -SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2016-09-20 21:58:09 diff --git a/modules/Blog/Article/Entities/Comment.php b/modules/Blog/Article/Entities/Comment.php new file mode 100644 index 00000000..4b42d711 --- /dev/null +++ b/modules/Blog/Article/Entities/Comment.php @@ -0,0 +1,26 @@ +belongsTo(User::class, 'user_id'); + } + + public function article() + { + return $this->belongsTo(Content::class, 'content_id'); + } +} \ No newline at end of file diff --git a/modules/Blog/Article/Http/Controllers/.gitkeep b/modules/Blog/Article/Events/ApprovedComment.php similarity index 100% rename from modules/Blog/Article/Http/Controllers/.gitkeep rename to modules/Blog/Article/Events/ApprovedComment.php diff --git a/modules/Blog/ArticleList/Assets/.gitkeep b/modules/Blog/Article/Events/DeletedComment.php similarity index 100% rename from modules/Blog/ArticleList/Assets/.gitkeep rename to modules/Blog/Article/Events/DeletedComment.php diff --git a/modules/Blog/Article/Handlers/Article.php b/modules/Blog/Article/Handlers/Article.php index 90d0f39d..2b58be8d 100644 --- a/modules/Blog/Article/Handlers/Article.php +++ b/modules/Blog/Article/Handlers/Article.php @@ -8,6 +8,7 @@ use App\Http\Controllers\Controller; use App\Http\Requests\Request; use App\Models\Content; use App\Models\User; +use Modules\Blog\Article\Entities\Comment; class Article extends Handler { @@ -21,6 +22,14 @@ class Article extends Handler */ public function render(Content $content, $parameters) { + $content->comments = []; + if (isset($content->module_settings->allowComments) && $content->module_settings->allowComments) { + $content->comments = Comment::where('content_id', $content->id) + ->where('approved', 1) + ->orderBy('created_at') + ->get(); + } + return Controller::VisitorsView($content->template, ['page' => $content]); } @@ -79,6 +88,11 @@ class Article extends Handler return $changedColumns; } + public static function getOverviewTitle() + { + return trans('article::module.allArticles'); + } + /** * * @param User $user @@ -89,10 +103,20 @@ class Article extends Handler $menu = \Menu::instance('mainMenu'); - $menu->dropdown(trans('article::module.name'), function ($sub) { + $blogSubmenu = $menu->whereTitle(trans('article::module.blog')); + if (!$blogSubmenu) { + $blogSubmenu = $menu->dropdown(trans('article::module.blog'), function ($sub) { + }, 20, ['icon' => 'fa fa-fw fa-book']); + } + + $blogSubmenu->dropdown(trans('article::module.name'), function ($sub) { $sub->route('content.new', trans('article::article.new'), ['id' => self::getModuleId()], 10, ['icon' => 'fa fa-fw fa-pencil']); $sub->route('content.overview', trans('article::article.overview'), ['id' => self::getModuleId()], 30, ['icon' => 'fa fa-fw fa-book']); }, 10, ['icon' => 'fa fa-fw fa-book']); + + $blogSubmenu->dropdown(trans('article::module.comments'), function ($sub) { + $sub->route('comments.overview', trans('article::module.commentsOverview'), [], 10, ['icon' => 'fa fa-fw fa-commenting-o']); + }, 20, ['icon' => 'fa fa-fw fa-comments-o']); } public static function getModuleId() diff --git a/modules/Blog/Article/Http/Controllers/Admin/CommentsOverview.php b/modules/Blog/Article/Http/Controllers/Admin/CommentsOverview.php new file mode 100644 index 00000000..8e55b3b9 --- /dev/null +++ b/modules/Blog/Article/Http/Controllers/Admin/CommentsOverview.php @@ -0,0 +1,19 @@ +with('author') + ->orderBy('approved') + ->orderBy('created_at', 'DESC') + ->paginate(20); + + return view('article::admin/comments.overview', ['comments' => $comments]); + } +} \ No newline at end of file diff --git a/modules/Blog/Article/Http/Controllers/Admin/EditComment.php b/modules/Blog/Article/Http/Controllers/Admin/EditComment.php new file mode 100644 index 00000000..ed4de413 --- /dev/null +++ b/modules/Blog/Article/Http/Controllers/Admin/EditComment.php @@ -0,0 +1,33 @@ +approved = 1; + $comment->save(); + + // Event::fire(ApprovedComment); + + return redirect() + ->back() + ->with('success', trans('article::comments.approved')); + } + + public function delete($id) + { + $comment = Comment::findOrFail($id); + $comment->delete(); + + // Event::fire(DeletedComment); + + return redirect() + ->back() + ->with('success', trans('article::comments.deleted')); + } +} \ No newline at end of file diff --git a/modules/Blog/Article/Http/Controllers/Visitor/CreateNewComment.php b/modules/Blog/Article/Http/Controllers/Visitor/CreateNewComment.php new file mode 100644 index 00000000..3a5b17ac --- /dev/null +++ b/modules/Blog/Article/Http/Controllers/Visitor/CreateNewComment.php @@ -0,0 +1,48 @@ +where('module_handler', Article::class) + ->firstOrFail(); + + $article->module_settings = json_decode($article->module_settings); + + if (!isset($article->module_settings->allowComments) || !$article->module_settings->allowComments) { + return redirect() + ->route('page.content', ['url' => $article->url]) + ->with('error', trans('article::comments.disabledComments')); + } + + $content = $request->message; + $user = Auth::user(); + if ($user) { + $name = $user->name; + $email = $user->email; + } else { + $name = $request->name; + $email = $request->email; + } + + Comment::create([ + 'user_id' => $user ? $user->id : null, + 'content_id' => $article->id, + 'name' => $name, + 'email' => $email, + 'content' => $content + ]); + + return redirect() + ->route('page.content', ['url' => $article->url]) + ->with('success', trans('article::comments.saved')); + } +} \ No newline at end of file diff --git a/modules/Blog/Article/Http/Requests/CreateNewComment.php b/modules/Blog/Article/Http/Requests/CreateNewComment.php new file mode 100644 index 00000000..18044f43 --- /dev/null +++ b/modules/Blog/Article/Http/Requests/CreateNewComment.php @@ -0,0 +1,28 @@ + 'required']; + + if (!$user) { + $required['name'] = 'required'; + $required['email'] = 'required|email'; + } + + return $required; + } +} diff --git a/modules/Blog/Article/Http/routes.php b/modules/Blog/Article/Http/routes.php index b9ad36f4..de6f6be4 100644 --- a/modules/Blog/Article/Http/routes.php +++ b/modules/Blog/Article/Http/routes.php @@ -1,25 +1,28 @@ 'web', - 'prefix' => 'article', - 'namespace' => 'Modules\Blog\Article\Http\Controllers' + 'middleware' => ['web', 'auth', 'adminMenu'], + 'prefix' => 'admin/comments', + 'namespace' => 'Modules\Blog\Article\Http\Controllers\Admin' ], function () { - Route::get('/', 'CategoriesOverview@getOverview') - ->name('article.overview'); - Route::get('/new', 'CreateNewCategory@getNew') - ->name('article.new'); + Route::get('/', 'CommentsOverview@getOverview') + ->name('comments.overview'); - Route::post('/new', 'CreateNewCategory@postNew') - ->name('article.postNew'); + Route::get('/delete/{id}', 'EditComment@delete') + ->name('comments.delete'); - Route::get('/edit/{id}', 'EditCategory@getEdit') - ->name('article.edit'); + Route::get('/approve/{id}', 'EditComment@approve') + ->name('comments.approve'); +}); - Route::post('/edit/{id}', 'EditCategory@postEdit') - ->name('article.postEdit'); +Route::group([ + 'middleware' => ['web'], + 'prefix' => 'comments', + 'namespace' => 'Modules\Blog\Article\Http\Controllers\Visitor' + ], function () { - Route::get('/delete/{id}', 'EditCategory@delete') - ->name('article.delete'); + Route::post('/new/{id}', 'CreateNewComment@postNew') + ->name('comments.new'); }); \ No newline at end of file diff --git a/modules/Blog/ArticleList/Config/.gitkeep b/modules/Blog/Article/Listeners/CommentEditLogger.php similarity index 100% rename from modules/Blog/ArticleList/Config/.gitkeep rename to modules/Blog/Article/Listeners/CommentEditLogger.php diff --git a/modules/Blog/ArticleList/Console/.gitkeep b/modules/Blog/Article/Providers/ArticleEventServiceProvider.php similarity index 100% rename from modules/Blog/ArticleList/Console/.gitkeep rename to modules/Blog/Article/Providers/ArticleEventServiceProvider.php diff --git a/modules/Blog/Article/Providers/ArticleServiceProvider.php b/modules/Blog/Article/Providers/ArticleServiceProvider.php index 0d52f03c..ac82f742 100644 --- a/modules/Blog/Article/Providers/ArticleServiceProvider.php +++ b/modules/Blog/Article/Providers/ArticleServiceProvider.php @@ -62,22 +62,6 @@ class ArticleServiceProvider extends ServiceProvider { $this->loadViewsFrom(array_merge(array_map(function ($path) { return $path . '/modules/article'; }, \Config::get('view.paths')), [$sourcePath]), 'article'); - - /** - * @var $registrator TemplateRegistrator - */ - try { - // setup some global variables - $registrator = $this->app->make('registerTemplateDir'); - $registrator->setBaseDirectory('resources/views/modules/article/visitor'); - $registrator->setNamespace('article'); - $registrator->setModuleId(Article::getModuleId()); - - // register path with current template name - $registrator->register(config('app.theme'), 'article::admin/adminMenu.templatePath'); - } catch (InvalidArgumentException $e) { - // ignore this exception - } } /** diff --git a/modules/Blog/Article/Resources/lang/cs/article.php b/modules/Blog/Article/Resources/lang/cs/article.php new file mode 100644 index 00000000..fd1c65c6 --- /dev/null +++ b/modules/Blog/Article/Resources/lang/cs/article.php @@ -0,0 +1,6 @@ + "Povolit komentáře?", +"new" => "Napsat", +"overview" => "Všechny články", +]; diff --git a/modules/Blog/Article/Resources/lang/cs/comments.php b/modules/Blog/Article/Resources/lang/cs/comments.php new file mode 100644 index 00000000..12062efe --- /dev/null +++ b/modules/Blog/Article/Resources/lang/cs/comments.php @@ -0,0 +1,8 @@ + "Schválit komentář a zobrazovat ho u článku", +"approved" => "Komentář úspěšně schválen", +"commentedArticle" => "Komentovaný článek", +"createdAt" => "Vloženo :date", +"deleted" => "Komentář smazán", +]; diff --git a/modules/Blog/Article/Resources/lang/cs/module.php b/modules/Blog/Article/Resources/lang/cs/module.php new file mode 100644 index 00000000..eceb1960 --- /dev/null +++ b/modules/Blog/Article/Resources/lang/cs/module.php @@ -0,0 +1,9 @@ + "Všechny články", +"blog" => "Blog", +"comments" => "Komentáře", +"commentsOverview" => "Všechny komentáře", +"description" => "Základní submodul pro blog. Tento modul umožňuje psát články.", +"name" => "Články", +]; diff --git a/modules/Blog/Article/Resources/lang/en/comments.php b/modules/Blog/Article/Resources/lang/en/comments.php new file mode 100644 index 00000000..8eb1d04c --- /dev/null +++ b/modules/Blog/Article/Resources/lang/en/comments.php @@ -0,0 +1,8 @@ + 'Created at :date', + 'commentedArticle' => 'Article', + 'approve' => 'Approve comment', + 'approved' => 'Comment successfully approved', + 'deleted' => 'Comment deleted' +]; \ No newline at end of file diff --git a/modules/Blog/Article/Resources/lang/en/module.php b/modules/Blog/Article/Resources/lang/en/module.php index a448c978..bf8644c0 100644 --- a/modules/Blog/Article/Resources/lang/en/module.php +++ b/modules/Blog/Article/Resources/lang/en/module.php @@ -1,5 +1,9 @@ 'Article', - 'description' => 'Base submodule for blog' + 'name' => 'Articles', + 'description' => 'Base submodule for blog', + 'comments' => 'Comments', + 'commentsOverview' => 'All comments', + 'allArticles' => 'All articles', + 'blog' => 'Blog' ]; \ No newline at end of file diff --git a/modules/Blog/Article/Resources/lang/sk/article.php b/modules/Blog/Article/Resources/lang/sk/article.php new file mode 100644 index 00000000..2a3ce3a6 --- /dev/null +++ b/modules/Blog/Article/Resources/lang/sk/article.php @@ -0,0 +1,6 @@ + "Povoliť komentáre?", + "new" => " Napísať nový", + "overview" => "Všetky články", +]; diff --git a/modules/Blog/Article/Resources/lang/sk/comments.php b/modules/Blog/Article/Resources/lang/sk/comments.php new file mode 100644 index 00000000..5bde58a3 --- /dev/null +++ b/modules/Blog/Article/Resources/lang/sk/comments.php @@ -0,0 +1,8 @@ + "Schváliť komentár", + "approved" => "Komentár úspešne schválený.", + "commentedArticle" => "Článok", + "createdAt" => "Vytvorené: :date", + "deleted" => "Komentár zmazaný.", +]; diff --git a/modules/Blog/Article/Resources/lang/sk/module.php b/modules/Blog/Article/Resources/lang/sk/module.php new file mode 100644 index 00000000..04b574c0 --- /dev/null +++ b/modules/Blog/Article/Resources/lang/sk/module.php @@ -0,0 +1,9 @@ + "Všetky články", + "blog" => "Blog", + "comments" => "Komentáre", + "commentsOverview" => "Všetky komentáre", + "description" => "Základný submodul pre blog", + "name" => "Články", +]; diff --git a/modules/Blog/Article/Resources/views/admin/comments/overview.blade.php b/modules/Blog/Article/Resources/views/admin/comments/overview.blade.php new file mode 100644 index 00000000..c7177e63 --- /dev/null +++ b/modules/Blog/Article/Resources/views/admin/comments/overview.blade.php @@ -0,0 +1,68 @@ +@extends('admin.layoutWithNavigation') + +@section('pageHeading', trans('article::module.comments')) + +@section('content') + @if (count($comments)) + @foreach($comments as $comment) + +
+
+
+

+ + {{$comment->author->name}} ({{$comment->author->email}}) +

+
+
+
+

+ {{$comment->content}} +

+
+ +
+ + + + {{--
--}} + {{--
--}} + {{----}} + + {{--{{$comment->author->name}}--}} + + {{--@date($comment->created_at)--}} + + {{--
--}} + {{--
{{$comment->content}}
--}} + {{--
--}} + + + + {{--
--}} + {{--
--}} + @endforeach + @else +
+ @lang('article::comments.noComments') +
+ @endif +@endsection \ No newline at end of file diff --git a/modules/Blog/Article/Resources/views/admin/content/basicInfo.blade.php b/modules/Blog/Article/Resources/views/admin/content/basicInfo.blade.php index e150c58f..ec02b51e 100644 --- a/modules/Blog/Article/Resources/views/admin/content/basicInfo.blade.php +++ b/modules/Blog/Article/Resources/views/admin/content/basicInfo.blade.php @@ -1,89 +1,89 @@
-
-