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..462d5127 --- /dev/null +++ b/app/Console/Commands/SetTemplate.php @@ -0,0 +1,73 @@ +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->confirm('Uninstall it and install *' . trans($config['name']) . '*? All data associated with installed template will be lost!'); + + if ($confirm) { + $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.'); + + // run again artisan for publishing resources + // it cannot be done using $this->call, because we need to load different template into service providers + exec('php artisan vendor:publish'); + } +} 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/TranslateAll.php b/app/Console/Commands/TranslateAll.php index 26635d17..508802fc 100644 --- a/app/Console/Commands/TranslateAll.php +++ b/app/Console/Commands/TranslateAll.php @@ -29,15 +29,32 @@ class TranslateAll extends Command /** * Execute the console command. - * @return mixed */ public function handle() { $this->call('translate:dir', ['dir' => 'resources/lang']); - - $modules = File::directories('modules'); - foreach ($modules as $module) { - $this->call('translate:dir', ['dir' => $module.'/Resources/lang']); + + $this->crawlDirectory('modules'); + $this->crawlDirectory('templates', ['templates/Base']); + $this->crawlDirectory('templates/Base'); + } + + /** + * Crawl directories and search for directories with translations + * + * @param $directory + * @param array $ignoredDirectories + */ + protected function crawlDirectory($directory, $ignoredDirectories = []) + { + $directories = File::directories($directory); + foreach ($directories as $dir) { + + if (in_array($dir, $ignoredDirectories)) { + continue; + } + + $this->call('translate:dir', ['dir' => $dir . '/Resources/lang']); } } } \ No newline at end of file diff --git a/app/Console/Commands/Translations.php b/app/Console/Commands/TranslateDirectory.php similarity index 89% rename from app/Console/Commands/Translations.php rename to app/Console/Commands/TranslateDirectory.php index aca0693c..22e1bcf5 100644 --- a/app/Console/Commands/Translations.php +++ b/app/Console/Commands/TranslateDirectory.php @@ -6,7 +6,7 @@ use Illuminate\Console\Command; use Illuminate\Support\Facades\File; use Illuminate\Support\Str; -class Translations extends Command +class TranslateDirectory extends Command { /** * The name and signature of the console command. @@ -25,7 +25,6 @@ class Translations extends Command /** * Execute the console command. - * @return mixed */ public function handle() { @@ -33,11 +32,21 @@ class Translations extends Command $defaultLocales = config('app.fallback_locale'); $currentLocales = config('app.locale'); - $defaultLocalesDir = $dir . '/' . $defaultLocales; - $currentLocalesDir = $dir . '/' . $currentLocales; + $defaultLocalesDir = base_path($dir . '/' . $defaultLocales); + $currentLocalesDir = base_path($dir . '/' . $currentLocales); if (!is_dir($defaultLocalesDir)) { // this is the place for checking for submodules + + $modulePath = dirname(dirname($dir)); + + // does exist directory with translations? + if (is_dir(base_path($modulePath . '/Resources/lang')) || file_exists(base_path($modulePath.DIRECTORY_SEPARATOR.'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..4c3aadf1 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -18,10 +18,10 @@ class Kernel extends ConsoleKernel Commands\ChangePassword::class, Commands\InstallModule::class, Commands\InstallAllModules::class, - Commands\Translations::class, + Commands\TranslateDirectory::class, Commands\TranslateAll::class, Commands\InitDatabaseSchema::class, - Commands\SetTheme::class, + Commands\SetTemplate::class, AddLanguage::class ]; diff --git a/app/Enumerations/ActiveContentStatuses.php b/app/Enumerations/ActiveContentStatuses.php deleted file mode 100644 index ed48c909..00000000 --- a/app/Enumerations/ActiveContentStatuses.php +++ /dev/null @@ -1,18 +0,0 @@ - $value) { - if ($key == Content::STATUS_DELETED) { - continue; - } - - self::push($key, $value); - } - } -} \ No newline at end of file diff --git a/app/Enumerations/ContentStatuses.php b/app/Enumerations/ContentStatuses.php index 8ed34f9f..9ed89887 100644 --- a/app/Enumerations/ContentStatuses.php +++ b/app/Enumerations/ContentStatuses.php @@ -9,7 +9,6 @@ class ContentStatuses extends BaseEnumeration { $roles = [ Content::STATUS_DRAFT => 'admin/contents.status_' . Content::STATUS_DRAFT, - Content::STATUS_DELETED => 'admin/contents.status_' . Content::STATUS_DELETED, Content::STATUS_PROTECTED => 'admin/contents.status_' . Content::STATUS_PROTECTED, Content::STATUS_PUBLIC => 'admin/contents.status_' . Content::STATUS_PUBLIC ]; diff --git a/app/Enumerations/UserStates.php b/app/Enumerations/UserStates.php index a693dce9..e28d4662 100644 --- a/app/Enumerations/UserStates.php +++ b/app/Enumerations/UserStates.php @@ -15,7 +15,6 @@ class UserStates extends BaseEnumeration public static function init() { $items = [ - User::STATE_DELETED => 'admin/userManagement.status_' . User::STATE_DELETED, User::STATE_BLOCKED => 'admin/userManagement.status_' . User::STATE_BLOCKED, User::STATE_ACTIVE => 'admin/userManagement.status_' . User::STATE_ACTIVE ]; diff --git a/app/Events/DeleteContent.php b/app/Events/DeleteContent.php index fe1f555a..47e2a7ff 100644 --- a/app/Events/DeleteContent.php +++ b/app/Events/DeleteContent.php @@ -12,7 +12,6 @@ class DeleteContent extends Event * @var Content */ public $page; - public $revision; /** * Create a new event instance. @@ -20,9 +19,8 @@ class DeleteContent extends Event * @param Content $page * @param $revision */ - public function __construct(Content $page, $revision) + public function __construct(Content $page) { $this->page = $page; - $this->revision = $revision; } } diff --git a/app/Handlers/Contracts/BaseModuleHandler.php b/app/Handlers/Contracts/BaseModuleHandler.php index c66e8a4e..9cd17c6b 100644 --- a/app/Handlers/Contracts/BaseModuleHandler.php +++ b/app/Handlers/Contracts/BaseModuleHandler.php @@ -5,8 +5,6 @@ use App\Models\User; interface BaseModuleHandler { - public static function getModuleId(); - /** * Register admin menu (or multiple ones) * @@ -18,9 +16,10 @@ interface BaseModuleHandler public static function registerAdminMenu(User $user); /** - * Install module + * Install module. * - * @return mixed + * This method is called after inserting info about module into database. It can be called only using Artisan in + * CLI. */ public static function install(); } \ No newline at end of file diff --git a/app/Handlers/Contracts/ContentTitles.php b/app/Handlers/Contracts/ContentTitles.php new file mode 100644 index 00000000..5d44f24f --- /dev/null +++ b/app/Handlers/Contracts/ContentTitles.php @@ -0,0 +1,40 @@ + '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/Handler.php b/app/Handlers/Handler.php index 63ddab59..06a29ae4 100644 --- a/app/Handlers/Handler.php +++ b/app/Handlers/Handler.php @@ -2,6 +2,7 @@ namespace App\Handlers; use App\Handlers\Contracts\AppHandler; +use App\Http\Controllers\Controller; use App\Models\Content; /** @@ -29,6 +30,19 @@ abstract class Handler extends BaseHandler implements AppHandler return History::class; } + /** + * Render given module into visitor area. + * + * @param Content $content DB model from table contents + * @param array $parameters parameters used for modifications/custom settings of given model + * + * @return mixed + */ + public function render(Content $content, $parameters) + { + return Controller::VisitorsView($content->template, ['page' => $content, 'extra' => $parameters]); + } + public static function getExtendedFormSections() { return [ diff --git a/app/Handlers/Registrators/TemplateRegistrator.php b/app/Handlers/Registrators/TemplateRegistrator.php index 6788df67..b2cd83db 100644 --- a/app/Handlers/Registrators/TemplateRegistrator.php +++ b/app/Handlers/Registrators/TemplateRegistrator.php @@ -28,6 +28,10 @@ class TemplateRegistrator * @var int ID of module */ protected $moduleId; + /** + * @var array list of all registered template paths + */ + protected $paths = []; /** * Set base path @@ -64,7 +68,7 @@ class TemplateRegistrator */ public function setNamespace($namespace) { - $this->namespace = $namespace . '::'; + $this->namespace = $namespace; } /** @@ -80,24 +84,45 @@ class TemplateRegistrator * Registers new template path. * This method registeres given path itself with all subdirectories * + * @param $baseDirectory * @param string $path path relatively to root of the project * @param string $name human readable identification of given template path * * @throws \Exception */ - public function register($path, $name) + public function register($baseDirectory, $path, $name) { - // check if base directory was set - if (!$this->baseDirectory || !is_dir($this->baseDirectory)) { - throw new \Exception('Set $baseDirectory before registering template path.'); + $this->paths[] = [ + 'namespace' => $this->namespace, + 'moduleId' => $this->moduleId, + 'baseDirectory' => $baseDirectory, + 'path' => $this->baseDirectory . DIRECTORY_SEPARATOR . $path, + 'name' => $name + ]; + $this->unsetNamespace(); + } + + /** + * Save templates into database + */ + public function saveTemplates() + { + if (!is_array($this->paths) || empty($this->paths)) { + return; } - $this->registeredPath = $this->baseDirectory . DIRECTORY_SEPARATOR . $path; + foreach ($this->paths as $path) { + if (!empty($path['namespace'])) { + $this->setNamespace($path['namespace'] . '::'); + } + $this->setModuleId($path['moduleId']); + $this->setBaseDirectory($path['baseDirectory']); - // register path - $this->registerPath($this->registeredPath, $name); - - $this->unsetNamespace(); + $this->registeredPath = $this->baseDirectory . DIRECTORY_SEPARATOR . $path['path']; + // register path + $this->registerPath($this->registeredPath, $path['name']); + $this->unsetNamespace(); + } } /** @@ -146,7 +171,7 @@ class TemplateRegistrator Template::ASSETS_DIRECTORY, Template::MODULES_DIRECTORY ]; - + if (in_array($dirName, $ignoredDirectories)) { return; } @@ -187,7 +212,6 @@ class TemplateRegistrator $registeredTemplates = Templates::getWithTranslations(); if (!in_array($templateName, $registeredTemplates)) { - $params = [ 'template_name' => $templateName, 'name' => $templateName, @@ -197,8 +221,6 @@ class TemplateRegistrator 'editable' => 1 ]; - echo 'ukladam do db '.$templateName."\n"; - // save template into database $newTemplate = Template::create($params); diff --git a/app/Handlers/Registrators/WidgetRegistrator.php b/app/Handlers/Registrators/WidgetRegistrator.php index e70cf9ee..f97aaef4 100644 --- a/app/Handlers/Registrators/WidgetRegistrator.php +++ b/app/Handlers/Registrators/WidgetRegistrator.php @@ -3,44 +3,56 @@ namespace App\Handlers\Registrators; use App\Enumerations\WidgetTypesWithHandlers; use App\Models\WidgetType; -use App\Widgets\Contracts\PublicWidget; -use Symfony\Component\Process\Exception\InvalidArgumentException; class WidgetRegistrator { - protected $widgets; + protected $registeredWidgets = []; - function __construct() - { - $this->widgets = WidgetTypesWithHandlers::getSimple(); - } /** * Method used for registering new widgets * - * @param $classname + * @param $classname + * @param null $templateId + * @param null $moduleId */ - public function register($classname) + public function register($classname, $templateId = null, $moduleId = null) { - $class = new $classname; - if (!$class instanceof PublicWidget) { - throw new InvalidArgumentException($class . ' must implement PublicWidget interface.'); - } + $this->registeredWidgets[] = [ + 'class' => $classname, + 'templateId' => $templateId, + 'moduleId' => $moduleId + ]; + } - // register widget as new widget type - if (!in_array($classname, $this->widgets)) { - $widgetName = call_user_func($classname . '::getName'); - $widgetDescription = call_user_func($classname . '::getDescription'); + /** + * Install all non-installed widgets + */ + public function installWidgets() + { + $installed = WidgetTypesWithHandlers::getSimple(); + foreach ($this->registeredWidgets as $registeredWidget) { + $classname = $registeredWidget['class']; + $templateId = $registeredWidget['templateId']; + $moduleId = $registeredWidget['moduleId']; - // create row in widget types - WidgetType::create([ - 'handler' => $classname, - 'name' => $widgetName, - 'description' => $widgetDescription - ]); + // register widget as new widget type + if (!in_array($classname, $installed)) { + $widgetName = call_user_func($classname . '::getName'); + $widgetDescription = call_user_func($classname . '::getDescription'); - // install widget (if neccessary) - call_user_func($classname . '::install'); + // create row in widget types + WidgetType::create([ + 'handler' => $classname, + 'name' => $widgetName, + 'module_id' => $moduleId, + 'template_id' => $templateId, + 'description' => $widgetDescription + ]); + + // install widget (if neccessary) + call_user_func($classname . '::install'); + } } } } diff --git a/app/Handlers/Search/Files.php b/app/Handlers/Search/Files.php index 5105b3c5..79d66bce 100644 --- a/app/Handlers/Search/Files.php +++ b/app/Handlers/Search/Files.php @@ -27,6 +27,7 @@ class Files extends Search $token = $this->tokenize($term); return File::where('original_filename', 'LIKE', $token) + ->orWhere('name', 'LIKE', $token) ->orWhere('keywords', 'LIKE', $token) ->orWhere('description', 'LIKE', $token) ->orWhere('mime_type', 'LIKE', $token) diff --git a/app/Helpers/CurrentTemplate.php b/app/Helpers/CurrentTemplate.php new file mode 100644 index 00000000..9f88dd88 --- /dev/null +++ b/app/Helpers/CurrentTemplate.php @@ -0,0 +1,57 @@ +where('enabled', true) + ->first(); + + if (self::$template) { + self::$template->module_settings = json_decode(self::$template->module_settings); + } + + self::$initialized = true; + } + + return self::$template; + } + + /** + * Shortcut for getting template handler + * + * @return string + * @throws \ErrorException + */ + public static function handler() + { + $template = self::get(); + + if (!$template) { + throw new \ErrorException('No template selected, no handler available!'); + } + + return $template->handler; + } + + /** + * Reset currently set template and load another one on next call of get() method. + */ + public static function reset() + { + self::$initialized = false; + } +} \ No newline at end of file diff --git a/app/Helpers/Date.php b/app/Helpers/Date.php new file mode 100644 index 00000000..83e1866d --- /dev/null +++ b/app/Helpers/Date.php @@ -0,0 +1,64 @@ +filename)) { - $url = config('app.thumbnail_url') . '/' . $file->filename; - } else { - $url = config('app.upload_url') . '/' . $file->filename; - } + $url = Template::thumbnail($file->filename); return '' . $file->description . ''; break; case 'text': - return ''; + return self::getIcon($file, 'fa-file-text-o'); break; case 'application': switch ($subtype) { case 'pdf': - return ''; + return self::getIcon($file, 'fa-file-pdf-o'); break; default: - return ''; + return self::getIcon($file, 'fa-newspaper-o'); break; } break; default: - return ''; + return self::getIcon($file, 'fa-question'); break; } } + /** + * Shortcut for displaying icons + * + * @param $file + * @param $icon + * + * @return string + */ + protected static function getIcon($file, $icon) + { + return '


' . e(str_limit($file->original_filename, 15)) . '

'; + } + /** * * @@ -83,7 +91,6 @@ class FileManager return '' . $file->description . ''; break; case 'text': - $string = '
';
                 $string .= htmlspecialchars(file_get_contents($path));
                 $string .= '
'; @@ -91,15 +98,8 @@ class FileManager return $string; break; case 'application': - switch ($subtype) { - case 'pdf': - return ''; - break; - default: - return ''; - break; - } - break; + return ''; + break; default: return self::getPreview($file); break; @@ -119,19 +119,12 @@ class FileManager switch ($type) { case 'text': - - return [ - trans('admin/fileManager.encoding') => mb_detect_encoding(file_get_contents($path)) - ]; + return [trans('admin/fileManager.encoding') => mb_detect_encoding(file_get_contents($path))]; break; case 'image': list ($width, $height) = getimagesize($path); - - return [ - trans('admin/fileManager.resolution') => $height . ' x ' . $width, - ]; - + return [trans('admin/fileManager.resolution') => $height . ' x ' . $width,]; break; } diff --git a/app/Helpers/Gravatar.php b/app/Helpers/Gravatar.php index de9e8407..7460cd9d 100644 --- a/app/Helpers/Gravatar.php +++ b/app/Helpers/Gravatar.php @@ -5,6 +5,6 @@ class Gravatar { public static function img($email, $size = 50) { - return "http://www.gravatar.com/avatar/" . md5(strtolower(trim($email))) . "?s=" . $size; + return "//www.gravatar.com/avatar/" . md5(strtolower(trim($email))) . "?s=" . $size; } } \ No newline at end of file diff --git a/app/Helpers/PageRenderer.php b/app/Helpers/PageRenderer.php new file mode 100644 index 00000000..f0acb176 --- /dev/null +++ b/app/Helpers/PageRenderer.php @@ -0,0 +1,43 @@ +module_settings = json_decode($page->module_settings); + + // do something template-specific + $parameters = call_user_func_array([CurrentTemplate::handler(), 'beforeRender'], [&$page]); + + /** + * @var $handler AppHandler + */ + // get handler + $handler = new $page->module_handler; + + $page->content = Shortcode::compile($page->content); + + // render page + return $handler->render($page, $parameters); + } +} diff --git a/app/Helpers/Settings.php b/app/Helpers/Settings.php new file mode 100644 index 00000000..56499040 --- /dev/null +++ b/app/Helpers/Settings.php @@ -0,0 +1,83 @@ +$key)) { + $exists = true; + self::$initialized = true; + return self::$settings->$key; + } + + $exists = false; + return $default; + } + + /** + * Shortcut for obtaining settings saved as JSON + * + * @param $key + * + * @return mixed + */ + public static function getJson($key) + { + return json_decode(self::get($key, '{}')); + } + + /** + * Get all settings + * + * @return array + */ + public static function all() + { + self::init(); + + return self::$settings; + } + + /** + * Initialize settings array + */ + private static function init() + { + if (!self::$initialized) { + $model = new SettingsModel(); + self::$settings = $model->getAll(); + } + } +} \ No newline at end of file diff --git a/app/Helpers/Template.php b/app/Helpers/Template.php index a085d384..07332d77 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,25 @@ class Template return config('app.upload_url') . '/' . $filename; } + public static function thumbnail($filename) + { + // try to check if file have thumbnail + if (file_exists(config('app.thumbnail_path') . '/' . $filename)) { + $url = config('app.thumbnail_url') . '/' . $filename; + } else { + $url = self::upload($filename); + } + + return $url; + } + + /** + * Get path to given asset + * + * @param $path + * + * @return string + */ public static function asset($path) { if (env('APP_DEBUG')) { @@ -75,6 +96,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/Helpers/functions.php b/app/Helpers/functions.php index 5fd8b190..8a163000 100644 --- a/app/Helpers/functions.php +++ b/app/Helpers/functions.php @@ -11,4 +11,20 @@ function e($value) { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); +} + +/** + * Shortcut for \App\Helpers\Settings::get() + * + * @param $key + * @param null $default + * + * @return null|string + */ +function setting($key, $default = null) { + return \App\Helpers\Settings::get($key, $default); +} + +function asset_path($path) { + return env('SITE_URL').'/assets/'.$path; } \ No newline at end of file diff --git a/app/Http/Controllers/Admin/ActionLog/Overview.php b/app/Http/Controllers/Admin/ActionLog/Overview.php new file mode 100644 index 00000000..a93f3a6b --- /dev/null +++ b/app/Http/Controllers/Admin/ActionLog/Overview.php @@ -0,0 +1,51 @@ +log = $log; + } + + /** + * Get overview of logged actions + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + */ + public function getOverview() + { + $user = null; + $userId = Input::get('userId'); + + $actions = $this->log->getLogActionsQuery(true); + + if ($userId) { + $user = User::findOrFail($userId); + $actions->where('users.id', $userId); + } + + $actions = $actions->paginate(25); + + $this->log->translateLogMessages($actions); + + return view('admin.actionLog.overview', ['actions' => $actions, 'user' => $user]); + } +} diff --git a/app/Http/Controllers/Admin/ActionLogOverview.php b/app/Http/Controllers/Admin/ActionLogOverview.php deleted file mode 100644 index 1f5d5f08..00000000 --- a/app/Http/Controllers/Admin/ActionLogOverview.php +++ /dev/null @@ -1,35 +0,0 @@ -log = $log; - } - - public function getOverview() - { - $actions = $this->log->getLogActionsQuery(true) - ->paginate(25); - - $this->log->translateLogMessages($actions); - - return view('admin.actionLog.overview', ['actions' => $actions]); - } -} diff --git a/app/Http/Controllers/Admin/ContentOverview.php b/app/Http/Controllers/Admin/Contents/ContentOverview.php similarity index 70% rename from app/Http/Controllers/Admin/ContentOverview.php rename to app/Http/Controllers/Admin/Contents/ContentOverview.php index d6b95f39..6e96e6d5 100644 --- a/app/Http/Controllers/Admin/ContentOverview.php +++ b/app/Http/Controllers/Admin/Contents/ContentOverview.php @@ -1,7 +1,8 @@ handler; + + if (is_callable([$module->handler, 'getOverviewTitle'])) { + $pageHeading = call_user_func([$module->handler, 'getOverviewTitle']); + } } // get pages for overview @@ -47,10 +53,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/Contents/CreateNewContent.php similarity index 75% rename from app/Http/Controllers/Admin/CreateNewContent.php rename to app/Http/Controllers/Admin/Contents/CreateNewContent.php index 1beee33b..2ccd4ba4 100644 --- a/app/Http/Controllers/Admin/CreateNewContent.php +++ b/app/Http/Controllers/Admin/Contents/CreateNewContent.php @@ -1,8 +1,10 @@ trans($module->name)]); + if (is_callable([$module->handler, 'getCreateNewTitle'])) { + $pageHeading = call_user_func([$module->handler, 'getCreateNewTitle']); + } + + // get list of usable templates $templates = Template::where('module_id', $moduleId) ->select(['template_name', 'name', 'description']) ->get(); - $categoryModule = ModuleInstance::get('categories'); // add categories, if supported + $categoryModule = ModuleInstance::get('categories'); $categories = []; - if ($module->support_categories && $categoryModule->enabled()) { + if ($module->support_categories && !is_null($categoryModule) && $categoryModule->enabled()) { $categories = Category::where('module_id', $moduleId) ->get(); } @@ -61,6 +86,7 @@ class CreateNewContent extends Controller // render view return view('admin.content.new', [ + 'pageHeading' => $pageHeading, 'contentStatuses' => $statuses, 'templates' => $templates, 'moduleId' => $moduleId, @@ -86,7 +112,6 @@ class CreateNewContent extends Controller */ public function postNew(CreateNewContentRequest $request, $moduleId) { - $page = new Content(); try { $module = Module::findOrFail($moduleId); } catch (NotFoundHttpException $e) { @@ -96,6 +121,10 @@ class CreateNewContent extends Controller ->with('error', trans('admin/contents.invalidModule')); } + // create new page + $page = new Content(); + $page->template = $request->template; + // setup basic info $page->user_id = \Auth::user()->id; $page->language = LaravelLocalization::getCurrentLocale(); @@ -107,20 +136,26 @@ class CreateNewContent extends Controller $page->meta_description = $request->metaDescription; $page->content = $request->pageContent; $page->status = $request->status; - $page->template = $request->template; $page->module_handler = $module->handler; $page->category_id = (int)$request->category > 0 ? $request->category : null; + // is any parent page specified? if ($request->parentPage) { $page->parent_id = $request->parentPage; } // upload file - if provided - $page->uploadTitlePhoto($request); + $file = $page->uploadTitlePhoto($request); + + $page->title_photo = $file ? $file['filename'] : null; // save module-specific stuff call_user_func_array([$module->handler, 'createNew'], [&$page, $request]); + // let template to modify request and content page + call_user_func_array([CurrentTemplate::handler(), 'beforeSave'], [&$page, $request]); + + // save content of page before (possible) uploading related files $page->save(); // upload files assigned to content page diff --git a/app/Http/Controllers/Admin/EditContent.php b/app/Http/Controllers/Admin/Contents/EditContent.php similarity index 83% rename from app/Http/Controllers/Admin/EditContent.php rename to app/Http/Controllers/Admin/Contents/EditContent.php index cae69a9a..44296dcc 100644 --- a/app/Http/Controllers/Admin/EditContent.php +++ b/app/Http/Controllers/Admin/Contents/EditContent.php @@ -1,14 +1,15 @@ history = $history; @@ -56,27 +65,38 @@ 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); + // get module ID + $module = Module::where('handler', $page->module_handler) + ->firstOrFail(); - $templates = Template::where('module_id', $moduleId) + $pageHeading = trans('admin/contents.edit', ['name' => e($page->title)]); + if (is_callable([$module->handler, 'getCreateNewTitle'])) { + $pageHeading = call_user_func([$module->handler, 'getEditTitle'], $page); + } + + // 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) + if ($module->support_categories && !is_null($categoryModule) && $categoryModule->enabled()) { + $categories = Category::where('module_id', $module->id) ->get(); } + // modify data for edit form according to installed template + call_user_func_array([CurrentTemplate::handler(), 'beforePopulatingEditForm'], [&$page]); + $history = $this->history->getStructuredHistoryOf($page); // render view return view('admin.content.edit', [ + 'pageHeading' => $pageHeading, 'contentStatuses' => $statuses, 'page' => $page, 'module' => $module, @@ -118,12 +138,20 @@ 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([CurrentTemplate::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); + if (!$file) { + $request->titlePhotoFilename = empty($request->existingTitlePhoto) ? null : $request->existingTitlePhoto; + } else { + $request->titlePhotoFilename = $file['filename']; + } // array of fields in database and fields from request $fields = [ @@ -142,7 +170,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]); @@ -160,7 +189,7 @@ class EditContent extends Controller // something was changed, inform user return redirect() - ->route('content.edit', ['id' => $page->id]) + ->route('content.overview') ->with('success', trans('admin/contents.successfullyEdited')); } @@ -178,7 +207,6 @@ class EditContent extends Controller { // get possible statuses $statuses = ContentStatuses::getWithTranslations(); - unset($statuses[Content::STATUS_DELETED]); // check if given status is valid if (!isset($statuses[$status])) { @@ -241,23 +269,17 @@ class EditContent extends Controller // change url to something unique $oldURL = $page->url; - $page->url = uniqid($page->url . '_deleted'); + $page->url .= uniqid('deleted_'); $page->save(); - list ($revision, $changedColumns) = $page->updateWithHistory(['status' => 'status'], ['status' => Content::STATUS_DELETED]); + // delete page + $page->delete(); // let module to delete some extra content created during page's life call_user_func([$page->module_handler, 'delete'], $page); - // was something changed? - if ($changedColumns == 0) { - return redirect() - ->route('content.edit', ['id' => $page->id]) - ->with('error', trans('admin/contents.nothingToEdit')); - } - // trigger delete event - Event::fire(new DeleteContent($page, $revision)); + Event::fire(new DeleteContent($page)); // redirect back to overview with info for user return redirect() @@ -286,7 +308,7 @@ class EditContent extends Controller } catch (NotFoundHttpException $e) { // page not found return redirect() - ->route('content.overview') + ->back() ->with('error', trans('admin/contents.invalidPage')); } @@ -301,7 +323,7 @@ class EditContent extends Controller // was something changed? if ($changedColumns == 0) { return redirect() - ->route('content.overview') + ->back() ->with('error', trans('admin/contents.nothingToEdit')); } @@ -310,7 +332,7 @@ class EditContent extends Controller // redirect back to overview with info for user return redirect() - ->route('content.overview') + ->back() ->with('success', trans('admin/contents.homepageSetup', [ 'name' => $page->title, 'url' => $page->url diff --git a/app/Http/Controllers/Admin/History.php b/app/Http/Controllers/Admin/Contents/History.php similarity index 92% rename from app/Http/Controllers/Admin/History.php rename to app/Http/Controllers/Admin/Contents/History.php index f7ad6d86..a58cdbcf 100644 --- a/app/Http/Controllers/Admin/History.php +++ b/app/Http/Controllers/Admin/Contents/History.php @@ -1,6 +1,7 @@ route('content.overview'); - -// return view('admin.dashboard'); - } -} diff --git a/app/Http/Controllers/Admin/Dashboard/Dashboard.php b/app/Http/Controllers/Admin/Dashboard/Dashboard.php new file mode 100644 index 00000000..3e666127 --- /dev/null +++ b/app/Http/Controllers/Admin/Dashboard/Dashboard.php @@ -0,0 +1,14 @@ +route('content.overview'); + } +} diff --git a/app/Http/Controllers/Admin/CreateNewDirectory.php b/app/Http/Controllers/Admin/FileManager/CreateNewDirectory.php similarity index 93% rename from app/Http/Controllers/Admin/CreateNewDirectory.php rename to app/Http/Controllers/Admin/FileManager/CreateNewDirectory.php index 31e960fa..8f6bfc00 100644 --- a/app/Http/Controllers/Admin/CreateNewDirectory.php +++ b/app/Http/Controllers/Admin/FileManager/CreateNewDirectory.php @@ -1,8 +1,8 @@ route('upload.overview', ['id' => $request->parentDirectory]) - ->with('error', trans('admin/fileManager.subdirectoryExists')) - ->withInput(); + ->withInput() + ->with('error', trans('admin/fileManager.subdirectoryExists')); } } diff --git a/app/Http/Controllers/Admin/EditDirectory.php b/app/Http/Controllers/Admin/FileManager/EditDirectory.php similarity index 88% rename from app/Http/Controllers/Admin/EditDirectory.php rename to app/Http/Controllers/Admin/FileManager/EditDirectory.php index 7992a7cb..d3fd42d2 100644 --- a/app/Http/Controllers/Admin/EditDirectory.php +++ b/app/Http/Controllers/Admin/FileManager/EditDirectory.php @@ -1,8 +1,8 @@ route('upload.overview', ['id' => $directoryId]) - ->with('error', trans('admin/fileManager.subdirectoryExists')) - ->withInput(); + ->withInput() + ->with('error', trans('admin/fileManager.subdirectoryExists')); } } diff --git a/app/Http/Controllers/Admin/EditUploadedFile.php b/app/Http/Controllers/Admin/FileManager/EditUploadedFile.php similarity index 96% rename from app/Http/Controllers/Admin/EditUploadedFile.php rename to app/Http/Controllers/Admin/FileManager/EditUploadedFile.php index dc923eba..c29bb82a 100644 --- a/app/Http/Controllers/Admin/EditUploadedFile.php +++ b/app/Http/Controllers/Admin/FileManager/EditUploadedFile.php @@ -1,10 +1,10 @@ file = $file; } - /** * Uploads any file * - * @return \Illuminate\Http\RedirectResponse + * @param Request $request + * + * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse */ public function postFile(Request $request) { @@ -38,12 +40,6 @@ class UploadFile extends Controller $uploadedFiles[] = $uploadedFile; } - // we do not want to redirect - if ($request->ajax()) { - return $this->jsonResponse($uploadedFiles); - } - - // upload was not performed via ajax, redirect user return redirect() ->route('upload.overview', ['directory' => $request->directoryId]) ->with('success', trans_choice('admin/fileManager.uploadSuccessfull', $successfullUploads)); @@ -52,31 +48,17 @@ class UploadFile extends Controller } } + /** + * Send message and redirect to upload.overview when upload failed + * + * @param $message + * + * @return \Illuminate\Http\RedirectResponse + */ protected function invalidUpload($message) { return redirect() ->route('upload.overview') ->with('error', trans('admin/fileManager.' . $message)); } - - /** - * Send response about uploaded file as JSON - * - * @param $uploadedFile - * - * @return \Illuminate\Http\JsonResponse - */ - private function jsonResponse($uploadedFile) - { - $filename = $uploadedFile['file']->getFilename(); - $url = config('app.upload_path') . DIRECTORY_SEPARATOR . $filename; - - $data = [ - 'filename' => $filename, - 'url' => $url, - 'id' => $uploadedFile['id'] - ]; - - return response()->json($data); - } } \ No newline at end of file diff --git a/app/Http/Controllers/Admin/UploadedFilesOverview.php b/app/Http/Controllers/Admin/FileManager/UploadedFilesOverview.php similarity index 93% rename from app/Http/Controllers/Admin/UploadedFilesOverview.php rename to app/Http/Controllers/Admin/FileManager/UploadedFilesOverview.php index 6b5c0a21..c8d1def9 100644 --- a/app/Http/Controllers/Admin/UploadedFilesOverview.php +++ b/app/Http/Controllers/Admin/FileManager/UploadedFilesOverview.php @@ -1,9 +1,7 @@ loginFailed(); } + /** + * Logs out user + * + * @return \Illuminate\Http\RedirectResponse + */ + public function logout() + { + + // remove user information + \Auth::logout(); + + // redirect to login form with info about successfull logout + return redirect() + ->route('login') + ->with('success', trans('admin/common.logoutSuccessfull')); + } + private function loginFailed() { return redirect() diff --git a/app/Http/Controllers/Admin/CopyTemplate.php b/app/Http/Controllers/Admin/Templates/CopyTemplate.php similarity index 93% rename from app/Http/Controllers/Admin/CopyTemplate.php rename to app/Http/Controllers/Admin/Templates/CopyTemplate.php index f4598804..1a407547 100644 --- a/app/Http/Controllers/Admin/CopyTemplate.php +++ b/app/Http/Controllers/Admin/Templates/CopyTemplate.php @@ -1,12 +1,10 @@ template_name, '::')) { list($namespace) = explode('::', $template->template_name); diff --git a/app/Http/Controllers/Admin/EditTemplate.php b/app/Http/Controllers/Admin/Templates/EditTemplate.php similarity index 95% rename from app/Http/Controllers/Admin/EditTemplate.php rename to app/Http/Controllers/Admin/Templates/EditTemplate.php index e7995dc5..fb372b83 100644 --- a/app/Http/Controllers/Admin/EditTemplate.php +++ b/app/Http/Controllers/Admin/Templates/EditTemplate.php @@ -1,6 +1,6 @@ getPath(); @@ -89,7 +92,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/TemplateOverview.php b/app/Http/Controllers/Admin/Templates/TemplateOverview.php similarity index 81% rename from app/Http/Controllers/Admin/TemplateOverview.php rename to app/Http/Controllers/Admin/Templates/TemplateOverview.php index f766b14e..8baf7300 100644 --- a/app/Http/Controllers/Admin/TemplateOverview.php +++ b/app/Http/Controllers/Admin/Templates/TemplateOverview.php @@ -1,11 +1,9 @@ id; } - // @todo add some authorization (e.g. via can/cannot methods) - // try to find given user - $user = User::where('status', '!=', User::STATE_DELETED) - ->findOrFail($id); + $user = User::findOrFail($id); $states = UserStates::getWithTranslations(); $roles = UserRoles::getWithTranslations(); @@ -87,7 +83,7 @@ class EditUser extends Controller Event::fire(new EditUserEvent($user)); return redirect() - ->route('users.detail', ['id' => $id]) + ->back() ->with('success', trans('admin/userManagement.userSuccessfullyEdited')); } } \ No newline at end of file diff --git a/app/Http/Controllers/Admin/UserDetail.php b/app/Http/Controllers/Admin/Users/UserDetail.php similarity index 86% rename from app/Http/Controllers/Admin/UserDetail.php rename to app/Http/Controllers/Admin/Users/UserDetail.php index 46c4d461..60e78ebc 100644 --- a/app/Http/Controllers/Admin/UserDetail.php +++ b/app/Http/Controllers/Admin/Users/UserDetail.php @@ -1,10 +1,10 @@ log->getUserActions($id); + $actions = $this->log->getUserActions($id, 10); $roles = UserRoles::getWithTranslations(); $states = UserStates::getWithTranslations(); diff --git a/app/Http/Controllers/Admin/UsersOverview.php b/app/Http/Controllers/Admin/Users/UsersOverview.php similarity index 64% rename from app/Http/Controllers/Admin/UsersOverview.php rename to app/Http/Controllers/Admin/Users/UsersOverview.php index e26513cb..86bf137d 100644 --- a/app/Http/Controllers/Admin/UsersOverview.php +++ b/app/Http/Controllers/Admin/Users/UsersOverview.php @@ -1,24 +1,21 @@ ', User::STATE_DELETED) - ->orderBy('id', 'desc'); + $users = User::orderBy('id', 'desc'); // get statuses $statuses = UserStates::getWithTranslations(); $statuses[''] = trans('admin/userManagement.allStatuses'); - unset($statuses[User::STATE_DELETED]); // filter using status if (!is_null($status) && isset($statuses[$status])) { @@ -29,9 +26,9 @@ class UsersOverview extends Controller $users = $users->paginate($perPage); return view('admin.userManagement.overview', [ - 'users' => $users, - 'roles' => UserRoles::getWithTranslations(), - 'statuses' => $statuses, + 'users' => $users, + 'roles' => UserRoles::getWithTranslations(), + 'statuses' => $statuses, 'currentStatus' => $status ]); } diff --git a/app/Http/Controllers/Admin/CreateNewWidgetArea.php b/app/Http/Controllers/Admin/Widgets/Areas/CreateNewWidgetArea.php similarity index 85% rename from app/Http/Controllers/Admin/CreateNewWidgetArea.php rename to app/Http/Controllers/Admin/Widgets/Areas/CreateNewWidgetArea.php index 9fe19ce6..91c22e35 100644 --- a/app/Http/Controllers/Admin/CreateNewWidgetArea.php +++ b/app/Http/Controllers/Admin/Widgets/Areas/CreateNewWidgetArea.php @@ -1,9 +1,8 @@ new WidgetArea()]); } /** diff --git a/app/Http/Controllers/Admin/EditWidgetArea.php b/app/Http/Controllers/Admin/Widgets/Areas/EditWidgetArea.php similarity index 60% rename from app/Http/Controllers/Admin/EditWidgetArea.php rename to app/Http/Controllers/Admin/Widgets/Areas/EditWidgetArea.php index 7dc3f31e..e2a9b834 100644 --- a/app/Http/Controllers/Admin/EditWidgetArea.php +++ b/app/Http/Controllers/Admin/Widgets/Areas/EditWidgetArea.php @@ -1,23 +1,45 @@ id); + $widgets = Widget::where('language', LaravelLocalization::getCurrentLocale()) + ->paginate(25); - return view('admin.widgets.area.edit', ['area' => $area]); + return view('admin.widgets.area.edit', [ + 'area' => $area, + 'widgets' => $widgets, + 'associatedWidgetIds' => $associatedWidgetIds + ]); } + /** + * @param EditWidgetAreaRequest $request + * @param $id + * + * @return \Illuminate\Http\RedirectResponse + */ public function postEdit(EditWidgetAreaRequest $request, $id) { try { @@ -35,7 +57,7 @@ class EditWidgetArea extends Controller Event::fire(new \App\Events\EditWidgetArea($area)); return redirect() - ->route('widgetareas.edit', ['id' => $id]) + ->route('widgetareas.overview') ->with('success', trans('admin/widgetAreas.editSuccessfull')); } @@ -55,6 +77,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/Admin/WidgetAreaOverview.php b/app/Http/Controllers/Admin/Widgets/Areas/WidgetAreaOverview.php similarity index 84% rename from app/Http/Controllers/Admin/WidgetAreaOverview.php rename to app/Http/Controllers/Admin/Widgets/Areas/WidgetAreaOverview.php index 079752cf..d813d1f2 100644 --- a/app/Http/Controllers/Admin/WidgetAreaOverview.php +++ b/app/Http/Controllers/Admin/Widgets/Areas/WidgetAreaOverview.php @@ -1,8 +1,8 @@ route('widgets.associate', ['widgetArea' => $widgetAreaId]) + ->back() ->with('success', trans('admin/widgets.successfullyAssociated')); } diff --git a/app/Http/Controllers/Admin/CreateNewWidget.php b/app/Http/Controllers/Admin/Widgets/CreateNewWidget.php similarity index 95% rename from app/Http/Controllers/Admin/CreateNewWidget.php rename to app/Http/Controllers/Admin/Widgets/CreateNewWidget.php index 405d6739..39443084 100644 --- a/app/Http/Controllers/Admin/CreateNewWidget.php +++ b/app/Http/Controllers/Admin/Widgets/CreateNewWidget.php @@ -1,8 +1,8 @@ deletable) { + return redirect() + ->back() + ->with('error', trans('admin/widgets.notDeletable')); + } + $widget->delete(); // redirect with success notification diff --git a/app/Http/Controllers/Admin/WidgetsOverview.php b/app/Http/Controllers/Admin/Widgets/WidgetsOverview.php similarity index 81% rename from app/Http/Controllers/Admin/WidgetsOverview.php rename to app/Http/Controllers/Admin/Widgets/WidgetsOverview.php index 424c2e84..4dc6ba1d 100644 --- a/app/Http/Controllers/Admin/WidgetsOverview.php +++ b/app/Http/Controllers/Admin/Widgets/WidgetsOverview.php @@ -1,6 +1,7 @@ status == Content::STATUS_PROTECTED && !Auth::user()) { - throw new \Exception('Cannot access protected resources'); +// throw new \Exception('Cannot access protected resources'); + abort(404); } - // get handler - $handler = new $page->module_handler; + // add one new "view" for page + $page->view(); - // render page - return $handler->render($page, ''); + return PageRenderer::render($page); } } diff --git a/app/Http/Controllers/Visitor/Homepage.php b/app/Http/Controllers/Visitor/Homepage.php index 00c0264d..a40ba1da 100644 --- a/app/Http/Controllers/Visitor/Homepage.php +++ b/app/Http/Controllers/Visitor/Homepage.php @@ -17,10 +17,10 @@ class Homepage extends ContentPage } // render page - try { +// try { return $this->renderPage($page); - } catch (\Exception $e) { - return view('errors.homepageNotAvailable', ['exception' => $e, 'reason' => 'exception']); - } +// } catch (\Exception $e) { +// return view('errors.homepageNotAvailable', ['exception' => $e, 'reason' => 'exception']); +// } } } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 15940382..a3f84d26 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -2,8 +2,13 @@ namespace App\Http; +use App\Http\Middleware\TemplatePathInstaller; +use App\Http\Middleware\WidgetInstaller; use Illuminate\Foundation\Http\Kernel as HttpKernel; +use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode; use Illuminate\Routing\Middleware\ThrottleRequests; +use Illuminate\Session\Middleware\StartSession; +use Illuminate\View\Middleware\ShareErrorsFromSession; use Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRedirectFilter; use Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRoutes; use Mcamara\LaravelLocalization\Middleware\LocaleSessionRedirect; @@ -18,7 +23,12 @@ class Kernel extends HttpKernel * @var array */ protected $middleware = [ - \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, + LocaleSessionRedirect::class, + LaravelLocalizationRedirectFilter::class, + Middleware\EncryptCookies::class, + StartSession::class, + ShareErrorsFromSession::class, + Middleware\VerifyCsrfToken::class ]; /** * The application's route middleware groups. @@ -26,14 +36,16 @@ class Kernel extends HttpKernel * @var array */ protected $middlewareGroups = [ - 'web' => [ - Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - Middleware\VerifyCsrfToken::class, + 'web' => [ + CheckForMaintenanceMode::class, ], - 'api' => [ + 'admin' => [ + Middleware\Authenticate::class, + Middleware\AdminMenuRegistrator::class, + TemplatePathInstaller::class, + WidgetInstaller::class, + ], + 'api' => [ 'throttle:60,1', ], ]; diff --git a/app/Http/Middleware/AdminMenuRegistrator.php b/app/Http/Middleware/AdminMenuRegistrator.php index 6c35d6f6..88d9549d 100644 --- a/app/Http/Middleware/AdminMenuRegistrator.php +++ b/app/Http/Middleware/AdminMenuRegistrator.php @@ -33,6 +33,9 @@ class AdminMenuRegistrator // iterate over all enabled modules foreach ($modules as $key => $module) { + if (!is_callable([$module->handler, 'registerAdminMenu'])) + continue; + // try to register menus try { call_user_func([$module->handler, 'registerAdminMenu'], $user); diff --git a/app/Http/Middleware/TemplatePathInstaller.php b/app/Http/Middleware/TemplatePathInstaller.php new file mode 100644 index 00000000..f06d1446 --- /dev/null +++ b/app/Http/Middleware/TemplatePathInstaller.php @@ -0,0 +1,31 @@ +saveTemplates(); + + return $next($request); + } +} diff --git a/app/Http/Middleware/WidgetInstaller.php b/app/Http/Middleware/WidgetInstaller.php new file mode 100644 index 00000000..883819f3 --- /dev/null +++ b/app/Http/Middleware/WidgetInstaller.php @@ -0,0 +1,30 @@ +installWidgets(); + + return $next($request); + } +} diff --git a/app/Http/Requests/CreateNewContentRequest.php b/app/Http/Requests/CreateNewContentRequest.php index 7284b6ae..df25858b 100644 --- a/app/Http/Requests/CreateNewContentRequest.php +++ b/app/Http/Requests/CreateNewContentRequest.php @@ -15,7 +15,6 @@ class CreateNewContentRequest extends Request 'titlePhoto' => 'image', 'title' => 'required', 'url' => 'required|unique:contents,url', - 'template' => 'required|exists:templates,template_name', 'status' => 'required|in:draft,protected,public', 'title_photo' => 'exists:files,id', 'parentPage' => 'exists:contents,id', diff --git a/app/Http/Requests/EditContentRequest.php b/app/Http/Requests/EditContentRequest.php index f92504b4..e04878e6 100644 --- a/app/Http/Requests/EditContentRequest.php +++ b/app/Http/Requests/EditContentRequest.php @@ -20,7 +20,6 @@ class EditContentRequest extends Request return [ 'title' => 'required', 'url' => 'required|unique:contents,url,' . $this->id, - 'template' => 'required|exists:templates,template_name', 'published_from' => 'date', 'published_to' => 'date', 'title_photo' => 'exists:files,id', 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/Http/routes.php b/app/Http/routes.php index d1913d45..4d9c385d 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -13,246 +13,36 @@ Route::pattern('url', '[0-9\-a-z]+'); Route::group([ 'prefix' => LaravelLocalization::setLocale(), - 'middleware' => ['localeSessionRedirect', 'localizationRedirect'] ], function () { /** * This route group serves to handle request in admin */ Route::group([ - 'middleware' => ['web', 'auth', 'adminMenu'], + 'middleware' => ['admin'], 'namespace' => 'Admin', 'prefix' => 'admin' ], function () { - // these routes are available only for user with role admin - Route::group(['middleware' => 'onlyAdmin'], function () { - // display form for creating new user - Route::get('users/new', 'CreateNewUser@getNewUser') - ->name('users.new'); - - // POST methods - // display form for creating new user - Route::post('users/new', 'CreateNewUser@postRegistration') - ->name('users.newPost'); - - // display form for edit of existing user - Route::get('users/edit/{id}', 'EditUser@getEditUser') - ->name('users.edit'); - - // form for adding new custom field - Route::get('customfields/new', 'CreateNewCustomField@getNew') - ->name('customfields.new'); - - // form for editing custom field - Route::get('customfields/edit/{id}', 'EditCustomField@getEditCustomField') - ->name('customfields.edit'); - - // form for adding new custom field - Route::post('customfields/new', 'CreateNewCustomField@postNew') - ->name('customfields.newPost'); - - // form for editing custom field - Route::post('customfields/edit/{id}', 'EditCustomField@postEditCustomField') - ->name('customfields.editPost'); - - Route::get('customfields/delete/{id}', 'EditCustomField@deleteCustomField') - ->name('customfields.delete'); - - Route::get('templates/overview/path/{id}', 'TemplateOverview@getPath') - ->name('templates.overview'); - - Route::post('templates/copy', 'CopyTemplate@createCopy') - ->name('templates.postCopy'); - - // Route::get('templates/new/{pathId}', 'TemplateOverview@getPath') - // ->name('templates.new'); - - Route::get('templates/edit/{id}', 'EditTemplate@getEdit') - ->name('templates.edit'); - - Route::post('templates/edit/{id}', 'EditTemplate@postEdit') - ->name('templates.postEdit'); - }); - - // list of all static pages - Route::get('content/overview/{moduleId?}/{status?}', 'ContentOverview@getOverview') - ->name('content.overview'); - - // form for creating new static page - Route::get('content/new/{moduleId}', 'CreateNewContent@getNew') - ->name('content.new'); - - // form for editing existing static page - Route::get('content/edit/{id}', 'EditContent@getEdit') - ->name('content.edit'); - - // post method for creating new static page - Route::post('content/new/{moduleId}', 'CreateNewContent@postNew') - ->name('content.newPost'); - - // post method for creating new static page - Route::post('content/edit/{id}', 'EditContent@postEdit') - ->name('content.editPost'); - - // routes for quick actions - - // change status of page (protected, public or draft) - Route::get('content/status/{id}/{status}', 'EditContent@changeStatus') - ->name('content.changeStatus'); - - // set homepage - Route::get('content/homepage/{id}', 'EditContent@setHomepage') - ->name('content.setHomepage'); - - // delete page - Route::get('content/delete/{id}', 'EditContent@deletePage') - ->name('content.delete'); - - // these routes are available for all logged (authenticated) users + // include all routes in routes/admin subfolder + $files = ['contents', 'fileManager', 'settings', 'templates', 'users', 'widgets']; + foreach ($files as $file) { + require_once __DIR__ . DIRECTORY_SEPARATOR . 'routes' . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR . $file . '.php'; + } // display dashboard with some usefull information - Route::get('/', 'Dashboard@getIndex') + Route::get('/', 'Dashboard\Dashboard@getIndex') ->name('admin.dashboard'); - // display user detail in admin - Route::get('users/detail/{id?}', 'UserDetail@getDetail') - ->name('users.detail'); - - // method for saving edited data of user - Route::post('users/edit/{id}', 'EditUser@postEdit') - ->name('users.editPost'); - - // display list of all users registered into our system - Route::get('users/overview/{state?}/{perPage?}', 'UsersOverview@getOverview') - ->name('users.overview'); - - // history of changes for specified page - Route::get('history/{id}', 'History@getOverview') - ->name('content.history'); - - // history of changes for specified page - Route::get('history/{revision}/{id}', 'History@getDiff') - ->name('history.diff'); - // list of all actions - Route::get('actions', 'ActionLogOverview@getOverview') + Route::get('actions', 'ActionLog\Overview@getOverview') ->name('log.overview'); - // routes for widget areas - Route::get('widgets/areas/overview', 'WidgetAreaOverview@getOverview') - ->name('widgetareas.overview'); - - // form for new widget area - Route::get('widgets/areas/new', 'CreateNewWidgetArea@getNew') - ->name('widgetareas.new'); - - // form for new widget area - Route::post('widgets/areas/new', 'CreateNewWidgetArea@postNew') - ->name('widgetareas.postNew'); - - // edit form for widget area - Route::get('widgets/areas/edit/{id}', 'EditWidgetArea@getEdit') - ->name('widgetareas.edit'); - - Route::post('widgets/areas/edit/{id}', 'EditWidgetArea@postEdit') - ->name('widgetareas.postEdit'); - - // edit form for widget area - Route::get('widgets/areas/delete/{id}', 'EditWidgetArea@deleteArea') - ->name('widgetareas.delete'); - - // list of all installed widget types - Route::get('widgets/overview', 'WidgetsOverview@getOverview') - ->name('widgets.overview'); - - // create new instance of widget of given type - Route::get('widgets/new/{type}', 'CreateNewWidget@getNew') - ->name('widgets.new'); - - Route::post('widgets/new/{type}', 'CreateNewWidget@postNew') - ->name('widgets.postNew'); - - // edit widget settings - Route::get('widgets/edit/{id}', 'EditWidget@getEdit') - ->name('widgets.edit'); - - // delete widget settings - Route::get('widgets/delete/{id}', 'EditWidget@deleteWidget') - ->name('widgets.delete'); - - Route::post('widgets/edit/{id}', 'EditWidget@postEdit') - ->name('widgets.postEdit'); - - // display form for association of widget and widget area - Route::get('widgets/associate/{widgetArea}', 'AssociateWidget@getList') - ->name('widgets.associate'); - - Route::get('widgets/deassociate/{widgetId}/{widgetArea}', 'AssociateWidget@deleteAssociation') - ->name('widgets.deleteAssociation'); - - Route::get('widgets/associate/{widgetId}/{widgetArea}', 'AssociateWidget@makeAssociation') - ->name('widgets.makeAssociation'); - - // uploader methods - Route::get('directories/new/{id?}', 'CreateNewDirectory@getNew') - ->name('upload.newDirectory'); - - Route::get('directories/edit/{id}', 'EditDirectory@getEdit') - ->name('upload.editDirectory'); - - Route::post('directories/edit/{id}', 'EditDirectory@postEdit') - ->name('upload.postEditDirectory'); - - Route::get('files/overview/{id?}/{type?}/{subtype?}', 'UploadedFilesOverview@getOverview') - ->name('upload.overview'); - - route::post('directories/new', 'CreateNewDirectory@postNew') - ->name('upload.postNewDirectory'); - - Route::get('upload/detail/{id}', 'EditUploadedFile@getDetail') - ->name('upload.detail'); - - Route::post('upload/detail/{id}', 'EditUploadedFile@postEdit') - ->name('upload.postEdit'); - - Route::get('upload/delete/{id}', 'EditUploadedFile@deleteFile') - ->name('upload.delete'); - - // uploader methods - Route::post('upload/file', 'UploadFile@postFile') - ->name('upload.postFile'); - - Route::post('upload/image', 'UploadFile@postImage') - ->name('upload.postImage'); - - // form with settings - Route::get('settings', 'Settings@getOverview') - ->name('settings.overview'); - - Route::post('settings', 'Settings@saveSettings') - ->name('settings.save'); - + // search form with results Route::get('search', 'Search@getSearch') ->name('search'); - // custom fields - // ------------------------------ - - // list of all custom fields - Route::get('customfields/overview', 'CustomFieldsOverview@getOverview') - ->name('customfields.overview'); - // logout method - Route::get('logout', function () { - - // remove user information - \Auth::logout(); - - // redirect to login form with info about successfull logout - return redirect() - ->route('login') - ->with('success', trans('admin/common.logoutSuccessfull')); - }) + Route::get('logout', 'Login@logout') ->name('logout'); }); @@ -264,13 +54,12 @@ Route::group([ Route::group(['middleware' => ['web']], function () { + // Homepage Route::get('/', 'Visitor\Homepage@getIndex') ->name('page.index'); // display login form - Route::get('admin/login', function () { - return view('admin.loginForm'); - }) + Route::get('admin/login', 'Admin\Login@getLogin') ->name('loginForm'); // handle login diff --git a/app/Http/routes/admin/contents.php b/app/Http/routes/admin/contents.php new file mode 100644 index 00000000..6e7b6a6a --- /dev/null +++ b/app/Http/routes/admin/contents.php @@ -0,0 +1,42 @@ + 'Contents'], function () { + // list of all static pages + Route::get('content/overview/{moduleId?}/{status?}', 'ContentOverview@getOverview') + ->name('content.overview'); + + // form for creating new static page + Route::get('content/new/{moduleId}', 'CreateNewContent@getNew') + ->name('content.new'); + + // form for editing existing static page + Route::get('content/edit/{id}', 'EditContent@getEdit') + ->name('content.edit'); + + // post method for creating new static page + Route::post('content/new/{moduleId}', 'CreateNewContent@postNew') + ->name('content.newPost'); + + // post method for creating new static page + Route::post('content/edit/{id}', 'EditContent@postEdit') + ->name('content.editPost'); + + // change status of page (protected, public or draft) + Route::get('content/status/{id}/{status}', 'EditContent@changeStatus') + ->name('content.changeStatus'); + + // set homepage + Route::get('content/homepage/{id}', 'EditContent@setHomepage') + ->name('content.setHomepage'); + + // delete page + Route::get('content/delete/{id}', 'EditContent@deletePage') + ->name('content.delete'); + + // history of changes for specified page + Route::get('history/{id}', 'History@getOverview') + ->name('content.history'); + + // history of changes for specified page + Route::get('history/{revision}/{id}', 'History@getDiff') + ->name('history.diff'); +}); \ No newline at end of file diff --git a/app/Http/routes/admin/fileManager.php b/app/Http/routes/admin/fileManager.php new file mode 100644 index 00000000..58eecfaf --- /dev/null +++ b/app/Http/routes/admin/fileManager.php @@ -0,0 +1,32 @@ + 'FileManager'], function () { + + Route::get('files/overview/{id?}/{type?}/{subtype?}', 'UploadedFilesOverview@getOverview') + ->name('upload.overview'); + + // uploader methods + Route::get('directories/new/{id?}', 'CreateNewDirectory@getNew') + ->name('upload.newDirectory'); + + Route::get('directories/edit/{id}', 'EditDirectory@getEdit') + ->name('upload.editDirectory'); + + Route::post('directories/edit/{id}', 'EditDirectory@postEdit') + ->name('upload.postEditDirectory'); + + route::post('directories/new', 'CreateNewDirectory@postNew') + ->name('upload.postNewDirectory'); + + Route::get('upload/detail/{id}', 'EditUploadedFile@getDetail') + ->name('upload.detail'); + + Route::post('upload/detail/{id}', 'EditUploadedFile@postEdit') + ->name('upload.postEdit'); + + Route::get('upload/delete/{id}', 'EditUploadedFile@deleteFile') + ->name('upload.delete'); + + // uploader methods + Route::post('upload', 'UploadFile@postFile') + ->name('upload.postFile'); +}); diff --git a/app/Http/routes/admin/settings.php b/app/Http/routes/admin/settings.php new file mode 100644 index 00000000..70b2b996 --- /dev/null +++ b/app/Http/routes/admin/settings.php @@ -0,0 +1,7 @@ +name('settings.overview'); + +Route::post('settings', 'Settings@saveSettings') + ->name('settings.save'); \ No newline at end of file diff --git a/app/Http/routes/admin/templates.php b/app/Http/routes/admin/templates.php new file mode 100644 index 00000000..bb265533 --- /dev/null +++ b/app/Http/routes/admin/templates.php @@ -0,0 +1,15 @@ + 'onlyAdmin', 'namespace' => 'Templates'], function () { + Route::get('templates/overview/path/{id}', 'TemplateOverview@getPath') + ->name('templates.overview'); + + Route::post('templates/copy', 'CopyTemplate@createCopy') + ->name('templates.postCopy'); + + Route::get('templates/edit/{id}', 'EditTemplate@getEdit') + ->name('templates.edit'); + + Route::post('templates/edit/{id}', 'EditTemplate@postEdit') + ->name('templates.postEdit'); +}); \ No newline at end of file diff --git a/app/Http/routes/admin/users.php b/app/Http/routes/admin/users.php new file mode 100644 index 00000000..69d643f7 --- /dev/null +++ b/app/Http/routes/admin/users.php @@ -0,0 +1,27 @@ + 'Users'], function () { + // display form for creating new user + Route::get('users/new', 'CreateNewUser@getNewUser') + ->name('users.new'); + + // POST methods + // display form for creating new user + Route::post('users/new', 'CreateNewUser@postRegistration') + ->name('users.newPost'); + + // display form for edit of existing user + Route::get('users/edit/{id}', 'EditUser@getEditUser') + ->name('users.edit'); + + // display user detail in admin + Route::get('users/detail/{id?}', 'UserDetail@getDetail') + ->name('users.detail'); + + // method for saving edited data of user + Route::post('users/edit/{id}', 'EditUser@postEdit') + ->name('users.editPost'); + + // display list of all users registered into our system + Route::get('users/overview/{state?}/{perPage?}', 'UsersOverview@getOverview') + ->name('users.overview'); +}); diff --git a/app/Http/routes/admin/widgets.php b/app/Http/routes/admin/widgets.php new file mode 100644 index 00000000..d10df3fd --- /dev/null +++ b/app/Http/routes/admin/widgets.php @@ -0,0 +1,60 @@ + 'Widgets'], function () { + + // list of all installed widget types + Route::get('widgets/overview', 'WidgetsOverview@getOverview') + ->name('widgets.overview'); + + // create new instance of widget of given type + Route::get('widgets/new/{type}', 'CreateNewWidget@getNew') + ->name('widgets.new'); + + Route::post('widgets/new/{type}', 'CreateNewWidget@postNew') + ->name('widgets.postNew'); + + // edit widget settings + Route::get('widgets/edit/{id}', 'EditWidget@getEdit') + ->name('widgets.edit'); + + // delete widget settings + Route::get('widgets/delete/{id}', 'EditWidget@deleteWidget') + ->name('widgets.delete'); + + Route::post('widgets/edit/{id}', 'EditWidget@postEdit') + ->name('widgets.postEdit'); + + // display form for association of widget and widget area + Route::get('widgets/associate/{widgetArea}', 'AssociateWidget@getList') + ->name('widgets.associate'); + + Route::get('widgets/deassociate/{widgetId}/{widgetArea}', 'AssociateWidget@deleteAssociation') + ->name('widgets.deleteAssociation'); + + Route::get('widgets/associate/{widgetId}/{widgetArea}', 'AssociateWidget@makeAssociation') + ->name('widgets.makeAssociation'); + + Route::group(['namespace' => 'Areas'], function () { + Route::get('widgets/areas/overview', 'WidgetAreaOverview@getOverview') + ->name('widgetareas.overview'); + + // form for new widget area + Route::get('widgets/areas/new', 'CreateNewWidgetArea@getNew') + ->name('widgetareas.new'); + + // form for new widget area + Route::post('widgets/areas/new', 'CreateNewWidgetArea@postNew') + ->name('widgetareas.postNew'); + + // edit form for widget area + Route::get('widgets/areas/edit/{id}', 'EditWidgetArea@getEdit') + ->name('widgetareas.edit'); + + Route::post('widgets/areas/edit/{id}', 'EditWidgetArea@postEdit') + ->name('widgetareas.postEdit'); + + // edit form for widget area + Route::get('widgets/areas/delete/{id}', 'EditWidgetArea@deleteArea') + ->name('widgetareas.delete'); + }); +}); diff --git a/app/Listeners/LogDeleteContent.php b/app/Listeners/LogDeleteContent.php index 1c443dd4..f5e9ff47 100644 --- a/app/Listeners/LogDeleteContent.php +++ b/app/Listeners/LogDeleteContent.php @@ -22,7 +22,6 @@ class LogDeleteContent extends Logger 'id' => $event->page->id, 'url' => $event->page->url, 'title' => $event->page->title, - 'revision' => $event->revision ]; $this->saveLog($parameters); diff --git a/app/Models/Content.php b/app/Models/Content.php index 8d13ee10..6dee27a4 100644 --- a/app/Models/Content.php +++ b/app/Models/Content.php @@ -5,6 +5,7 @@ namespace App\Models; use App\Enumerations\ContentStatuses; use App\Http\Requests\Request; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\DB; use Mcamara\LaravelLocalization\Facades\LaravelLocalization; @@ -15,6 +16,7 @@ use Modules\Categories\Entities\Category; */ class Content extends Model { + use SoftDeletes; public $timestamps = true; protected $fillable = [ 'user_id', @@ -36,9 +38,8 @@ class Content extends Model 'status' ]; protected $guarded = []; - protected $subpages = []; + protected $foundSubpages = []; protected $subpagesInit = false; - const STATUS_DELETED = 'deleted'; const STATUS_DRAFT = 'draft'; const STATUS_PROTECTED = 'protected'; const STATUS_PUBLIC = 'public'; @@ -59,8 +60,11 @@ class Content extends Model */ public function getPageQuery() { - return Content::where('status', '!=', Content::STATUS_DELETED) - ->where('status', '!=', Content::STATUS_DRAFT); + return Content::where('status', '!=', Content::STATUS_DRAFT) + ->select('contents.*') + ->join('modules', 'modules.handler', '=', 'contents.module_handler') + ->where('language', LaravelLocalization::getCurrentLocale()) + ->where('modules.enabled', true); } /** @@ -70,7 +74,6 @@ class Content extends Model public function getHomepage() { return $this->getPageQuery() - ->where('language', LaravelLocalization::getCurrentLocale()) ->where('is_homepage', 1) ->first(); } @@ -86,32 +89,39 @@ class Content extends Model { return $this->getPageQuery() ->where('url', $url) - ->where('language', LaravelLocalization::getCurrentLocale()) ->first(); } /** * Get list of all subpages * - * @return \Illuminate\Database\Eloquent\Relations\HasMany + * @param bool $orderBy + * @param bool $orderDirection + * + * @return array */ - public function subpages() + public function subpages($orderBy = false, $orderDirection = false) { if (!$this->subpagesInit) { - $this->subpages = $this->getPageQuery() - ->where('parent_id', $this->id) - ->where('language', LaravelLocalization::getCurrentLocale()) - ->get(); + $subpages = $this->getPageQuery() + ->where('contents.parent_id', $this->id); + + if ($orderBy) { + $subpages->orderBy($orderBy, $orderDirection ? $orderDirection : 'ASC'); + } + + $subpages = $subpages->get(); // convert JSON settings into stdClass - foreach ($this->subpages as $subpage) { - $subpage->module_settings = json_decode($subpage->module_settings); + foreach ($subpages as $i => $subpage) { + $subpages[$i]->module_settings = json_decode($subpage->module_settings); } $this->subpagesInit = true; + $this->foundSubpages = $subpages; } - return $this->subpages; + return $this->foundSubpages; } /** @@ -162,7 +172,6 @@ class Content extends Model public function rootPages() { $rootPages = $this->getPageQuery() - ->where('language', LaravelLocalization::getCurrentLocale()) ->where('parent_id', null); if ($this->id) { @@ -179,6 +188,7 @@ class Content extends Model * @param bool $handler * @param bool $status * @param bool $joinAuthor + * @param bool $joinParent * * @return */ @@ -186,9 +196,10 @@ class Content extends Model { $query = DB::table('contents') ->select('contents.*', 'modules.name AS module_name', 'modules.id AS module_id') - ->where('contents.status', '<>', self::STATUS_DELETED) - ->where('contents.language', LaravelLocalization::getCurrentLocale()) ->join('modules', 'modules.handler', '=', 'contents.module_handler') + ->whereNull('contents.deleted_at') + ->where('contents.language', LaravelLocalization::getCurrentLocale()) + ->where('modules.enabled', 1) ->orderBy('created_at', 'DESC'); // filter results using handler @@ -293,10 +304,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; } /** @@ -435,4 +446,12 @@ class Content extends Model // save history ContentHistory::create($history); } + + public function view() + { + $this->timestamps = false; + $this->viewed++; + $this->save(); + $this->timestamps = true; + } } diff --git a/app/Models/File.php b/app/Models/File.php index 1c7c39a0..6c171459 100644 --- a/app/Models/File.php +++ b/app/Models/File.php @@ -4,9 +4,13 @@ namespace App\Models; use App\Events\UploadFile; use App\Http\Requests\Request; +use Franzose\ClosureTable\Extensions\Str; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Event; +use Illuminate\Support\Facades\File as FileFacade; +use Intervention\Image\Constraint; +use Intervention\Image\Facades\Image; use Symfony\Component\HttpFoundation\File\File as SymphonyFile; use Symfony\Component\HttpFoundation\File\UploadedFile; @@ -63,6 +67,12 @@ class File extends Model // save file into physical location (local FS) $uploadedFile = $file->move($destination, $filename); + // is uploaded file an image? + if (Str::startsWith($uploadedFile->getMimeType(), 'image')) { + // yes, create thumbnail + $this->createThumbnail($uploadedFile); + } + // insert record into database $id = $this->saveIntoDatabase($uploadedFile, $request, $file->getClientOriginalName()); @@ -118,4 +128,29 @@ class File extends Model return $newFile->id; } + + /** + * Create thumbnail of given image + * + * @param SymphonyFile $file + */ + private function createThumbnail(SymphonyFile $file) + { + // does thumbnail directory exist? + if (!is_dir(config('app.thumbnail_path'))) { + FileFacade::makeDirectory(config('app.thumbnail_path')); + } + + // get image instance + $image = Image::make(config('app.upload_path') . DIRECTORY_SEPARATOR . $file->getFilename()); + + // resize image and keep aspect ration + $image->resize(env('THUMBNAIL_WIDTH', 300), null, function (Constraint $constraint) { + $constraint->aspectRatio(); + $constraint->upsize(); + }); + + // save thumbnail with lower quality + $image->save(config('app.thumbnail_path') . DIRECTORY_SEPARATOR . $file->getFilename(), 75); + } } \ No newline at end of file diff --git a/app/Models/Module.php b/app/Models/Module.php index d782c2d9..bcf9d696 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,52 @@ 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(resource_path('views/modules')); + $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/Models/User.php b/app/Models/User.php index ef114c18..be576578 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,6 +6,7 @@ use App\Helpers\Gravatar; use Illuminate\Auth\Authenticatable; use Illuminate\Contracts\Auth\Authenticatable as AuthenticableInterface; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Foundation\Auth\Access\Authorizable; /** @@ -13,8 +14,8 @@ use Illuminate\Foundation\Auth\Access\Authorizable; */ class User extends Model implements AuthenticableInterface { - use Authenticatable, Authorizable; - public $timestamps = false; + use SoftDeletes, Authenticatable, Authorizable; + public $timestamps = true; protected $fillable = [ 'email', 'username', diff --git a/app/Models/Widget.php b/app/Models/Widget.php index 5ed96c28..dc0d14bf 100644 --- a/app/Models/Widget.php +++ b/app/Models/Widget.php @@ -17,7 +17,9 @@ class Widget extends Model 'user_id', 'name', 'settings', - 'description' + 'language', + 'description', + 'deletable' ]; protected $guarded = []; @@ -31,7 +33,6 @@ class Widget extends Model */ public function createNew(WidgetType $widgetType, $data) { - // convert array or whatever into object if (!is_object($data)) { $data = (object)$data; diff --git a/app/Models/WidgetArea.php b/app/Models/WidgetArea.php index 009e5f32..1eaf2592 100644 --- a/app/Models/WidgetArea.php +++ b/app/Models/WidgetArea.php @@ -15,7 +15,8 @@ class WidgetArea extends Model protected $fillable = [ 'name', 'code', - 'description' + 'description', + 'deletable' ]; protected $guarded = []; diff --git a/app/Models/WidgetType.php b/app/Models/WidgetType.php index 07330dc1..caa38892 100644 --- a/app/Models/WidgetType.php +++ b/app/Models/WidgetType.php @@ -15,6 +15,8 @@ class WidgetType extends Model protected $fillable = [ 'handler', 'name', + 'template_id', + 'module_id', 'description' ]; diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index fe7104a6..cb1c8fec 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -16,6 +16,7 @@ use App\Widgets\HtmlCode\HtmlCode; use App\Widgets\Menu\Menu; use App\Widgets\OrbitSlider\OrbitSlider; use Illuminate\Support\Facades\App; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\File; use Illuminate\Support\ServiceProvider; use Symfony\Component\Process\Exception\InvalidArgumentException; @@ -30,22 +31,12 @@ class AppServiceProvider extends ServiceProvider { // ignore running from artisan if (App::runningInConsole()) { - $this->publishViewResources(); - return; } $this->registerWidgets(); - $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'); - } } /** @@ -106,24 +97,6 @@ class AppServiceProvider extends ServiceProvider $registrator->register(HtmlCode::class); $registrator->register(Menu::class); $registrator->register(OrbitSlider::class); - - // register some menu extensions - // $menuExtensions = $this->app->make('registerMenuExtension'); - // $menuExtensions->register(new URL()); - } - - /** - * Register path with editable contents - */ - private function registerTemplates() - { - /** - * @var $registrator TemplateRegistrator - */ - $registrator = $this->app->make('registerTemplateDir'); - $registrator->setBaseDirectory('resources/views/visitor'); - - $registrator->register(config('app.theme'), 'admin/templates.core'); } /** @@ -150,37 +123,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..58c943fb 100644 --- a/app/Providers/GlobalViewDataProvider.php +++ b/app/Providers/GlobalViewDataProvider.php @@ -21,11 +21,7 @@ class GlobalViewDataProvider extends ServiceProvider return; } - $settings = new Settings(); - - foreach ($settings->getForView() as $key => $value) { - view()->share($key, $value); - } + view()->share('currentUrl', \Request::url()); $this->extendBlade(); } @@ -65,7 +61,7 @@ class GlobalViewDataProvider extends ServiceProvider return preg_replace($pattern, $code, $view); }); - // shortcut for loading uploaded images + // shortcut for loading uploaded files Blade::extend(function ($view) { $pattern = $this->createPattern('upload'); $code = ''; @@ -73,6 +69,22 @@ class GlobalViewDataProvider extends ServiceProvider return preg_replace($pattern, $code, $view); }); + // shortcut for loading thumbnails of uploaded images + Blade::extend(function ($view) { + $pattern = $this->createPattern('thumbnail'); + $code = ''; + + 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'); @@ -88,6 +100,21 @@ class GlobalViewDataProvider extends ServiceProvider return preg_replace($pattern, $code, $view); }); + Blade::extend(function ($view) { + $pattern = $this->createPattern('customDate'); + $code = ''; + + return preg_replace($pattern, $code, $view); + }); + + Blade::extend(function ($view) { + $pattern = $this->createPattern('monthName'); + $code = ''; + + return preg_replace($pattern, $code, $view); + }); + + // register extension for rendering widget areas Blade::extend(function ($view) { $pattern = $this->createPattern('widgetArea'); 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..f925aec1 100644 --- a/app/Providers/ModuleManager.php +++ b/app/Providers/ModuleManager.php @@ -3,7 +3,6 @@ namespace App\Providers; use App\Models\Module; -use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Log; use Illuminate\Support\ServiceProvider; @@ -15,16 +14,31 @@ 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('enabled', true) + ->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) { + + $module->module_settings = json_decode($module->module_settings); + + if ($module->is_template) { + + if (isset($module->module_settings->providers) && !empty($module->module_settings->providers)) { + foreach ($module->module_settings->providers as $provider) { + $this->app->register($provider); + } + } + + continue; + } + $this->createModulesJson($module); } } @@ -47,7 +61,7 @@ class ModuleManager extends ServiceProvider */ private function createModulesJson(Module $module) { - $config = json_decode($module->module_settings); + $config = $module->module_settings; if (is_null($config)) { return; @@ -58,7 +72,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/SubmodulesManager.php b/app/Providers/SubmodulesManager.php index 26408faf..48907798 100644 --- a/app/Providers/SubmodulesManager.php +++ b/app/Providers/SubmodulesManager.php @@ -9,11 +9,11 @@ use Pingpong\Modules\Json; class SubmodulesManager extends ServiceProvider { /** - * Register the service provider. + * Boot submodules * * @return void */ - public function register() + public function boot() { $enabledModules = \Module::enabled(); @@ -52,4 +52,14 @@ class SubmodulesManager extends ServiceProvider } } } + + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + // TODO: Implement register() method. + } } \ No newline at end of file diff --git a/app/Providers/TemplateServiceProvider.php b/app/Providers/TemplateServiceProvider.php new file mode 100644 index 00000000..c9511a2f --- /dev/null +++ b/app/Providers/TemplateServiceProvider.php @@ -0,0 +1,100 @@ +publishViewResources(); + + return; + } + + $template = CurrentTemplate::get(); + + if (!$template) { + throw new \ErrorException('Template is not set, cannot continue'); + } + + // include files + $files = isset($template->module_settings->files) ? $template->module_settings->files : []; + + foreach ($files as $file) { + include_once $file; + } + + // register templates + $publicTemplates = call_user_func([CurrentTemplate::handler(), 'getPublicTemplatePaths']); + + if (!is_array($publicTemplates)) { + throw new \ErrorException(CurrentTemplate::handler() . '::getPublicTemplatePaths() must return array!'); + } + + /** + * @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']); + + // register path with current template name + + $registrator->setNamespace($namespace); + if (is_callable([config($module . '.handler'), 'getModuleId'])) { + $registrator->setModuleId(call_user_func([config($module . '.handler'), 'getModuleId'])); + } + $registrator->register('resources/views/modules/' . $module, 'visitor', $template['templateName']); + } + + // register also global resources/views/visitor directory + $registrator->register('resources/views/', 'visitor', 'admin/templates.core'); + } + + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + } + + /** + * Publish all public assets from all themes + */ + private function publishViewResources() + { + $template = CurrentTemplate::get(); + if (!$template) { + return; + } + + $templateDir = base_path('templates/' . $template->module_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/app/Widgets/Menu/Menu.php b/app/Widgets/Menu/Menu.php index b3ca55d9..0bb5f1b3 100644 --- a/app/Widgets/Menu/Menu.php +++ b/app/Widgets/Menu/Menu.php @@ -72,9 +72,15 @@ class Menu extends NonInstallableWidget public static function selectContentPages($ids) { - $pages = Content::where('status', '!=', Content::STATUS_DELETED) - ->where('language', LaravelLocalization::getCurrentLocale()) + $allowedStatuses = [Content::STATUS_PUBLIC]; + + if (\Auth::user()) { + $allowedStatuses[] = Content::STATUS_PROTECTED; + } + + $pages = Content::where('language', LaravelLocalization::getCurrentLocale()) ->whereIn('id', $ids) + ->whereIn('status', $allowedStatuses) ->get(); $used = []; foreach ($pages as $page) { diff --git a/app/Widgets/Menu/Presenters/Menu.php b/app/Widgets/Menu/Presenters/Menu.php index db3014d6..f4e38029 100644 --- a/app/Widgets/Menu/Presenters/Menu.php +++ b/app/Widgets/Menu/Presenters/Menu.php @@ -5,10 +5,11 @@ use App\Models\Content; use App\Models\Widget; use App\Models\WidgetType; use App\Widgets\Contracts\WidgetRenderer; +use App\Widgets\Menu\Menu as WidgetMenu; use Illuminate\Support\Facades\App; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Route; -use Mcamara\LaravelLocalization\Facades\LaravelLocalization; class Menu implements WidgetRenderer { @@ -24,11 +25,10 @@ class Menu implements WidgetRenderer public static function render($settings) { $links = (array)$settings->links; - $pageIds = array_keys($links); // get pages - $pages = \App\Widgets\Menu\Menu::selectContentPages($pageIds); + $pages = WidgetMenu::selectContentPages($pageIds); // sort links using "order" uasort($links, function ($a, $b) { @@ -46,18 +46,16 @@ class Menu implements WidgetRenderer if (!isset($pages[$pageId])) { continue; } - $page = $pages[$pageId]; - $active = false; - if (($page->is_homepage && $route == 'page.index') || ($route == 'page.content' && isset($parameters['url']) && $parameters['url'] == $page->url)) { - $active = true; - } + $page = $pages[$pageId]; + $active = ($page->is_homepage && $route == 'page.index') || ($route == 'page.content' && isset($parameters['url']) && $parameters['url'] == $page->url); + $route = $page->is_homepage ? route('page.index') : route('page.content', ['url' => $page->url]); $code .= ' $page->url]) . '">' . e($page->title) . ''; + $code .= '>' . e($page->title) . ''; } $code .= ""; diff --git a/composer.json b/composer.json index 69031af8..de605fc4 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,9 @@ "pingpong/menus": "^2.2", "watson/sitemap": "^2.0", "mcamara/laravel-localization": "^1.1", - "franzose/closure-table": "^4.1" + "franzose/closure-table": "^4.1", + "intervention/image": "^2.3", + "webwizo/laravel-shortcodes": "1.0.4" }, "require-dev": { "fzaninotto/faker": "~1.4", @@ -30,16 +32,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/composer.lock b/composer.lock index cc93517b..72379f2f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "34fec17afe62b99ed90d0f72d3536a7b", - "content-hash": "58f7e332f9be3faf1f8d481191cf1bb1", + "hash": "1d486fb23e76c7af1daf00bee207919c", + "content-hash": "957b49e5a2e691ad8dbc0e665a87d6c7", "packages": [ { "name": "classpreloader/classpreloader", @@ -704,6 +704,133 @@ ], "time": "2016-07-08 08:19:46" }, + { + "name": "guzzlehttp/psr7", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "64862e854f876bc5aee2996cab2f552db8586065" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/64862e854f876bc5aee2996cab2f552db8586065", + "reference": "64862e854f876bc5aee2996cab2f552db8586065", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2016-08-03 09:33:34" + }, + { + "name": "intervention/image", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Intervention/image.git", + "reference": "4064a980324f6c3bfa2bd981dfb247afa705ec3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/image/zipball/4064a980324f6c3bfa2bd981dfb247afa705ec3c", + "reference": "4064a980324f6c3bfa2bd981dfb247afa705ec3c", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "guzzlehttp/psr7": "~1.1", + "php": ">=5.4.0" + }, + "require-dev": { + "mockery/mockery": "~0.9.2", + "phpunit/phpunit": "3.*" + }, + "suggest": { + "ext-gd": "to use GD library based image processing.", + "ext-imagick": "to use Imagick based image processing.", + "intervention/imagecache": "Caching extension for the Intervention Image library" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "Intervention\\Image\\": "src/Intervention/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@olivervogel.net", + "homepage": "http://olivervogel.net/" + } + ], + "description": "Image handling and manipulation library with support for Laravel integration", + "homepage": "http://image.intervention.io/", + "keywords": [ + "gd", + "image", + "imagick", + "laravel", + "thumbnail", + "watermark" + ], + "time": "2016-09-01 17:04:03" + }, { "name": "jakub-onderka/php-console-color", "version": "0.1", @@ -1632,6 +1759,56 @@ "description": "Support Component of Pingpong Sky Package", "time": "2016-01-07 08:23:04" }, + { + "name": "psr/http-message", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06 14:39:51" + }, { "name": "psr/log", "version": "dev-master", @@ -2678,6 +2855,67 @@ "sitemaps" ], "time": "2016-08-18 23:27:53" + }, + { + "name": "webwizo/laravel-shortcodes", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/webwizo/laravel-shortcodes.git", + "reference": "da0774fcba968b11591693cc55b28fabe7eaf047" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webwizo/laravel-shortcodes/zipball/da0774fcba968b11591693cc55b28fabe7eaf047", + "reference": "da0774fcba968b11591693cc55b28fabe7eaf047", + "shasum": "" + }, + "require": { + "illuminate/contracts": "~5.2", + "illuminate/support": "~5.2", + "illuminate/view": "~5.2", + "php": "~5.5|~7.0" + }, + "require-dev": { + "orchestra/testbench": "~3.2", + "phpunit/phpunit": "4.*", + "scrutinizer/ocular": "~1.1", + "squizlabs/php_codesniffer": "~2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "tests/TestCase.php" + ], + "psr-4": { + "Webwizo\\Shortcodes\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Asif Iqbal", + "email": "webwizo@gmail.com", + "homepage": "http://www.webwizo.com", + "role": "Developer" + } + ], + "description": "Wordpress like shortcodes for Laravel 5.2", + "homepage": "https://github.com/webwizo/laravel-shortcodes", + "keywords": [ + "laravel", + "shortcodes", + "wordpress" + ], + "time": "2016-08-25 06:02:46" } ], "packages-dev": [ diff --git a/config/.gitignore b/config/.gitignore index 2475f7d1..a6acbe5b 100644 --- a/config/.gitignore +++ b/config/.gitignore @@ -17,4 +17,5 @@ !services.php !session.php !sitemap.php +!image.php !view.php diff --git a/config/app.php b/config/app.php index dd7ce301..c8306b9f 100644 --- a/config/app.php +++ b/config/app.php @@ -129,49 +129,51 @@ 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, + Intervention\Image\ImageServiceProvider::class, + Webwizo\Shortcodes\ShortcodesServiceProvider::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 +187,42 @@ 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, + 'Shortcode' => Webwizo\Shortcodes\Facades\Shortcode::class, -], + ], ]; diff --git a/config/image.php b/config/image.php new file mode 100644 index 00000000..b106809e --- /dev/null +++ b/config/image.php @@ -0,0 +1,20 @@ + 'gd' + +); 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_19_225527_AddNumberOfViewsOfPage.php b/database/migrations/2016_09_19_225527_AddNumberOfViewsOfPage.php new file mode 100644 index 00000000..d4061908 --- /dev/null +++ b/database/migrations/2016_09_19_225527_AddNumberOfViewsOfPage.php @@ -0,0 +1,34 @@ +integer('viewed') + ->comment('How many times has been this content page displayed?') + ->after('updated_at'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('contents', function (Blueprint $table) { + $table->dropColumn('viewed'); + }); + } +} 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/migrations/2016_09_22_191204_AddSoftDeletesToContent.php b/database/migrations/2016_09_22_191204_AddSoftDeletesToContent.php new file mode 100644 index 00000000..5046f05d --- /dev/null +++ b/database/migrations/2016_09_22_191204_AddSoftDeletesToContent.php @@ -0,0 +1,53 @@ +getDoctrineSchemaManager() + ->getDatabasePlatform(); + $platform->registerDoctrineTypeMapping('enum', 'string'); + } + + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + + Schema::table('contents', function (Blueprint $table) { + $table->softDeletes() + ->after('updated_at'); + }); + + DB::statement(" + ALTER TABLE `contents` + CHANGE `status` `status` enum('draft','protected','public') COLLATE 'utf8_unicode_ci' NOT NULL DEFAULT 'draft' AFTER `module_settings` + "); + + DB::statement('UPDATE contents SET status="protected", deleted_at=NOW() WHERE status=""'); + DB::statement('DELETE FROM content_history WHERE new_value="deleted" OR old_value="deleted"'); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('contents', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + + DB::statement("ALTER TABLE `contents` +CHANGE `status` `status` enum('draft','protected','public', 'deleted') COLLATE 'utf8_unicode_ci' NULL DEFAULT 'draft' AFTER `module_settings`;"); + } +} diff --git a/database/migrations/2016_09_22_195825_AddSoftDeletesToUsers.php b/database/migrations/2016_09_22_195825_AddSoftDeletesToUsers.php new file mode 100644 index 00000000..fd1b05e2 --- /dev/null +++ b/database/migrations/2016_09_22_195825_AddSoftDeletesToUsers.php @@ -0,0 +1,61 @@ +getDoctrineSchemaManager() + ->getDatabasePlatform(); + $platform->registerDoctrineTypeMapping('enum', 'string'); + } + + /** + * Run the migrations. + * + * @return void + */ + public function up() + { + Schema::table('users', function (Blueprint $table) { + $table->timestamp('created_at') + ->nullable() + ->after('id'); + $table->timestamp('updated_at') + ->nullable() + ->after('created_at'); + $table->timestamp('deleted_at') + ->nullable() + ->after('updated_at'); + }); + + DB::statement(" + ALTER TABLE `users` + CHANGE `status` `status` enum('blocked','active') COLLATE 'utf8_unicode_ci' NOT NULL DEFAULT 'active' + "); + + DB::statement('UPDATE users SET created_at=NOW(), updated_at=NOW()'); + DB::statement('UPDATE users SET status="blocked", deleted_at=NOW() WHERE status=""'); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropTimestamps(); + $table->dropSoftDeletes(); + }); + + DB::statement(" + ALTER TABLE `users` + CHANGE `status` `status` enum('blocked','active', 'deleted') COLLATE 'utf8_unicode_ci' NOT NULL DEFAULT 'active' + "); + } +} diff --git a/database/migrations/2016_09_24_175541_FixForeignKeysOnActionLog.php b/database/migrations/2016_09_24_175541_FixForeignKeysOnActionLog.php new file mode 100644 index 00000000..c24b2248 --- /dev/null +++ b/database/migrations/2016_09_24_175541_FixForeignKeysOnActionLog.php @@ -0,0 +1,44 @@ +dropForeign('fk_action_log_action_log_type1'); + $table->dropForeign('fk_action_log_user1'); + + + $table->foreign('type_id') + ->references('id') + ->on('action_log_types') + ->onDelete('cascade') + ->onUpdate('cascade'); + + + $table->foreign('user_id') + ->references('id') + ->on('users') + ->onDelete('cascade') + ->onUpdate('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // do nothing + } +} diff --git a/database/migrations/2016_09_24_181733_RenameForeignKeyInWidgets.php b/database/migrations/2016_09_24_181733_RenameForeignKeyInWidgets.php new file mode 100644 index 00000000..79049688 --- /dev/null +++ b/database/migrations/2016_09_24_181733_RenameForeignKeyInWidgets.php @@ -0,0 +1,36 @@ +dropForeign('fk_widget_user1'); + + $table->foreign('user_id') + ->references('id') + ->on('users') + ->onDelete('no action') + ->onUpdate('no action'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // do nothing + } +} diff --git a/database/migrations/2016_09_24_220747_MakeUserIdNullableAtWidgets.php b/database/migrations/2016_09_24_220747_MakeUserIdNullableAtWidgets.php new file mode 100644 index 00000000..e7b47208 --- /dev/null +++ b/database/migrations/2016_09_24_220747_MakeUserIdNullableAtWidgets.php @@ -0,0 +1,55 @@ +dropForeign(['user_id']); + + $table->integer('user_id') + ->nullable(true) + ->default(null) + ->comment('User ID who created this widget. NULL for widget created by template.')->change(); + + $table->foreign('user_id') + ->references('id') + ->on('users') + ->onDelete('cascade') + ->onUpdate('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('widgets', function (Blueprint $table) { + $table->dropForeign(['user_id']); + }); + + Schema::table('widgets', function (Blueprint $table) { + $table->integer('user_id') + ->nullable(false) + ->comment('User ID who created this widget. ')->change(); + + $table->foreign('user_id') + ->references('id') + ->on('users') + ->onDelete('no action') + ->onUpdate('no action'); + }); + } +} diff --git a/database/migrations/2016_09_26_132627_RemoveOnUpdateCurrentTimestampFromContentsUpdatedAt.php b/database/migrations/2016_09_26_132627_RemoveOnUpdateCurrentTimestampFromContentsUpdatedAt.php new file mode 100644 index 00000000..a8538c80 --- /dev/null +++ b/database/migrations/2016_09_26_132627_RemoveOnUpdateCurrentTimestampFromContentsUpdatedAt.php @@ -0,0 +1,29 @@ +hasMany(Comment::class, 'content_id') + ->where('approved', true); + } +} \ No newline at end of file diff --git a/modules/Blog/Article/Entities/Comment.php b/modules/Blog/Article/Entities/Comment.php new file mode 100644 index 00000000..6fc5d45f --- /dev/null +++ b/modules/Blog/Article/Entities/Comment.php @@ -0,0 +1,27 @@ +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/Events/ApprovedComment.php b/modules/Blog/Article/Events/ApprovedComment.php new file mode 100644 index 00000000..3fab72bc --- /dev/null +++ b/modules/Blog/Article/Events/ApprovedComment.php @@ -0,0 +1,20 @@ +comment = $comment; + } +} \ No newline at end of file diff --git a/modules/Blog/Article/Events/CreatedComment.php b/modules/Blog/Article/Events/CreatedComment.php new file mode 100644 index 00000000..d21f685b --- /dev/null +++ b/modules/Blog/Article/Events/CreatedComment.php @@ -0,0 +1,20 @@ +comment = $comment; + } +} \ No newline at end of file diff --git a/modules/Blog/Article/Events/DeletedComment.php b/modules/Blog/Article/Events/DeletedComment.php new file mode 100644 index 00000000..048341ab --- /dev/null +++ b/modules/Blog/Article/Events/DeletedComment.php @@ -0,0 +1,20 @@ +comment = $comment; + } +} \ No newline at end of file diff --git a/modules/Blog/Article/Handlers/Article.php b/modules/Blog/Article/Handlers/Article.php index 90d0f39d..bc5481b9 100644 --- a/modules/Blog/Article/Handlers/Article.php +++ b/modules/Blog/Article/Handlers/Article.php @@ -2,14 +2,17 @@ namespace Modules\Blog\Article\Handlers; use App\Enumerations\ModulesHandlerToId; +use App\Handlers\Contracts\ContentTitles; use App\Handlers\Handler; -use App\Http\Controllers\Admin\EditContent; +use App\Http\Controllers\Admin\Contents\EditContent; use App\Http\Controllers\Controller; use App\Http\Requests\Request; +use App\Models\ActionLogType; use App\Models\Content; use App\Models\User; +use Modules\Blog\Article\Entities\Comment; -class Article extends Handler +class Article extends Handler implements ContentTitles { /** * Render given module into visitor area. @@ -21,7 +24,15 @@ class Article extends Handler */ public function render(Content $content, $parameters) { - return Controller::VisitorsView($content->template, ['page' => $content]); + $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, 'extra' => $parameters]); } public static function getExtendedFormInputsCreate() @@ -80,6 +91,7 @@ class Article extends Handler } /** + * Register admin menu * * @param User $user */ @@ -89,10 +101,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() @@ -106,10 +128,63 @@ class Article extends Handler /** * Install module - * - * @return mixed */ public static function install() { + $types = [ + [ + 'string_id' => 'commentCreated', + 'name' => 'article::comments.events.names.commentCreated', + 'value' => 'article::comments.events.descriptions.commentCreated', + ], + [ + 'string_id' => 'commentDeleted', + 'name' => 'article::comments.events.names.commentDeleted', + 'value' => 'article::comments.events.descriptions.commentDeleted', + ], + [ + 'string_id' => 'commentApproved', + 'name' => 'article::comments.events.names.commentApproved', + 'value' => 'article::comments.events.descriptions.commentApproved', + ], + + ]; + foreach ($types as $type) { + ActionLogType::create($type); + } + } + + /** + * Get translated title for pages overview of given type. + * + * This value is used for rendering list of all content pages of given type in administration. + * + * @return string + */ + public static function getOverviewTitle() + { + return trans('article::module.allArticles'); + } + + /** + * Title displayed above for for creating new content page + * + * @return string + */ + public static function getCreateNewTitle() + { + return trans('article::module.newArticle'); + } + + /** + * Title for form displayed for editin content page + * + * @param Content $content + * + * @return mixed + */ + public static function getEditTitle(Content $content) + { + return trans('article::module.editArticle'); } } \ No newline at end of file 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..9293d2b1 --- /dev/null +++ b/modules/Blog/Article/Http/Controllers/Admin/CommentsOverview.php @@ -0,0 +1,19 @@ +with('author') + ->orderBy('approved') + ->orderBy('created_at', 'DESC') + ->paginate(25); + + 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..969f845d --- /dev/null +++ b/modules/Blog/Article/Http/Controllers/Admin/EditComment.php @@ -0,0 +1,37 @@ +approved = 1; + $comment->save(); + + Event::fire(new ApprovedComment($comment)); + + return redirect() + ->back() + ->with('success', trans('article::comments.approved')); + } + + public function delete($id) + { + $comment = Comment::findOrFail($id); + + Event::fire(new DeletedComment($comment)); + + $comment->delete(); + + 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..ce295327 --- /dev/null +++ b/modules/Blog/Article/Http/Controllers/Visitor/CreateNewComment.php @@ -0,0 +1,54 @@ +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 = Comment::create([ + 'user_id' => $user ? $user->id : null, + 'content_id' => $article->id, + 'name' => $name, + 'email' => $email, + 'content' => $content, + 'approved' => !is_null($user) + ]); + + // fire event about new comment + Event::fire(new CreatedComment($comment)); + + 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..ef3ef17c 100644 --- a/modules/Blog/Article/Http/routes.php +++ b/modules/Blog/Article/Http/routes.php @@ -1,25 +1,29 @@ 'web', - 'prefix' => 'article', + 'prefix' => LaravelLocalization::setLocale(), + 'middleware' => ['localeSessionRedirect', 'localizationRedirect'], 'namespace' => 'Modules\Blog\Article\Http\Controllers' ], function () { - Route::get('/', 'CategoriesOverview@getOverview') - ->name('article.overview'); + Route::group([ + 'middleware' => ['web', 'auth', 'adminMenu'], + 'prefix' => 'admin/comments', + 'namespace' => 'Admin' + ], function () { - 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'); + // public routes + Route::group(['middleware' => ['web'], 'namespace' => 'Visitor'], function () { - Route::get('/delete/{id}', 'EditCategory@delete') - ->name('article.delete'); + Route::post('comments/new/{id}', 'CreateNewComment@postNew') + ->name('comments.new'); + }); }); \ No newline at end of file diff --git a/modules/Blog/Article/Listeners/LogCommentApproval.php b/modules/Blog/Article/Listeners/LogCommentApproval.php new file mode 100644 index 00000000..24a9e049 --- /dev/null +++ b/modules/Blog/Article/Listeners/LogCommentApproval.php @@ -0,0 +1,35 @@ + $event->comment->name, + 'email' => $event->comment->email, + 'content' => $event->comment->content + ]; + + $this->saveLog($parameters); + } + + /** + * Getter for action name. Used only internally + * + * @return mixed + */ + protected function getName() + { + return 'commentApproved'; + } +} \ No newline at end of file diff --git a/modules/Blog/Article/Listeners/LogDeletedComment.php b/modules/Blog/Article/Listeners/LogDeletedComment.php new file mode 100644 index 00000000..d4cb97ea --- /dev/null +++ b/modules/Blog/Article/Listeners/LogDeletedComment.php @@ -0,0 +1,36 @@ + $event->comment->name, + 'email' => $event->comment->email, + 'content' => $event->comment->content + ]; + + $this->saveLog($parameters); + } + + /** + * Getter for action name. Used only internally + * + * @return mixed + */ + protected function getName() + { + return 'commentDeleted'; + } +} \ No newline at end of file diff --git a/modules/Blog/Article/Listeners/LogNewComment.php b/modules/Blog/Article/Listeners/LogNewComment.php new file mode 100644 index 00000000..672a5748 --- /dev/null +++ b/modules/Blog/Article/Listeners/LogNewComment.php @@ -0,0 +1,39 @@ +comment->user_id) + return; + + $parameters = [ + 'author' => $event->comment->name, + 'email' => $event->comment->email, + 'content' => $event->comment->content + ]; + + $this->saveLog($parameters); + } + + /** + * Getter for action name. Used only internally + * + * @return mixed + */ + protected function getName() + { + return 'commentCreated'; + } +} \ No newline at end of file diff --git a/modules/Blog/Article/Providers/ArticleEventServiceProvider.php b/modules/Blog/Article/Providers/ArticleEventServiceProvider.php new file mode 100644 index 00000000..13059628 --- /dev/null +++ b/modules/Blog/Article/Providers/ArticleEventServiceProvider.php @@ -0,0 +1,31 @@ + [LogCommentApproval::class], + DeletedComment::class => [LogDeletedComment::class], + CreatedComment::class => [LogNewComment::class], + ]; + + public function boot(DispatcherContract $events) + { + parent::boot($events); // TODO: Change the autogenerated stub + } +} \ No newline at end of file diff --git a/modules/Blog/Article/Providers/ArticleServiceProvider.php b/modules/Blog/Article/Providers/ArticleServiceProvider.php index 0d52f03c..188e2fef 100644 --- a/modules/Blog/Article/Providers/ArticleServiceProvider.php +++ b/modules/Blog/Article/Providers/ArticleServiceProvider.php @@ -25,6 +25,8 @@ class ArticleServiceProvider extends ServiceProvider { $this->registerTranslations(); $this->registerConfig(); $this->registerViews(); + + $this->app->register(ArticleEventServiceProvider::class); } /** @@ -57,27 +59,11 @@ class ArticleServiceProvider extends ServiceProvider { $viewPath = base_path('resources/views/modules/article'); $sourcePath = __DIR__.'/../Resources/views'; - $this->publishes([$sourcePath => $viewPath]); + $this->publishes([$sourcePath => $viewPath], 'module-views'); $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..35468658 --- /dev/null +++ b/modules/Blog/Article/Resources/lang/cs/comments.php @@ -0,0 +1,18 @@ + "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", +"events" => ["descriptions" => ["commentApproved" => "Schválen komentář od autora :authro (:email) s následujícím obsahem: :content", +"commentCreated" => "Přidán komentář od autora :author (e-mail :e-mail) s následujícím obsahem: :content", +"commentDeleted" => "Smazán komentář od autora :author (:email) s následujícím obsahem: :content", +], +"names" => ["commentApproved" => "Komentář schválen", +"commentCreated" => "Komentář úspěšně přidán.", +"commentDeleted" => "Smazán komentář", +], +], +"noComments" => "Nejsou dostupné žádné komentáře", +]; 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..f000fdb7 --- /dev/null +++ b/modules/Blog/Article/Resources/lang/cs/module.php @@ -0,0 +1,11 @@ + "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.", +"editArticle" => "Upravuji článek", +"name" => "Články", +"newArticle" => "Napsat nový článek", +]; 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..9d8818cd --- /dev/null +++ b/modules/Blog/Article/Resources/lang/en/comments.php @@ -0,0 +1,21 @@ + 'Created at :date', + 'commentedArticle' => 'Article', + 'approve' => 'Approve comment', + 'approved' => 'Comment successfully approved', + 'deleted' => 'Comment deleted', + 'noComments' => 'No comments available', + 'events' => [ + 'names' => [ + 'commentCreated' => 'Added comment', + 'commentApproved' => 'Approved comment', + 'commentDeleted' => 'Deleted comment', + ], + 'descriptions' => [ + 'commentCreated' => 'Added comment from author :author with e-mail :email and following content: :content', + 'commentApproved' => 'Approved comment from author :author with e-mail :email and following content: :content', + 'commentDeleted' => 'Deleted comment from author :author with e-mail :email and following content: :content', + ], + ] +]; \ 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..8b9f7e18 100644 --- a/modules/Blog/Article/Resources/lang/en/module.php +++ b/modules/Blog/Article/Resources/lang/en/module.php @@ -1,5 +1,11 @@ 'Article', - 'description' => 'Base submodule for blog' + 'name' => 'Articles', + 'description' => 'Base submodule for blog', + 'comments' => 'Comments', + 'commentsOverview' => 'All comments', + 'blog' => 'Blog', + 'allArticles' => 'All articles', + 'newArticle' => 'Write new article', + 'editArticle' => 'Editing article' ]; \ 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..61a8c751 --- /dev/null +++ b/modules/Blog/Article/Resources/views/admin/comments/overview.blade.php @@ -0,0 +1,47 @@ +@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}} +

+
+ +
+ @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 @@
-
-