Compare commits
52
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8098288139 | ||
|
|
cf90d1b91e | ||
|
|
8acfcabb3c | ||
|
|
b91b80debb | ||
|
|
ae29c49acf | ||
|
|
7e4507e9e4 | ||
|
|
f0bb159efb | ||
|
|
ddae98d233 | ||
|
|
b1ca6e4cfb | ||
|
|
c69bd9bcf7 | ||
|
|
271e7f5170 | ||
|
|
1ecef555ef | ||
|
|
4ef5e3e3cd | ||
|
|
1388383bb6 | ||
|
|
f1ff45cfb1 | ||
|
|
6d723f026d | ||
|
|
b99f70c269 | ||
|
|
680edb14c1 | ||
|
|
ced920505a | ||
|
|
a1c4a19a59 | ||
|
|
62f73c51e6 | ||
|
|
ba55702585 | ||
|
|
4d68156a73 | ||
|
|
f10847dede | ||
|
|
73681a5a4d | ||
|
|
a2d83ce3fa | ||
|
|
a0c31c7a5f | ||
|
|
422339b313 | ||
|
|
3fdae2669b | ||
|
|
d90ca08ba6 | ||
|
|
e8cbc1b537 | ||
|
|
983efad9f0 | ||
|
|
2d66cf672a | ||
|
|
a5b6f6fa0d | ||
|
|
c92ba41def | ||
|
|
6468c8f49c | ||
|
|
8bae8a43f9 | ||
|
|
0e535c3ed1 | ||
|
|
ce183d4805 | ||
|
|
8946ef7441 | ||
|
|
295e8927a9 | ||
|
|
029d84fed1 | ||
|
|
862f49771f | ||
|
|
5abd7d9151 | ||
|
|
7d6a3737e3 | ||
|
|
aa55ffe46a | ||
|
|
848fa7b7cb | ||
|
|
05c9e56973 | ||
|
|
52e02d67c6 | ||
|
|
9c90e844b0 | ||
|
|
d99c046103 | ||
|
|
c027ffe2df |
+3
-4
@@ -1,5 +1,7 @@
|
||||
# published files from modules
|
||||
/modules
|
||||
/templates
|
||||
|
||||
# published files from modules
|
||||
/public/modules
|
||||
/public/assets
|
||||
/resources/views/modules
|
||||
@@ -10,9 +12,6 @@
|
||||
**/views/**/custom/*
|
||||
!**/views/**/custom/.gitkeep
|
||||
|
||||
# module config - generated in ModuleManager service provider
|
||||
**/module.json
|
||||
|
||||
# uploaded files
|
||||
/public/uploads/*
|
||||
!/public/uploads/.gitkeep
|
||||
|
||||
@@ -10,7 +10,7 @@ interface Module extends BaseRepository
|
||||
|
||||
public function getUpdateContentTitle(ModuleModel $module, ContentModel $content = null);
|
||||
|
||||
public function getOverviewTitle(ModuleModel $module);
|
||||
public function getOverviewTitle(ModuleModel $module = null);
|
||||
|
||||
public function getByHandler($handler);
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\CMS\Handlers;
|
||||
|
||||
use App\CMS\Contracts\Modules\BaseModuleHandler;
|
||||
use App\CMS\Enumerations\TemplatePathsHumanReadableRootOnly;
|
||||
use App\CMS\Enumerations\WidgetTypes;
|
||||
use App\CMS\Repositories\Module;
|
||||
use App\Models\ActionLog\ActionLog;
|
||||
@@ -14,6 +13,7 @@ use App\Models\Templates\Template;
|
||||
use App\Models\User;
|
||||
use App\Models\Widgets\Widget;
|
||||
use App\Models\Widgets\WidgetArea;
|
||||
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
|
||||
|
||||
abstract class BaseHandler implements BaseModuleHandler
|
||||
{
|
||||
@@ -44,6 +44,7 @@ abstract class BaseHandler implements BaseModuleHandler
|
||||
|
||||
if ($user->can('manage', Content::class) && !$contentModules->isEmpty()) {
|
||||
$menu->dropdown(trans('admin/mainMenu.contentPages'), function ($sub) {
|
||||
$sub->route('content.order', trans('admin/mainMenu.contentOrder'), [], 999, ['icon' => 'fa fa-fw fa-reorder']);
|
||||
$sub->route('content.overview', trans('admin/mainMenu.contentPageOverview'), [], 10000, ['icon' => 'fa fa-fw fa-list']);
|
||||
}, 20, ['icon' => 'fa fa-fw fa-database']);
|
||||
}
|
||||
@@ -110,15 +111,7 @@ abstract class BaseHandler implements BaseModuleHandler
|
||||
* Templates
|
||||
*/
|
||||
if ($user->can('manage', Template::class)) {
|
||||
$templatePaths = TemplatePathsHumanReadableRootOnly::getWithTranslations();
|
||||
if (!empty($templatePaths)) {
|
||||
$sub->dropdown(trans('admin/mainMenu.templates'), function ($sub) use ($templatePaths) {
|
||||
$i = 1;
|
||||
foreach ($templatePaths as $id => $name) {
|
||||
$sub->route('templates.overview', $name, ['id' => $id], $i++);
|
||||
}
|
||||
}, 60, ['icon' => 'fa fa-fw fa-file-code-o']);
|
||||
}
|
||||
$sub->route('templates.index', trans('admin/mainMenu.templates'), [], 70, ['icon' => 'fa fa-fw fa-file-code-o']);
|
||||
}
|
||||
|
||||
if ($user->can('manage', ActionLog::class)) {
|
||||
@@ -132,6 +125,26 @@ abstract class BaseHandler implements BaseModuleHandler
|
||||
}, 10000, ['icon' => 'fa fa-fw fa-gears']);
|
||||
});
|
||||
|
||||
\Menu::create('topMenu', function ($menu) use ($user) {
|
||||
if (count(LaravelLocalization::getSupportedLocales()) > 1) {
|
||||
$menu->dropdown(trans(LaravelLocalization::getCurrentLocaleName()), function ($menu) use ($user) {
|
||||
foreach (LaravelLocalization::getSupportedLocales() as $localeCode => $properties) {
|
||||
$menu->url(LaravelLocalization::getLocalizedURL($localeCode), $properties['native']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ($user->can('manage', Content::class)) {
|
||||
$menu->route('page.index', '', [], 10, [
|
||||
'icon' => 'fa fa-fw fa-globe',
|
||||
'title' => trans('common.goToWeb'),
|
||||
'target' => '_blank',
|
||||
'data-toggle' => 'tooltip',
|
||||
'data-placement' => 'left'
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
// create menu with user profile
|
||||
\Menu::create('profileMenu', function ($menu) use ($user) {
|
||||
$menu->route('users.detail', trans('admin/userManagement.profile'), ['user' => $user], 10, ['icon' => 'fa fa-fw fa-user icon']);
|
||||
|
||||
@@ -17,8 +17,7 @@ class History implements HistoryHandler
|
||||
switch ($changeset->column) {
|
||||
case 'pageContent':
|
||||
$route = route('history.diff', [
|
||||
'revision' => $changeset->edit_batch,
|
||||
'id' => $changeset->id
|
||||
'revision' => $changeset->id
|
||||
]);
|
||||
|
||||
return "(<a href='" . $route . "'>" . trans('admin/history.diff') . "</a>)";
|
||||
@@ -48,8 +47,8 @@ class History implements HistoryHandler
|
||||
break;
|
||||
default:
|
||||
return trans('admin/history.changedFromTo', [
|
||||
'from' => is_null($changeset->old_value) ? trans('admin/history.emptyValue') : $changeset->old_value,
|
||||
'to' => is_null($changeset->new_value) ? trans('admin/history.emptyValue') : $changeset->new_value,
|
||||
'from' => empty($changeset->old_value) ? trans('admin/history.emptyValue') : $changeset->old_value,
|
||||
'to' => empty($changeset->new_value) ? trans('admin/history.emptyValue') : $changeset->new_value,
|
||||
]);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ class SettingExtension
|
||||
* @var string view used for rendering given settings part
|
||||
*/
|
||||
protected $view;
|
||||
protected $order;
|
||||
|
||||
/**
|
||||
* SettingExtension constructor.
|
||||
@@ -23,11 +24,12 @@ class SettingExtension
|
||||
* @param string $name
|
||||
* @param string $view
|
||||
*/
|
||||
public function __construct($id, $name, $view)
|
||||
public function __construct($id, $name, $view, $order)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
$this->view = $view;
|
||||
$this->order = $order;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,4 +55,14 @@ class SettingExtension
|
||||
{
|
||||
return $this->view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getOrder()
|
||||
{
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\CMS\Handlers\Registrators;
|
||||
|
||||
use App\CMS\Enumerations\Exceptions\EnumerationExistsException;
|
||||
use App\CMS\Exceptions\Enumerations\EnumerationExistsException;
|
||||
use App\CMS\Handlers\Registrators\Items\SettingExtension;
|
||||
|
||||
class SettingsExtensionRegistrator
|
||||
@@ -22,12 +23,16 @@ class SettingsExtensionRegistrator
|
||||
*
|
||||
* @throws EnumerationExistsException
|
||||
*/
|
||||
public function register($sectionId, $sectionName, $sectionView)
|
||||
public function register($sectionId, $sectionName, $sectionView, $order = null)
|
||||
{
|
||||
$this->checkIfRegistered($sectionId);
|
||||
|
||||
if (is_null($order)) {
|
||||
$order = count($this->settingsExtensions);
|
||||
}
|
||||
|
||||
// register new extension
|
||||
$this->settingsExtensions[$sectionId] = new SettingExtension($sectionId, $sectionName, $sectionView);
|
||||
$this->settingsExtensions[$sectionId] = new SettingExtension($sectionId, $sectionName, $sectionView, $order);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,14 +49,15 @@ class SettingsExtensionRegistrator
|
||||
|
||||
/**
|
||||
* Getter for all registered extensions
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
// sort registered extensions by key
|
||||
ksort($this->settingsExtensions);
|
||||
|
||||
// sort registered extensions
|
||||
uasort($this->settingsExtensions, function (SettingExtension $a, SettingExtension $b) {
|
||||
return $a->getOrder() - $b->getOrder();
|
||||
});
|
||||
|
||||
return $this->settingsExtensions;
|
||||
}
|
||||
|
||||
|
||||
@@ -207,6 +207,7 @@ class TemplateRegistrator
|
||||
protected function registerTemplates($path, $templatePathId)
|
||||
{
|
||||
$templates = File::files($path);
|
||||
$registeredTemplates = Templates::getWithTranslations();
|
||||
|
||||
foreach ($templates as $template) {
|
||||
|
||||
@@ -217,7 +218,6 @@ class TemplateRegistrator
|
||||
|
||||
// get all templates and template name (path/to/template.blade.php -> path.to.template)
|
||||
$templateName = $this->getTemplateName($template);
|
||||
$registeredTemplates = Templates::getWithTranslations();
|
||||
|
||||
if (!in_array($templateName, $registeredTemplates)) {
|
||||
$params = [
|
||||
@@ -316,4 +316,14 @@ class TemplateRegistrator
|
||||
|
||||
return $this->namespace . trim($templateName, '.');
|
||||
}
|
||||
|
||||
public function deleteNonExistingPaths()
|
||||
{
|
||||
$paths = TemplatePath::all();
|
||||
|
||||
foreach ($paths as $path) {
|
||||
if (!is_dir(base_path($path->path)))
|
||||
\File::deleteDirectory($path->path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
namespace App\CMS\Helpers;
|
||||
|
||||
use App\CMS\Handlers\Contracts\AppHandler;
|
||||
use App\Models\Content\Content;
|
||||
use Webwizo\Shortcodes\Facades\Shortcode;
|
||||
|
||||
/**
|
||||
* Class PageRenderer
|
||||
*
|
||||
* Simple class for rendering page for visitor
|
||||
*
|
||||
* @package App\CMS\Helpers
|
||||
*/
|
||||
class PageRenderer
|
||||
{
|
||||
/**
|
||||
* Render page
|
||||
*
|
||||
* @param Content $page
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function render(Content $page)
|
||||
{
|
||||
// do something template-specific
|
||||
$parameters = call_user_func_array([CurrentTemplate::handler(), 'beforeRender'], [&$page]);
|
||||
|
||||
/**
|
||||
* @var $handler AppHandler
|
||||
*/
|
||||
$handler = new $page->moduleHandler;
|
||||
|
||||
// render page
|
||||
return $handler->render($page, $parameters);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ trait Nullable {
|
||||
* @return null|string
|
||||
*/
|
||||
public function nullableDate($date) {
|
||||
return empty($date) ? null : Carbon::parse($date)->toDateTimeString();
|
||||
return empty($date) ? null : Carbon::createFromFormat(config('app.date_format'), $date)->toDateTimeString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\CMS\MenuPresenters;
|
||||
|
||||
use Nwidart\Menus\MenuItem;
|
||||
use Nwidart\Menus\Presenters\Presenter;
|
||||
|
||||
class FoundationDrilldownMenuPresenter extends Presenter
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc }
|
||||
*/
|
||||
public function getOpenTagWrapper()
|
||||
{
|
||||
return PHP_EOL . '<ul class="vertical menu" data-drilldown>' . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc }
|
||||
*/
|
||||
public function getCloseTagWrapper()
|
||||
{
|
||||
return PHP_EOL . '</ul>' . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc }
|
||||
*/
|
||||
public function getMenuWithoutDropdownWrapper($item)
|
||||
{
|
||||
return '<li' . $this->getActiveState($item) . '><a href="' . $item->getUrl() . '">' . $item->getIcon() . ' ' . $item->title . '</a></li>' . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc }
|
||||
*/
|
||||
public function getActiveState(MenuItem $item)
|
||||
{
|
||||
$active = ' class="active"';
|
||||
|
||||
if (empty($item->getChilds())) {
|
||||
// for direct menu input return if page is active
|
||||
return \Request::is($item->getRequest()) ? $active : null;
|
||||
} else {
|
||||
// on nested menu inputs loop over children and in case any of them is active, mark active also root level
|
||||
foreach ($item->getChilds() as $child) {
|
||||
if ($this->getActiveState($child)) {
|
||||
return $active;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc }
|
||||
*/
|
||||
public function getDividerWrapper()
|
||||
{
|
||||
return '<li class="divider"></li>' . PHP_EOL;
|
||||
}
|
||||
|
||||
public function getMultiLevelDropdownWrapper($item)
|
||||
{
|
||||
return $this->getMenuWithDropDownWrapper($item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc }
|
||||
*/
|
||||
public function getMenuWithDropDownWrapper($item)
|
||||
{
|
||||
$active = $this->getActiveState($item);
|
||||
|
||||
return '<li>
|
||||
<a ' . $active . ' href="#">' . $item->getIcon() . ' ' . $item->title . '</a>
|
||||
<ul class="vertical menu">
|
||||
' . $this->getChildMenuItems($item) . '
|
||||
</ul>
|
||||
</li>' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\CMS\MenuPresenters;
|
||||
|
||||
use Nwidart\Menus\Presenters\Presenter;
|
||||
|
||||
class FoundationDropdownMenuPresenter extends Presenter
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc }
|
||||
*/
|
||||
public function getOpenTagWrapper()
|
||||
{
|
||||
return PHP_EOL . '<ul class="vertical dropdown menu" data-dropdown-menu>' . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc }
|
||||
*/
|
||||
public function getCloseTagWrapper()
|
||||
{
|
||||
return PHP_EOL . '</ul>' . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc }
|
||||
*/
|
||||
public function getMenuWithoutDropdownWrapper($item)
|
||||
{
|
||||
$link = '<a href="' . $item->getUrl() . '">' . $item->getIcon() . ' ' . $item->title . '</a>';
|
||||
return '<li' . $this->getActiveState($item) . '>' . $link . '</li>' . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc }
|
||||
*/
|
||||
public function getActiveState($item)
|
||||
{
|
||||
return \Request::is($item->getRequest()) || $item->hasActiveOnChild ? ' class="active"' : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc }
|
||||
*/
|
||||
public function getDividerWrapper()
|
||||
{
|
||||
return '<li class="divider"></li>' . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc }
|
||||
*/
|
||||
public function getMenuWithDropDownWrapper($item)
|
||||
{
|
||||
return '<li>
|
||||
<a href="#">' . $item->getIcon() . ' ' . $item->title . '</a>
|
||||
<ul class="vertical menu">
|
||||
' . $this->getChildMenuItems($item) . '
|
||||
</ul>
|
||||
</li>' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
namespace App\CMS\MenuPresenters;
|
||||
|
||||
class FoundationInitializedDropdownMenuPresenter extends FoundationDropdownMenuPresenter
|
||||
{
|
||||
public function getOpenTagWrapper()
|
||||
{
|
||||
return PHP_EOL . '<ul class="menu">' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace App\CMS\MenuPresenters;
|
||||
|
||||
use Nwidart\Menus\Presenters\Bootstrap\NavbarPresenter;
|
||||
|
||||
class TopMenuPresenter extends NavbarPresenter {
|
||||
public function getOpenTagWrapper()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getCloseTagWrapper()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
namespace App\CMS\ModuleManager\Providers;
|
||||
|
||||
use App\CMS\Repositories\Module as ModuleRepository;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class ModuleManager extends ServiceProvider
|
||||
@@ -14,13 +16,22 @@ class ModuleManager extends ServiceProvider
|
||||
public function boot()
|
||||
{
|
||||
// this service provider needs published config - config is merged right after this service provider
|
||||
$modules = app(ModuleRepository::class)->getOnlyModules();
|
||||
|
||||
try {
|
||||
$modules = app(ModuleRepository::class)->all();
|
||||
} catch (QueryException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// iterate over all modules and enable or disable them
|
||||
foreach ($modules as $module) {
|
||||
if (isset($module->module_settings['providers']) && !empty($module->module_settings['providers'])) {
|
||||
foreach ($module->module_settings['providers'] as $provider) {
|
||||
|
||||
if (!class_exists($provider)) {
|
||||
Log::warning($provider.' does not exists. Skipping');
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->app->register($provider);
|
||||
}
|
||||
}
|
||||
@@ -28,7 +39,13 @@ class ModuleManager extends ServiceProvider
|
||||
// include files (if any)
|
||||
if (isset($module->module_settings['files']) && !empty($module->module_settings['files'])) {
|
||||
foreach ($module->module_settings['files'] as $file) {
|
||||
include config('modules.paths.modules') . '/' . $module->static_name . '/' . $file;
|
||||
$path = config('modules.paths.modules') . '/' . $module->static_name . '/' . $file;
|
||||
|
||||
if (!file_exists($path)) {
|
||||
$path = base_path($file);
|
||||
}
|
||||
|
||||
@include_once $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ abstract class ServiceProvider extends LaravelServiceProvider
|
||||
protected $defer = false;
|
||||
protected $alias = false;
|
||||
protected $path = false;
|
||||
protected $publishViews = true;
|
||||
|
||||
abstract protected function setModuleAlias();
|
||||
|
||||
@@ -59,7 +60,7 @@ abstract class ServiceProvider extends LaravelServiceProvider
|
||||
return $path . '/modules/' . $this->alias;
|
||||
}, \Config::get('view.paths')), [$sourcePath]), $this->alias);
|
||||
|
||||
if (is_dir($sourcePath)) {
|
||||
if (is_dir($sourcePath) && $this->publishViews) {
|
||||
$this->publishes([$sourcePath => $viewPath], 'module-views');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class $MigrationName$ extends Migration
|
||||
{
|
||||
/**
|
||||
* Run migration
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('$TableName$', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback migration
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('$TableName$');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class $MigrationName$ extends Migration
|
||||
{
|
||||
/**
|
||||
* Run migration
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('$TableName$', function (Blueprint $table) {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback migration
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('$TableName$', function (Blueprint $table) {
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@ return [
|
||||
'name' => '$Module$',
|
||||
'translatedName' => '$module$::$module$.name',
|
||||
'translatedDescription' => '$module$::$module$.description',
|
||||
'alias' => 'categories',
|
||||
'alias' => strtolower('$Module$'),
|
||||
'handler' => \Modules\$Module$\Handlers\$Module$Handler::class,
|
||||
'providers' => [\Modules\$Module$\Providers\$Module$ServiceProvider::class],
|
||||
'files' => ['Http/routes.php'],
|
||||
+2
@@ -4,5 +4,7 @@ namespace Modules\$ModuleNamespace$\Entities;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class $Entity$ extends Model {
|
||||
|
||||
protected $table = 'contents';
|
||||
// ...
|
||||
}
|
||||
+1
-1
@@ -6,7 +6,7 @@ use App\Http\Controllers\Admin\Controller;
|
||||
use Modules\$ModuleNamespace$\Http\Requests\Update$Entity$;
|
||||
use Modules\$ModuleNamespace$\Http\Requests\Create$Entity$;
|
||||
use Modules\$ModuleNamespace$\Repositories\$Module$Repository;
|
||||
use $EntityNamespace$\$Entity$ as $Entity$Entity;
|
||||
use $EntityNamespace$$Entity$ as $Entity$Entity;
|
||||
|
||||
class $Module$ extends Controller
|
||||
{
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Modules\$Module$\Providers;
|
||||
|
||||
use App\CMS\Contracts\Modules\ServiceProvider;
|
||||
use App\CMS\Modules\ServiceProviders\ServiceProvider;
|
||||
|
||||
/**
|
||||
* Class $Module$ServiceProvider
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace Modules\$ModuleNamespace$\Repositories;
|
||||
|
||||
use $EntityNamespace$\$Entity$ as $Entity$Entity;
|
||||
use $EntityNamespace$$Entity$ as $Entity$Entity;
|
||||
|
||||
class $Module$Repository {
|
||||
/**
|
||||
@@ -7,7 +7,6 @@ use App\CMS\Enumerations\ContentStatuses;
|
||||
use App\CMS\Repositories\Repository;
|
||||
use App\Models\Content\Content as ContentModel;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
|
||||
|
||||
class Content extends Repository implements IContent
|
||||
@@ -106,15 +105,15 @@ class Content extends Repository implements IContent
|
||||
*/
|
||||
public function getOverview($handler = null, $statuses = null)
|
||||
{
|
||||
$query = ContentModel::select('contents.*', 'modules.name AS module_name', 'modules.id AS module_id')
|
||||
$query = ContentModel::select('contents.*')
|
||||
->join('modules', 'modules.handler', '=', 'contents.moduleHandler')
|
||||
->join('users', 'users.id', '=', 'contents.userId')
|
||||
->addSelect(DB::raw('COALESCE(users.name, users.email) AS author_name'))
|
||||
->addSelect('users.email AS author_email')
|
||||
->whereNull('contents.deleted_at')
|
||||
->whereNull('contents.parentId')
|
||||
->where('contents.language', LaravelLocalization::getCurrentLocale())
|
||||
->where('modules.enabled', 1)
|
||||
->with('module')
|
||||
->with('module', 'subpages', 'subpages.module', 'subpages.templateModel', 'templateModel')
|
||||
->orderBy('contents.order')
|
||||
->orderBy('contents.created_at', 'DESC');
|
||||
|
||||
// filter results using handler
|
||||
|
||||
@@ -30,10 +30,10 @@ class Module extends Repository implements IModule
|
||||
|
||||
public function getContentModules()
|
||||
{
|
||||
return Cache::remember('enabled-content-modules', 3600, function () {
|
||||
return Cache::remember('enabled-content-modules', setting('cacheModules', 3600), function () {
|
||||
return ModuleModel::where('enabled', true)
|
||||
->where('manages_content', true)
|
||||
->get();
|
||||
->where('manages_content', true)
|
||||
->get();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,9 +53,14 @@ class Module extends Repository implements IModule
|
||||
return is_null($moduleHeading) ? $pageHeading : $moduleHeading;
|
||||
}
|
||||
|
||||
public function getOverviewTitle(ModuleModel $module)
|
||||
public function getOverviewTitle(ModuleModel $module = null)
|
||||
{
|
||||
$pageHeading = 'admin/contents.overview';
|
||||
|
||||
if (is_null($module)) {
|
||||
return $pageHeading;
|
||||
}
|
||||
|
||||
$moduleHeading = $this->call($module, 'getOverviewTitle');
|
||||
|
||||
return is_null($moduleHeading) ? $pageHeading : $moduleHeading;
|
||||
@@ -63,7 +68,7 @@ class Module extends Repository implements IModule
|
||||
|
||||
public function all()
|
||||
{
|
||||
return Cache::rememberForever('enabled-modules-and-template', function () {
|
||||
return Cache::remember('enabled-modules-and-template', setting('cacheModules', 3600), function () {
|
||||
return ModuleModel::where('enabled', true)
|
||||
->get();
|
||||
});
|
||||
@@ -71,7 +76,7 @@ class Module extends Repository implements IModule
|
||||
|
||||
public function getOnlyModules()
|
||||
{
|
||||
return Cache::rememberForever('enabled-modules', function () {
|
||||
return Cache::remember('enabled-modules', setting('cacheModules', 3600), function () {
|
||||
return ModuleModel::where('enabled', true)
|
||||
->where('is_template', false)
|
||||
->get();
|
||||
|
||||
@@ -70,7 +70,7 @@ class Template extends Repository implements ITemplate
|
||||
|
||||
public function getForModule(Module $module)
|
||||
{
|
||||
return Cache::rememberForever('templates-for-module', function () use ($module) {
|
||||
return Cache::remember('templates-for-module-'.$module->id, setting('cacheModules', 3600), function () use ($module) {
|
||||
return TemplateModel::where('module_id', $module->id)
|
||||
->select(['template_name', 'name', 'description'])
|
||||
->get();
|
||||
|
||||
@@ -12,9 +12,10 @@ use Illuminate\Support\Facades\Route;
|
||||
|
||||
class Menu implements WidgetRenderer
|
||||
{
|
||||
public static $template = null;
|
||||
|
||||
/**
|
||||
* Method used for displaying widget.
|
||||
*
|
||||
* This method renders some HTML code for displaying.
|
||||
*
|
||||
* @param Widget $widget
|
||||
@@ -27,6 +28,11 @@ class Menu implements WidgetRenderer
|
||||
$links = (array)$settings->links;
|
||||
$pageIds = array_keys($links);
|
||||
|
||||
if (!self::$template) {
|
||||
throw new \Exception('neni nastavena sablona');
|
||||
return;
|
||||
}
|
||||
|
||||
// get pages
|
||||
$pages = WidgetMenu::selectContentPages($pageIds);
|
||||
|
||||
@@ -40,14 +46,15 @@ class Menu implements WidgetRenderer
|
||||
$parameters = Route::current()
|
||||
->parameters();
|
||||
|
||||
$code = '<ul class="menu ' . $widget->container_class . '"';
|
||||
// $code = '<ul class="menu ' . $widget->container_class . '"';
|
||||
|
||||
if ($widget->container_id) {
|
||||
$code .= ' id="' . e($widget->container_id) . '"';
|
||||
}
|
||||
// if ($widget->container_id) {
|
||||
// $code .= ' id="' . e($widget->container_id) . '"';
|
||||
// }
|
||||
|
||||
$code .= '>';
|
||||
// $code .= '>';
|
||||
|
||||
$menuItems = [];
|
||||
foreach ($links as $pageId => $link) {
|
||||
if (!isset($pages[$pageId])) {
|
||||
continue;
|
||||
@@ -57,16 +64,22 @@ class Menu implements WidgetRenderer
|
||||
$active = ($page->isHomepage && $routeName == 'page.index') || ($routeName == 'page.content' && isset($parameters['url']) && $parameters['url'] == $page->url);
|
||||
$route = $page->isHomepage ? route('page.index') : route('page.content', ['url' => $page->url]);
|
||||
|
||||
$code .= '<li';
|
||||
if ($active) {
|
||||
$code .= ' class="is-active active"';
|
||||
}
|
||||
$code .= '><a href="' . $route . '">' . e($page->title) . '</a></li>';
|
||||
$menuItems[] = compact('page', 'active', 'route');
|
||||
|
||||
// $code .= '<li';
|
||||
// if ($active) {
|
||||
// $code .= ' class="is-active active"';
|
||||
}
|
||||
// $code .= '><a href="' . $route . '">' . e($page->title) . '</a></li>';
|
||||
// }
|
||||
|
||||
$code .= "</ul>";
|
||||
// $code .= "</ul>";
|
||||
|
||||
return $code;
|
||||
return view(self::$template, [
|
||||
'containerClass' => $widget->container_class,
|
||||
'containerId' => $widget->container_id,
|
||||
'menuItems' => $menuItems
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,7 @@ class ChangePassword extends Command
|
||||
$email = $this->argument('email');
|
||||
try {
|
||||
$user = User::where(['email' => $email])
|
||||
->first();
|
||||
->firstOrFail();
|
||||
} catch (ModelNotFoundException $e) {
|
||||
$this->error('Invalid user ' . $email . '.');
|
||||
return;
|
||||
|
||||
@@ -13,14 +13,14 @@ class InitDatabaseSchema extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'db:init {schema.sql}';
|
||||
protected $signature = 'db:init';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Command description';
|
||||
protected $description = 'Init database structure';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
@@ -39,13 +39,16 @@ class InitDatabaseSchema extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
do {
|
||||
$schema = $this->ask('Path to database schema file', 'database/schema.sql');
|
||||
} while (!file_exists($schema));
|
||||
|
||||
$schema = $this->argument('schema.sql');
|
||||
|
||||
if (!file_exists($schema)) throw new NotFoundHttpException('File '.$schema.' does not exist.');
|
||||
|
||||
DB::unprepared(file_get_contents($schema));
|
||||
|
||||
$this->line('Successfully imported');
|
||||
$this->call('migrate');
|
||||
$this->call('db:seed');
|
||||
|
||||
$this->line('Database successfully initialized.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Language;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\QueryException;
|
||||
|
||||
class InstallCMS extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'cms:install';
|
||||
/**
|
||||
* The console command description.
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Try to make installation of CMS as easy as possible';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// check and eventualy create .env file
|
||||
if (!file_exists(base_path('.env'))) {
|
||||
$this->createEnv();
|
||||
|
||||
$this->line('Rerun this script again, we need to load changes.');
|
||||
return;
|
||||
}
|
||||
|
||||
// install php dependencies
|
||||
$parameters = env('APP_DEBUG') ? '' : '--no-dev';
|
||||
exec('composer install ' . $parameters);
|
||||
|
||||
// init database
|
||||
try {
|
||||
// does exist something in laguages table? (Or event table itself?)
|
||||
Language::all();
|
||||
} catch (QueryException $e) {
|
||||
// database is not initialized
|
||||
$this->call('db:init');
|
||||
}
|
||||
|
||||
// install npm dependencies
|
||||
exec('npm install');
|
||||
exec('bower install');
|
||||
exec('gulp');
|
||||
|
||||
// change chmod of needed directories
|
||||
$directories = ['bootstrap/cache', 'storage', 'public'];
|
||||
foreach ($directories as $directory) {
|
||||
$this->changeChmod($directory);
|
||||
}
|
||||
$this->line('Chmod changed');
|
||||
|
||||
// generate APP_KEY in .env
|
||||
$this->call('key:generate');
|
||||
|
||||
// create user
|
||||
$this->line('Creating admin user...');
|
||||
do {
|
||||
$email = $this->ask('E-mail for admin user');
|
||||
} while (empty($email));
|
||||
|
||||
$this->call('user:new', compact('email'));
|
||||
|
||||
$this->line('All done. CMS should be prepared.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert value into .env config file
|
||||
*
|
||||
* @param $envConstant
|
||||
* @param $value
|
||||
*/
|
||||
private function setEnvConstant($envConstant, $value)
|
||||
{
|
||||
$content = file_get_contents(base_path('.env'));
|
||||
$content = preg_replace('/' . $envConstant . '=(.*)/i', $envConstant . '=' . $value, $content);
|
||||
file_put_contents(base_path('.env'), $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create .env file with all needed constants
|
||||
*/
|
||||
private function createEnv()
|
||||
{
|
||||
\File::copy(base_path('.env.example'), base_path('.env'));
|
||||
|
||||
// set environment
|
||||
do {
|
||||
$env = $this->ask('Install environment: ', 'local');
|
||||
} while (empty($env));
|
||||
$this->setEnvConstant('APP_ENV', $env);
|
||||
|
||||
// set debugging
|
||||
if ($env == 'local') {
|
||||
$debug = 'true';
|
||||
} else {
|
||||
$debug = $this->confirm('Enable debugging?', true) ? 'true' : 'false';
|
||||
}
|
||||
$this->setEnvConstant('APP_DEBUG', $debug);
|
||||
|
||||
// setup database
|
||||
$databaseFields = [
|
||||
'DB_HOST' => 'Database server',
|
||||
'DB_DATABASE' => 'Database name',
|
||||
'DB_USERNAME' => 'Database user',
|
||||
'DB_PASSWORD' => 'Password'
|
||||
];
|
||||
foreach ($databaseFields as $constant => $text) {
|
||||
do {
|
||||
$value = $this->ask($text);
|
||||
} while (empty ($value));
|
||||
|
||||
$this->setEnvConstant($constant, $value);
|
||||
}
|
||||
|
||||
// set app url
|
||||
do {
|
||||
$url = $this->ask('Application URL');
|
||||
$url = trim($url, ' /');
|
||||
} while (empty($url));
|
||||
$this->setEnvConstant('SITE_URL', $url);
|
||||
$this->setEnvConstant('UPLOAD_URL', $url . '/public/uploads');
|
||||
$this->setEnvConstant('THUMBNAIL_URL', $url . '/public/uploads/thumbnails');
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively change chmod to 777 in given directory
|
||||
*
|
||||
* @param $directory
|
||||
*/
|
||||
private function changeChmod($directory)
|
||||
{
|
||||
@chmod($directory, 0777);
|
||||
|
||||
if (!is_dir($directory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subdirectories = \File::allFiles($directory);
|
||||
|
||||
foreach ($subdirectories as $subdirectory) {
|
||||
$this->changeChmod($subdirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Modules;
|
||||
|
||||
use App\Models\Module;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class CreateMigration extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'module:make-migration {module?} {name?} {--t|table= : Table name} {--c|create : Use template for creating new table}';
|
||||
/**
|
||||
* The console command description.
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Make new migration for given module';
|
||||
protected $migrationName;
|
||||
protected $tableName;
|
||||
protected $moduleName;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->moduleName = $module = $this->argument('module');
|
||||
$this->migrationName = $name = $this->argument('name');
|
||||
$this->tableName = trim($this->option('table'), ' =');
|
||||
|
||||
$modules = Module::where('enabled', true)
|
||||
->get()
|
||||
->pluck('static_name', 'id')
|
||||
->toArray();
|
||||
|
||||
while (is_null($module) || !in_array($module, $modules)) {
|
||||
$module = $this->anticipate('Input valid module name', $modules);
|
||||
}
|
||||
|
||||
$selectedModule = Module::where('static_name', $module)
|
||||
->first();
|
||||
|
||||
$basePath = $selectedModule->is_template ? config('modules.path.template') : config('modules.paths.modules');
|
||||
$path = $basePath . '/' . $module . '/Database/Migrations/';
|
||||
if (!is_dir($path)) {
|
||||
mkdir($path, 0777, true);
|
||||
}
|
||||
|
||||
while (is_null($name)) {
|
||||
$this->migrationName = $name = $this->ask('Input migration name');
|
||||
}
|
||||
|
||||
$stubName = $this->option('create') ? 'create' : 'new';
|
||||
$content = file_get_contents(config('modules.paths.stubs') . '/Migrations/' . $stubName . '.stub');
|
||||
$content = $this->replacePlaceholders($content);
|
||||
|
||||
$migrationName = $path . $this->getMigrationName($name);
|
||||
|
||||
file_put_contents($migrationName, $content);
|
||||
|
||||
$this->line('Migration successfully created.', 'info');
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and replace all placeholders in given content
|
||||
*
|
||||
* @param $content
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function replacePlaceholders($content)
|
||||
{
|
||||
$replace = [
|
||||
'$MigrationName$' => studly_case($this->migrationName),
|
||||
'$ModuleName$' => $this->moduleName,
|
||||
'$TableName$' => $this->tableName
|
||||
];
|
||||
|
||||
foreach ($replace as $what => $for) {
|
||||
$content = str_replace($what, $for, $content);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get prefix for created migration
|
||||
* @return false|string
|
||||
*/
|
||||
protected function getDatePrefix()
|
||||
{
|
||||
return date('Y_m_d_His');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getMigrationName($name)
|
||||
{
|
||||
return $this->getDatePrefix() . '_' . snake_case($name) . '.php';
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ class CreateNew extends Command
|
||||
mkdir($path);
|
||||
|
||||
// copy stub files
|
||||
\File::copyDirectory(config('modules.paths.stubs'), $path);
|
||||
\File::copyDirectory(config('modules.paths.stubs').'/NewModule', $path);
|
||||
|
||||
$files = \File::allFiles($path);
|
||||
foreach ($files as $file) {
|
||||
@@ -64,6 +64,9 @@ class CreateNew extends Command
|
||||
}
|
||||
|
||||
$this->info('Module successfully created.');
|
||||
|
||||
$this->call('module:install', ['name' => $this->moduleName]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -40,6 +40,7 @@ class DisableModule extends Command
|
||||
$module->save();
|
||||
|
||||
\Cache::forget('enabled-modules');
|
||||
\Cache::forget('enabled-content-modules');
|
||||
\Cache::forget('enabled-modules-and-template');
|
||||
|
||||
$this->line('Module ' . $module->static_name . ' disabled.');
|
||||
|
||||
@@ -44,6 +44,7 @@ class EnableModule extends Command
|
||||
$module->save();
|
||||
|
||||
\Cache::forget('enabled-modules');
|
||||
\Cache::forget('enabled-content-modules');
|
||||
\Cache::forget('enabled-modules-and-template');
|
||||
|
||||
$this->line('Module ' . $module->static_name . ' enabled.');
|
||||
|
||||
@@ -15,13 +15,11 @@ class InstallModule extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'module:install {name} {parentModuleId?}';
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Custom installation of CMS module. Name given as directory name in /modules.';
|
||||
@@ -31,7 +29,6 @@ class InstallModule extends Command
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
@@ -56,8 +53,9 @@ class InstallModule extends Command
|
||||
// get config
|
||||
$config = $this->loadConfig();
|
||||
|
||||
if (empty($config))
|
||||
if (empty($config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if it is valid
|
||||
$this->checkMandatoryFields($config);
|
||||
@@ -69,14 +67,17 @@ class InstallModule extends Command
|
||||
Cache::forget('enabled-modules');
|
||||
Cache::forget('module-handler-to-id-enumeration');
|
||||
|
||||
exec('php artisan module:enable '.$name);
|
||||
exec('php artisan cache:clear');
|
||||
exec('composer dump-autoload');
|
||||
|
||||
// inform user
|
||||
$this->line('Module ' . $name . ' successfuly installed');
|
||||
$this->line('Module ' . $name . ' successfuly installed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load config from Config/config.php in module files. We cannot use config() because the file have not published
|
||||
* yet.
|
||||
*
|
||||
* @return array
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
@@ -85,8 +86,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')
|
||||
if (basename($basePath) == 'Database') {
|
||||
return [];
|
||||
}
|
||||
|
||||
// check if module has Config/config.php file
|
||||
if (!file_exists($path)) {
|
||||
@@ -121,7 +123,6 @@ class InstallModule extends Command
|
||||
|
||||
/**
|
||||
* Installs module into database.
|
||||
*
|
||||
* This method calls Module::install, which handles specific installation of module. In this way we should be able
|
||||
* to install any module even with some other database structures.
|
||||
*
|
||||
@@ -155,9 +156,9 @@ class InstallModule extends Command
|
||||
if (is_null($module)) {
|
||||
// insert module into database
|
||||
$settings = [
|
||||
'providers' => $providers,
|
||||
'order' => isset($config['order']) ? $config['order'] : 0,
|
||||
'files' => isset($config['files']) ? $config['files'] : []
|
||||
'providers' => $providers,
|
||||
'order' => isset($config['order']) ? $config['order'] : 0,
|
||||
'files' => isset($config['files']) ? $config['files'] : []
|
||||
];
|
||||
|
||||
$module = Installer::install($name, $alias, $this->moduleName, $description, $handler, $this->parentModuleId, $settings);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Modules;
|
||||
|
||||
use App\Models\Module;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class RunMigrations extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'module:migrate {module?} {--rollback : Rollback one migration}';
|
||||
/**
|
||||
* The console command description.
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Run migrations for all modules or for one module';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$modules = Module::where('enabled', true);
|
||||
if (!is_null($module = $this->argument('module'))) {
|
||||
$modules = $modules->where('static_name', $module);
|
||||
}
|
||||
$modules = $modules->get();
|
||||
|
||||
if ($modules->isEmpty()) {
|
||||
$this->error('No valid modules.');
|
||||
return;
|
||||
}
|
||||
|
||||
$command = $this->option('rollback') ? 'migrate:rollback' : 'migrate';
|
||||
foreach ($modules as $module) {
|
||||
$this->info('Migrating '.$module->static_name);
|
||||
|
||||
$basePath = $module->is_template ? config('modules.path.templates') : config('modules.paths.modules');
|
||||
$path = $basePath . '/' . $module->static_name . '/Database/Migrations/';
|
||||
$path = str_replace(base_path(), '', $path);
|
||||
|
||||
$this->call($command, ['--path' => $path]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\CMS\Enumerations\UserRoles;
|
||||
use App\Events\CreateNewUser;
|
||||
use App\Models\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
@@ -11,13 +12,11 @@ class NewUser extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'user:new {email}';
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Creates new user';
|
||||
@@ -27,13 +26,9 @@ class NewUser extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$email = $this->argument('email');
|
||||
|
||||
// check if username is really email
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$this->error($email . ' is not a valid e-mail!');
|
||||
return;
|
||||
}
|
||||
do {
|
||||
$email = $this->argument('email');
|
||||
} while (!filter_var($email, FILTER_VALIDATE_EMAIL));
|
||||
|
||||
// ask for password
|
||||
do {
|
||||
@@ -63,6 +58,8 @@ class NewUser extends Command
|
||||
$user->role = $role;
|
||||
$user->save();
|
||||
|
||||
event(new CreateNewUser($user));
|
||||
|
||||
$this->info('User ' . $email . ' successfully created.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ class SetTemplate extends Command
|
||||
}
|
||||
|
||||
try {
|
||||
Installer::install($config['name'], $config['alias'], $current->static_name, $config['description'], $config['handler'], false, $config);
|
||||
Installer::install($config['name'], $config['alias'], $config['staticName'], $config['description'], $config['handler'], false, $config);
|
||||
} catch (\Exception $e) {
|
||||
$this->error('Cannot install template ' . $template . '. Message: ' . $e->getMessage());
|
||||
return;
|
||||
@@ -74,7 +74,7 @@ class SetTemplate extends Command
|
||||
// run again artisan for publishing resources
|
||||
// it cannot be done using $this->call, because we need to load different template into service providers
|
||||
if ($publish) {
|
||||
exec('php artisan vendor:publish');
|
||||
exec('php artisan template:publish');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use App\Console\Commands\InstallCMS;
|
||||
use App\Console\Commands\Modules\CreateMigration;
|
||||
use App\Console\Commands\Modules\RunMigrations;
|
||||
use App\Console\Commands\Modules\CreateNew;
|
||||
use App\Console\Commands\Modules\DisableModule;
|
||||
use App\Console\Commands\Modules\EnableModule;
|
||||
@@ -32,7 +35,10 @@ class Kernel extends ConsoleKernel
|
||||
Commands\SetTemplate::class,
|
||||
Commands\AddLanguage::class,
|
||||
PublishTemplateResources::class,
|
||||
TemplateInfo::class
|
||||
TemplateInfo::class,
|
||||
InstallCMS::class,
|
||||
CreateMigration::class,
|
||||
RunMigrations::class
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,7 @@ class ContentOverview extends Controller
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
* @internal param int|null $moduleId
|
||||
*/
|
||||
public function index(Module $module = null, $status = null)
|
||||
public function index(Module $module, $status = null)
|
||||
{
|
||||
// get module settings
|
||||
$pageHeading = $this->module->getOverviewTitle($module);
|
||||
@@ -46,10 +46,13 @@ class ContentOverview extends Controller
|
||||
// get homepage - for checking if homepage is set and available
|
||||
$homepage = $this->content->getHomepage();
|
||||
|
||||
$contentModules = app(\App\CMS\Repositories\Module::class)->getContentModules();
|
||||
|
||||
return view('admin.content.overview', [
|
||||
'pages' => $pages,
|
||||
'homepage' => $homepage,
|
||||
'pageHeading' => $pageHeading,
|
||||
'contentModules' => $contentModules,
|
||||
'statuses' => ContentStatuses::getWithTranslations(),
|
||||
'module' => isset($module) ? $module : null
|
||||
]);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin\Contents;
|
||||
|
||||
use App\CMS\Contracts\Repositories\Content as IContent;
|
||||
@@ -11,13 +12,12 @@ use App\Http\Controllers\Admin\Controller;
|
||||
use App\Http\Requests\CreateNewContentRequest;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Module;
|
||||
use Illuminate\Support\Collection;
|
||||
use Modules\Categories\Contracts\Repositories\Category as ICategory;
|
||||
|
||||
/**
|
||||
* Class CreateNewContent
|
||||
*
|
||||
* Controller for creating new content page.
|
||||
*
|
||||
* @package App\Http\Controllers\Admin\Contents
|
||||
*/
|
||||
class CreateNewContent extends Controller
|
||||
@@ -30,10 +30,6 @@ class CreateNewContent extends Controller
|
||||
* @var IModuleRepository
|
||||
*/
|
||||
private $module;
|
||||
/**
|
||||
* @var ICategory
|
||||
*/
|
||||
private $category;
|
||||
/**
|
||||
* @var ITemplate
|
||||
*/
|
||||
@@ -41,23 +37,20 @@ class CreateNewContent extends Controller
|
||||
|
||||
/**
|
||||
* CreateNewContent constructor.
|
||||
*
|
||||
* Sets File and Content models into class variables
|
||||
*
|
||||
* @param IContent|Content $content
|
||||
* @param IModuleRepository $module
|
||||
* @param ICategory $category
|
||||
*
|
||||
* @param ITemplate $template
|
||||
*
|
||||
* @internal param File $file
|
||||
*/
|
||||
public function __construct(IContent $content, IModuleRepository $module, ICategory $category, ITemplate $template)
|
||||
public function __construct(IContent $content, IModuleRepository $module, ITemplate $template)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->content = $content;
|
||||
$this->module = $module;
|
||||
$this->category = $category;
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
@@ -73,7 +66,11 @@ class CreateNewContent extends Controller
|
||||
$statuses = ContentStatuses::getWithTranslations();
|
||||
$pageHeading = $this->module->getCreateContentTitle($module);
|
||||
$templates = $this->template->getForModule($module);
|
||||
$categories = $this->category->getForModule($module);
|
||||
|
||||
$categories = new Collection();
|
||||
if (class_exists(ICategory::class)) {
|
||||
$categories = app(ICategory::class)->getForModule($module);
|
||||
}
|
||||
|
||||
$page = $this->content->getOne();
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin\Contents;
|
||||
|
||||
use App\CMS\Contracts\Repositories\Content as IContent;
|
||||
@@ -6,20 +7,15 @@ use App\CMS\Contracts\Repositories\Module as IModuleRepository;
|
||||
use App\CMS\Contracts\Repositories\Template as ITemplate;
|
||||
use App\CMS\Enumerations\ContentStatuses;
|
||||
use App\Cms\Handlers\Interactions\Interaction;
|
||||
use App\CMS\Helpers\CurrentTemplate;
|
||||
use App\Cms\Interactions\Admin\Content\EditContent as EditContentInteraction;
|
||||
use App\Events\AttemptToEditInvalidContent;
|
||||
use App\Http\Controllers\Admin\Controller;
|
||||
use App\Http\Requests\EditContentRequest;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Content\Content;
|
||||
use App\Models\Content\ContentHistory;
|
||||
use App\Models\FileManager\File;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Collection;
|
||||
use Modules\Categories\Contracts\Repositories\Category as ICategory;
|
||||
use Modules\Categories\Repositories\Category;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class EditContent extends Controller
|
||||
{
|
||||
@@ -39,10 +35,6 @@ class EditContent extends Controller
|
||||
* @var IModuleRepository
|
||||
*/
|
||||
private $module;
|
||||
/**
|
||||
* @var ICategory
|
||||
*/
|
||||
private $category;
|
||||
/**
|
||||
* @var ITemplate
|
||||
*/
|
||||
@@ -50,7 +42,6 @@ class EditContent extends Controller
|
||||
|
||||
/**
|
||||
* EditContent constructor.
|
||||
*
|
||||
* Load ContentHistory and File into this controller
|
||||
*
|
||||
* @param ContentHistory $history
|
||||
@@ -60,13 +51,12 @@ class EditContent extends Controller
|
||||
* @param ICategory $category
|
||||
* @param ITemplate $template
|
||||
*/
|
||||
public function __construct(ContentHistory $history, File $file, IContent $content, IModuleRepository $module, Category $category, ITemplate $template)
|
||||
public function __construct(ContentHistory $history, File $file, IContent $content, IModuleRepository $module, ITemplate $template)
|
||||
{
|
||||
$this->history = $history;
|
||||
$this->file = $file;
|
||||
$this->content = $content;
|
||||
$this->module = $module;
|
||||
$this->category = $category;
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
@@ -85,9 +75,13 @@ class EditContent extends Controller
|
||||
$module = $this->module->getByHandler($page->moduleHandler);
|
||||
$pageHeading = $this->module->getUpdateContentTitle($module, $page);
|
||||
$templates = $this->template->getForModule($module);
|
||||
$categories = $this->category->getForModule($module);
|
||||
$history = $this->history->getStructuredHistoryOf($page);
|
||||
|
||||
$categories = new Collection();
|
||||
if (class_exists(ICategory::class)) {
|
||||
$categories = app(ICategory::class)->getForModule($module);
|
||||
}
|
||||
|
||||
// render view
|
||||
return view('admin.content.edit', [
|
||||
'pageHeading' => $pageHeading,
|
||||
@@ -95,7 +89,8 @@ class EditContent extends Controller
|
||||
'page' => $page,
|
||||
'module' => $module,
|
||||
'categories' => $categories,
|
||||
'rootPages' => $this->content->getOverview()->get(),
|
||||
'rootPages' => $this->content->getOverview()
|
||||
->get(),
|
||||
'templates' => $templates,
|
||||
'formSectionNames' => $this->module->call($module, 'getExtendedFormSections'),
|
||||
'formSections' => $this->module->call($module, 'getExtendedFormInputsEdit'),
|
||||
@@ -122,4 +117,24 @@ class EditContent extends Controller
|
||||
->route('content.overview')
|
||||
->with('success', trans('admin/contents.successfullyEdited'));
|
||||
}
|
||||
|
||||
public function order() {
|
||||
$pages = $this->content->getOverview()->get();
|
||||
|
||||
return view('admin.content.order', compact('pages'));
|
||||
}
|
||||
|
||||
public function setOrder(Request $request) {
|
||||
|
||||
$data = $request->get('page');
|
||||
|
||||
foreach ($data as $id => $order) {
|
||||
Content::where('id', $id)->update(['order' => $order]);
|
||||
}
|
||||
|
||||
// something was changed, inform user
|
||||
return redirect()
|
||||
->route('content.overview')
|
||||
->with('success', trans('admin/contents.orderSuccessfullySet'));
|
||||
}
|
||||
}
|
||||
@@ -30,12 +30,12 @@ class UploadedFilesOverview extends Controller
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function index(Directory $directory = null)
|
||||
public function index(Directory $directory)
|
||||
{
|
||||
// include hidden directories?
|
||||
$subdirectories = $this->directoryRepository->getAllSubdirectories($directory);
|
||||
|
||||
if (!Input::get('hidden', true)) {
|
||||
if (!Input::get('hidden', false)) {
|
||||
$subdirectories = $subdirectories->where('hidden', false);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ class Settings extends Controller
|
||||
*/
|
||||
public function saveSettings(Requests\SaveSettingsRequest $request)
|
||||
{
|
||||
|
||||
$this->settings->save($request->except(['_token']));
|
||||
|
||||
return redirect()
|
||||
|
||||
@@ -98,7 +98,7 @@ class EditTemplate extends Controller
|
||||
// try to save content
|
||||
if (!is_writable($customDir) && !is_writable($originalDir)) {
|
||||
return redirect()
|
||||
->route('templates.edit', ['id' => $request->id])
|
||||
->route('templates.edit', ['id' => $template->id])
|
||||
->withInput()
|
||||
->with('warning', trans('admin/templates.cannotWriteContent'));
|
||||
}
|
||||
|
||||
@@ -7,6 +7,16 @@ use App\Models\Templates\TemplatePath;
|
||||
|
||||
class TemplateOverview extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
|
||||
$templatePaths = TemplatePath::with('subdirectories')
|
||||
->whereNull('parent_id')
|
||||
->get();
|
||||
|
||||
return view('admin.templates.index', compact('templatePaths'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display overview of all templates in given path
|
||||
*
|
||||
@@ -17,12 +27,8 @@ class TemplateOverview extends Controller
|
||||
public function getPath(TemplatePath $templatePath)
|
||||
{
|
||||
return view('admin.templates.list', [
|
||||
'directory' => $templatePath,
|
||||
'isWritable' => is_writable(base_path($templatePath->path)),
|
||||
'templates' => $templatePath->templates()
|
||||
->get(),
|
||||
'subdirectories' => $templatePath->subdirectories()
|
||||
->get()
|
||||
'directory' => $templatePath,
|
||||
'isWritable' => is_writable(base_path($templatePath->path))
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ class Controller extends BaseController
|
||||
|
||||
/**
|
||||
* Custom wrapper for generating view.
|
||||
*
|
||||
* This method will be used for switching among templates.
|
||||
*
|
||||
* @param $template
|
||||
@@ -25,8 +24,23 @@ class Controller extends BaseController
|
||||
*/
|
||||
public static function visitorsView($template, $data = [], $mergeData = [])
|
||||
{
|
||||
$view = Template::name($template);
|
||||
$view = self::getView($template, $data, $mergeData);
|
||||
|
||||
return response(view($view, $data, $mergeData))->header('X-Powered-By', strrev('zc.ytiranib.www//:sptth | .o.r.s ytiranib | SMCiniM'));
|
||||
return response($view)->header('X-Powered-By', strrev('zc.ytiranib.www//:sptth | .o.r.s ytiranib | SMCiniM'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get instance of view for passing into response
|
||||
*
|
||||
* @param $template
|
||||
* @param $data
|
||||
* @param $mergeData
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public static function getView($template, $data = [], $mergeData = [])
|
||||
{
|
||||
$view = Template::name($template);
|
||||
return view($view, $data, $mergeData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ class TemplatePathInstaller
|
||||
// install registered widgets
|
||||
$registator->saveTemplates();
|
||||
|
||||
$registator->deleteNonExistingPaths();
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,6 @@ class CreateImageThumbnail implements ShouldQueue
|
||||
});
|
||||
|
||||
// save thumbnail with lower quality
|
||||
$image->save(config('app.thumbnail_path') . DIRECTORY_SEPARATOR . $file->getFilename(), 75);
|
||||
$image->save(config('app.thumbnail_path') . DIRECTORY_SEPARATOR . $file->getFilename(), setting('previewQuality', 75));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\CMS\Helpers\Traits\Nullable;
|
||||
use App\CMS\Observers\Content as ContentObserver;
|
||||
use App\Models\FileManager\File;
|
||||
use App\Models\Module;
|
||||
use App\Models\Templates\Template;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@@ -33,6 +34,7 @@ class Content extends Model
|
||||
'isHomepage',
|
||||
'title',
|
||||
'url',
|
||||
'order',
|
||||
'metaKeywords',
|
||||
'metaDescription',
|
||||
'pageContent',
|
||||
@@ -63,6 +65,10 @@ class Content extends Model
|
||||
return $this->hasMany(ContentHistory::class);
|
||||
}
|
||||
|
||||
public function templateModel() {
|
||||
return $this->belongsTo(Template::class, 'template', 'template_name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark relationship with users table
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
@@ -87,6 +93,10 @@ class Content extends Model
|
||||
*/
|
||||
public function category()
|
||||
{
|
||||
if (!class_exists(Category::class)) {
|
||||
throw new \ErrorException('Categories are not installed');
|
||||
}
|
||||
|
||||
return $this->belongsTo(Category::class);
|
||||
}
|
||||
|
||||
@@ -96,7 +106,24 @@ class Content extends Model
|
||||
*/
|
||||
public function subpages()
|
||||
{
|
||||
return $this->hasMany(Content::class, 'parentId');
|
||||
return $this->hasMany(Content::class, 'parentId')->orderBy('order')->orderBy('created_at', 'DESC');
|
||||
}
|
||||
|
||||
/**
|
||||
* Subpages assigned to page
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function publishedSubpages()
|
||||
{
|
||||
$query = $this->subpages();
|
||||
|
||||
if (\Auth::check()) {
|
||||
$query = $query->where('status', '!=', ContentContract::STATUS_DRAFT);
|
||||
} else {
|
||||
$query = $query->where('status', ContentContract::STATUS_PUBLIC);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,8 +22,12 @@ class ContentHistory extends Model
|
||||
'new_value',
|
||||
'old_value'
|
||||
];
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* Author of the change
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function author()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
|
||||
|
||||
class Settings extends Model
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Policies;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
abstract class BasicPolicy
|
||||
{
|
||||
@@ -24,8 +25,6 @@ abstract class BasicPolicy
|
||||
if (!$this->manage($user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\CMS\Contracts\Repositories\Content as ContentContract;
|
||||
use App\Models\Content\Content;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
@@ -21,10 +22,6 @@ class ContentPolicy extends BasicPolicy
|
||||
*/
|
||||
public function update(User $user, Content $content)
|
||||
{
|
||||
// if (!$user->hasAbility('update-contents-' . $content->module->alias)) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
if ($user->id != $content->user_id) {
|
||||
return $user->hasAbility('update-contents') && $user->hasAbility('update-all-contents');
|
||||
};
|
||||
@@ -32,21 +29,90 @@ class ContentPolicy extends BasicPolicy
|
||||
return $user->hasAbility('update-contents');
|
||||
}
|
||||
|
||||
public function create(User $user, Content $content) {
|
||||
/**
|
||||
* Does given user has permissions to create new content page?
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function create(User $user)
|
||||
{
|
||||
return $user->hasAbility('create-contents');
|
||||
}
|
||||
|
||||
public function publish(User $user, Content $content)
|
||||
/**
|
||||
* Change page status
|
||||
*
|
||||
* @param User $user
|
||||
* @param Content $page
|
||||
* @param $status
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function changeStatus(User $user, Content $page, $status)
|
||||
{
|
||||
switch ($status) {
|
||||
case ContentContract::STATUS_PUBLIC:
|
||||
return $this->publish($user, $page);
|
||||
break;
|
||||
case ContentContract::STATUS_DRAFT:
|
||||
return $this->draft($user, $page);
|
||||
break;
|
||||
case ContentContract::STATUS_PROTECTED:
|
||||
return $this->protect($user, $page);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can user publish given page?
|
||||
*
|
||||
* @param User $user
|
||||
* @param Content $page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function publish(User $user, Content $page)
|
||||
{
|
||||
return $user->hasAbility('publish-contents');
|
||||
}
|
||||
|
||||
public function setHomepage(User $user, Content $content)
|
||||
/**
|
||||
* Can user make draft from given page?
|
||||
*
|
||||
* @param User $user
|
||||
* @param Content $page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function draft(User $user, Content $page)
|
||||
{
|
||||
return $user->hasAbility('draft-contents');
|
||||
}
|
||||
|
||||
/**
|
||||
* Can user publish page only for protected users?
|
||||
*
|
||||
* @param User $user
|
||||
* @param Content $page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function protect(User $user, Content $page)
|
||||
{
|
||||
return $user->hasAbility('protect-contents');
|
||||
}
|
||||
|
||||
public function setHomepage(User $user)
|
||||
{
|
||||
return $user->hasAbility('set-homepage-contents');
|
||||
}
|
||||
|
||||
public function viewHistory(User $user, Content $content) {
|
||||
public function viewHistory(User $user)
|
||||
{
|
||||
return $user->hasAbility('view-history-contents');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,6 @@ class AppServiceProvider extends ServiceProvider
|
||||
$this->app->register(DebugBarServiceProvider::class);
|
||||
}
|
||||
|
||||
// register PingPong module extension
|
||||
$this->app->register(LaravelLocalizationServiceProvider::class);
|
||||
|
||||
// create singletons for registering widgets
|
||||
@@ -120,7 +119,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
$registrator->register('basic', 'admin/settings.basic', 'admin.settings.parts.basic');
|
||||
$registrator->register('meta', 'admin/settings.meta', 'admin.settings.parts.meta');
|
||||
$registrator->register('social', 'admin/settings.socialSites', 'admin.settings.parts.social');
|
||||
$registrator->register('widgets', 'admin/settings.widgets.title', 'admin.settings.parts.widgets');
|
||||
$registrator->register('advanced', 'admin/settings.advanced.title', 'admin.settings.parts.advanced', 999);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,7 +138,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
$registrator = $this->app->make('displayPipeline');
|
||||
|
||||
$registrator->add(AddViewedToPage::class);
|
||||
$registrator->add(CompileShortcodes::class);
|
||||
// $registrator->add(CompileShortcodes::class);
|
||||
}
|
||||
|
||||
private function registerSavePipeline()
|
||||
|
||||
@@ -26,7 +26,7 @@ class BladeExtender extends ServiceProvider
|
||||
|
||||
$this->extendBlade();
|
||||
|
||||
Shortcode::register('widget', Widget::class);
|
||||
// Shortcode::register('widget', Widget::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php namespace App\Providers;
|
||||
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
|
||||
@@ -8,8 +9,19 @@ class LaravelLocalizationServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function register()
|
||||
{
|
||||
$languages = DB::table('languages')
|
||||
->get();
|
||||
try {
|
||||
$languages = DB::table('languages')
|
||||
->get();
|
||||
} catch (QueryException $e) {
|
||||
$supportedLocales[config('app.locale')] = [
|
||||
'native' => env('LOCALE_NATIVE_NAME', config('app.locale')),
|
||||
'script' => env('LOCALE_SCRIPT', 'Latn'),
|
||||
'name' => env('LOCALE_TRANSLATED_NAME', config('app.locale')),
|
||||
'code' => config('app.locale'),
|
||||
];
|
||||
$this->setSupportedLocales($supportedLocales);
|
||||
return;
|
||||
}
|
||||
|
||||
$supportedLocales = [];
|
||||
if (empty($languages)) {
|
||||
@@ -32,15 +44,20 @@ class LaravelLocalizationServiceProvider extends ServiceProvider
|
||||
];
|
||||
}
|
||||
|
||||
config([
|
||||
'laravellocalization.supportedLocales' => $supportedLocales,
|
||||
'laravellocalization.useAcceptLanguageHeader' => true,
|
||||
'laravellocalization.hideDefaultLocaleInURL' => true
|
||||
]);
|
||||
$this->setSupportedLocales($supportedLocales);
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
\App::setLocale(LaravelLocalization::getCurrentLocale());
|
||||
}
|
||||
|
||||
private function setSupportedLocales($supportedLocales)
|
||||
{
|
||||
config([
|
||||
'laravellocalization.supportedLocales' => $supportedLocales,
|
||||
'laravellocalization.useAcceptLanguageHeader' => true,
|
||||
'laravellocalization.hideDefaultLocaleInURL' => true
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,12 @@ class RouteServiceProvider extends ServiceProvider
|
||||
*/
|
||||
protected function mapAdminRoutes()
|
||||
{
|
||||
Route::namespace($this->namespace.'\\Admin')
|
||||
// separate route file for Auth::routes()
|
||||
Route::namespace($this->namespace)
|
||||
->group(base_path('routes/auth.php'));
|
||||
|
||||
// routes for administration
|
||||
Route::namespace($this->namespace . '\\Admin')
|
||||
->group(base_path('routes/admin.php'));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\CMS\Contracts\Repositories\Template as TemplateContract;
|
||||
use App\CMS\Handlers\Registrators\TemplateRegistrator;
|
||||
use App\CMS\Helpers\CurrentTemplate;
|
||||
use App\CMS\Repositories\Module;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
@@ -13,7 +14,11 @@ class TemplateServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function boot()
|
||||
{
|
||||
$template = CurrentTemplate::get();
|
||||
try {
|
||||
$template = CurrentTemplate::get();
|
||||
} catch (QueryException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore running from artisan
|
||||
if (App::runningInConsole() || App::environment('testing') || !$template) {
|
||||
@@ -22,12 +27,7 @@ class TemplateServiceProvider extends ServiceProvider
|
||||
return;
|
||||
}
|
||||
|
||||
// include files
|
||||
$files = isset($template->moduleSettings->files) ? $template->moduleSettings->files : [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
include_once $file;
|
||||
}
|
||||
// service providers and files already registered in ModuleManager
|
||||
|
||||
// register templates
|
||||
$publicTemplates = CurrentTemplate::getPublicTemplatePaths();
|
||||
|
||||
+1
-17
@@ -14,21 +14,5 @@ define('LARAVEL_START', microtime(true));
|
||||
|
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../app/CMS/Helpers/functions.php';
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Include The Compiled Class File
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| To dramatically increase your application's performance, you may use a
|
||||
| compiled class file which contains all of the classes commonly used
|
||||
| by a request. The Artisan "optimize" is used to create this file.
|
||||
|
|
||||
*/
|
||||
|
||||
$compiledPath = __DIR__.'/cache/compiled.php';
|
||||
|
||||
if (file_exists($compiledPath)) {
|
||||
require $compiledPath;
|
||||
}
|
||||
|
||||
+4
-3
@@ -5,8 +5,8 @@
|
||||
"dependencies": {
|
||||
"animate.css": "~3.4.0",
|
||||
"bootstrap": "v4.0.0-alpha.5",
|
||||
"font-awesome": "~4.5.0",
|
||||
"jquery": "~2.1.4",
|
||||
"font-awesome": "~4.7.0",
|
||||
"jquery": "~3.2.1",
|
||||
"metisMenu": "~2.0.3",
|
||||
"nprogress": "~0.2.0",
|
||||
"responsive-bootstrap-toolkit": "~2.5.0",
|
||||
@@ -14,6 +14,7 @@
|
||||
"tether": "~1.3.7",
|
||||
"tinycolor": "~1.2.1",
|
||||
"dropzone": "~4.2.0",
|
||||
"jquery.are-you-sure": "~1.9"
|
||||
"jquery.are-you-sure": "~1.9",
|
||||
"chart.js": "^2.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -8,13 +8,14 @@
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=7.0.0",
|
||||
"laravel/framework": "5.4.*",
|
||||
"laravel/framework": "5.5.*",
|
||||
"watson/sitemap": "^2.0",
|
||||
"mcamara/laravel-localization": "^1.1",
|
||||
"franzose/closure-table": "5.*",
|
||||
"intervention/image": "^2.3",
|
||||
"webwizo/laravel-shortcodes": "1.0.4",
|
||||
"webwizo/laravel-shortcodes": "1.*",
|
||||
"greggilbert/recaptcha": "^2.2@dev",
|
||||
"doctrine/dbal": "^2.7@dev",
|
||||
"nwidart/laravel-menus": "dev-master"
|
||||
},
|
||||
"require-dev": {
|
||||
|
||||
Generated
+776
-188
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -160,7 +160,7 @@ return [
|
||||
Franzose\ClosureTable\ClosureTableServiceProvider::class,
|
||||
\Watson\Sitemap\SitemapServiceProvider::class,
|
||||
Intervention\Image\ImageServiceProvider::class,
|
||||
Webwizo\Shortcodes\ShortcodesServiceProvider::class,
|
||||
// Webwizo\Shortcodes\ShortcodesServiceProvider::class,
|
||||
/*
|
||||
* Application Service Providers...
|
||||
*/
|
||||
|
||||
+3
-5
@@ -3,11 +3,9 @@
|
||||
return [
|
||||
|
||||
'styles' => [
|
||||
'foundation-drilldown' => App\CMS\MenuPresenters\FoundationDrilldownMenuPresenter::class,
|
||||
'foundation-dropdown' => App\CMS\MenuPresenters\FoundationDropdownMenuPresenter::class,
|
||||
'foundation-dropdown-initialized' => App\CMS\MenuPresenters\FoundationInitializedDropdownMenuPresenter::class,
|
||||
'metismenu' => App\CMS\MenuPresenters\MetismenuPresenter::class,
|
||||
'profile-menu' => App\CMS\MenuPresenters\ProfileMenuPresenter::class,
|
||||
'metismenu' => App\CMS\MenuPresenters\MetismenuPresenter::class,
|
||||
'profile-menu' => App\CMS\MenuPresenters\ProfileMenuPresenter::class,
|
||||
'top-menu' => \App\CMS\MenuPresenters\TopMenuPresenter::class
|
||||
],
|
||||
'ordering' => true,
|
||||
|
||||
|
||||
+2
-1
@@ -4,7 +4,8 @@ return [
|
||||
'namespace' => 'Modules',
|
||||
'paths' => [
|
||||
'modules' => base_path('modules'),
|
||||
'templates' => base_path('templates'),
|
||||
'assets' => public_path('modules'),
|
||||
'stubs' => base_path('app/CMS/Modules/Stubs'),
|
||||
'stubs' => base_path('app/CMS/Modules/Stubs'),
|
||||
]
|
||||
];
|
||||
|
||||
@@ -28,10 +28,13 @@ $factory->define(User::class, function (Faker\Generator $faker) {
|
||||
});
|
||||
|
||||
$factory->define(Content::class, function (Faker\Generator $faker) {
|
||||
|
||||
$slug = isset($faker->slug) ? $faker->slug : $faker->slug(5);
|
||||
|
||||
return [
|
||||
'title' => $faker->sentence(5),
|
||||
'userId' => 1,
|
||||
'url' => $faker->slug(5),
|
||||
'url' => $slug,
|
||||
'pageContent' => $faker->text(1500),
|
||||
'metaDescription' => $faker->text,
|
||||
'moduleHandler' => config('staticpages.handler'),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Widgets\Widget;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
@@ -45,12 +46,16 @@ class AddUniqueCodeToWidgets extends Migration
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('widgets', function (Blueprint $table) {
|
||||
$table->dropUnique('widgets_language_container_id_unique');
|
||||
});
|
||||
try {
|
||||
Schema::table('widgets', function (Blueprint $table) {
|
||||
$table->dropUnique('widgets_language_container_id_unique');
|
||||
});
|
||||
} catch (\Exception $e) {
|
||||
|
||||
}
|
||||
|
||||
// migrate data
|
||||
$widgets = \App\Models\Widget::all();
|
||||
$widgets = Widget::all();
|
||||
foreach ($widgets as $widget) {
|
||||
$widget->settings = (object)json_decode($widget->settings);
|
||||
$widget->settings->containerClass = $widget->container_class;
|
||||
|
||||
@@ -27,7 +27,6 @@ class RenameColumnInContents extends Migration
|
||||
'url',
|
||||
'meta_keywords',
|
||||
'meta_description',
|
||||
'content',
|
||||
'module_settings',
|
||||
'status'
|
||||
];
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class DropCustomFieldsTables extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::dropIfExists('content_has_custom_fields');
|
||||
Schema::dropIfExists('user_has_custom_field');
|
||||
Schema::dropIfExists('custom_field_resource');
|
||||
Schema::dropIfExists('custom_fields');
|
||||
Schema::dropIfExists('custom_field_resources');
|
||||
Schema::dropIfExists('custom_field_types');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddOrderToContent extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('contents', function (Blueprint $table) {
|
||||
$table->integer('order')
|
||||
->after('viewed')
|
||||
->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('contents', function (Blueprint $table) {
|
||||
$table->dropColumn('order');
|
||||
});
|
||||
}
|
||||
}
|
||||
+27
-23
@@ -1,6 +1,19 @@
|
||||
var elixir = require ('laravel-elixir');
|
||||
|
||||
require ('laravel-elixir-remove')
|
||||
|
||||
elixir (function (mix) {
|
||||
var defaultColor = "green";
|
||||
var colors = ["blue", "seagreen", 'orange', "green", 'purple', 'red'];
|
||||
var outputName;
|
||||
colors.forEach (function (color) {
|
||||
outputName = color === defaultColor ? 'app.css' : 'app-' + color + ".css";
|
||||
mix.copy ('resources/assets/sass/admin/_themes/' + color + '-theme.scss', 'public/css/admin/theme.scss')
|
||||
.sass ([
|
||||
'admin/**/!(_imports|_variables|*-theme)*.scss',
|
||||
'../admin/css'
|
||||
], 'public/css/admin/' + outputName, null, {includePaths: ['bower_components', 'resources/assets/sass/admin']})
|
||||
})
|
||||
|
||||
// vendor files
|
||||
mix
|
||||
@@ -11,28 +24,19 @@ elixir (function (mix) {
|
||||
'responsive-bootstrap-toolkit/dist/bootstrap-toolkit.js',
|
||||
'metisMenu/dist/metisMenu.min.js',
|
||||
'tinycolor/dist/tinycolor-min.js',
|
||||
'chart.js/dist/Chart.bundle.min.js',
|
||||
'jquery.are-you-sure/jquery.are-you-sure.js',
|
||||
'../resources/assets/admin/js/app.js',
|
||||
], 'public/js/vendor.js', 'bower_components');
|
||||
|
||||
var defaultColor = "green";
|
||||
var colors = ["blue", "seagreen", 'orange', "green", 'purple', 'red'];
|
||||
var outputName;
|
||||
colors.forEach (function (color) {
|
||||
outputName = color == defaultColor ? 'app.css' : 'app-' + color + ".css";
|
||||
mix.copy ('resources/assets/sass/admin/_themes/' + color + '-theme.scss', 'public/css/admin/theme.scss');
|
||||
mix.sass ([
|
||||
'admin/**/!(_imports|_variables|*-theme)*.scss',
|
||||
'../admin/css/custom.css'
|
||||
|
||||
], 'public/css/admin/' + outputName, null, {includePaths: ['bower_components', 'resources/assets/sass/admin']});
|
||||
})
|
||||
|
||||
mix.copy ('bower_components/font-awesome/css/font-awesome.min.css', 'public/css');
|
||||
mix.copy ('bower_components/font-awesome/fonts', 'public/fonts');
|
||||
mix.sass (['bootstrap/bootstrap.scss'], 'public/css/admin/bootstrap.css');
|
||||
mix.styles ([
|
||||
'../../../public/css/admin/bootstrap.css',
|
||||
'../../../bower_components/animate.css/animate.css',
|
||||
], 'public/css/admin/style.css');
|
||||
'../node_modules/bootstrap-datepicker/dist/js/bootstrap-datepicker.js',
|
||||
'../resources/assets/admin/js/*.js',
|
||||
], 'public/js/vendor.js', 'bower_components')
|
||||
.copy ('bower_components/font-awesome/css/font-awesome.min.css', 'public/css')
|
||||
.copy ('node_modules/bootstrap-datepicker/dist/css/bootstrap-datepicker.css', 'public/css')
|
||||
.copy ('bower_components/font-awesome/fonts', 'public/fonts')
|
||||
.sass (['bootstrap/bootstrap.scss'], 'public/css/admin/bootstrap.css')
|
||||
.styles ([
|
||||
'../../../public/css/admin/bootstrap.css',
|
||||
'../../../public/css/bootstrap-datepicker.css',
|
||||
'../../../bower_components/animate.css/animate.css',
|
||||
], 'public/css/admin/style.css')
|
||||
.remove ('public/css/admin/theme.scss');
|
||||
});
|
||||
|
||||
@@ -16,5 +16,4 @@ if ($uri !== '/' && file_exists(__DIR__ . '/public' . $uri)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/app/CMS/Helpers/functions.php';
|
||||
require_once __DIR__ . '/public/index.php';
|
||||
|
||||
Generated
+5499
-6118
File diff suppressed because it is too large
Load Diff
+5
-6
@@ -5,15 +5,14 @@
|
||||
"dev": "gulp watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bootstrap-sass": "^3.3.7",
|
||||
"bower": "1.8.0",
|
||||
"gulp": "^3.9.1",
|
||||
"jquery": "^3.2.1",
|
||||
"laravel-elixir": "^6.0.0-11",
|
||||
"laravel-elixir-vue-2": "^0.2.0",
|
||||
"laravel-elixir-webpack-official": "^1.0.2",
|
||||
"lodash": "^4.16.2",
|
||||
"vue": "^2.4.2",
|
||||
"vue-resource": "^1.3.4"
|
||||
"lodash": "^4.16.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap-datepicker": "^1.7.1",
|
||||
"laravel-elixir-remove": "^0.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
stopOnFailure="false">
|
||||
<testsuites>
|
||||
<testsuite name="unit">
|
||||
<directory>./tests/unit/</directory>
|
||||
<directory>./tests/Unit/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
|
||||
@@ -14,3 +14,69 @@
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
||||
</IfModule>
|
||||
|
||||
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE application/javascript
|
||||
AddOutputFilterByType DEFLATE application/rss+xml
|
||||
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
|
||||
AddOutputFilterByType DEFLATE application/x-font
|
||||
AddOutputFilterByType DEFLATE application/x-font-opentype
|
||||
AddOutputFilterByType DEFLATE application/x-font-otf
|
||||
AddOutputFilterByType DEFLATE application/x-font-truetype
|
||||
AddOutputFilterByType DEFLATE application/x-font-ttf
|
||||
AddOutputFilterByType DEFLATE application/x-javascript
|
||||
AddOutputFilterByType DEFLATE application/xhtml+xml
|
||||
AddOutputFilterByType DEFLATE application/xml
|
||||
AddOutputFilterByType DEFLATE font/opentype
|
||||
AddOutputFilterByType DEFLATE font/otf
|
||||
AddOutputFilterByType DEFLATE font/ttf
|
||||
AddOutputFilterByType DEFLATE image/svg+xml
|
||||
AddOutputFilterByType DEFLATE image/x-icon
|
||||
AddOutputFilterByType DEFLATE text/css
|
||||
AddOutputFilterByType DEFLATE text/html
|
||||
AddOutputFilterByType DEFLATE text/javascript
|
||||
AddOutputFilterByType DEFLATE text/plain
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_gzip.c>
|
||||
mod_gzip_on Yes
|
||||
mod_gzip_dechunk Yes
|
||||
mod_gzip_item_include file .(html?|txt|css|js|php|pl)$
|
||||
mod_gzip_item_include handler ^cgi-script$
|
||||
mod_gzip_item_include mime ^text/.*
|
||||
mod_gzip_item_include mime ^application/x-javascript.*
|
||||
mod_gzip_item_exclude mime ^image/.*
|
||||
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
|
||||
</IfModule>
|
||||
#
|
||||
# Leverage Browser Caching
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresByType image/jpg "access 1 year"
|
||||
ExpiresByType image/jpeg "access 1 year"
|
||||
ExpiresByType image/gif "access 1 year"
|
||||
ExpiresByType image/png "access 1 year"
|
||||
ExpiresByType text/css "access 1 month"
|
||||
ExpiresByType text/html "access 1 month"
|
||||
ExpiresByType application/pdf "access 1 month"
|
||||
ExpiresByType text/x-javascript "access 1 month"
|
||||
ExpiresByType application/x-shockwave-flash "access 1 month"
|
||||
ExpiresByType image/x-icon "access 1 year"
|
||||
ExpiresDefault "access 1 month"
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_headers.c>
|
||||
<filesmatch "\.(ico|flv|jpg|jpeg|png|gif|css|swf)$">
|
||||
Header set Cache-Control "max-age=2678400, public"
|
||||
</filesmatch>
|
||||
<filesmatch "\.(html|htm)$">
|
||||
Header set Cache-Control "max-age=7200, private, must-revalidate"
|
||||
</filesmatch>
|
||||
<filesmatch "\.(pdf)$">
|
||||
Header set Cache-Control "max-age=86400, public"
|
||||
</filesmatch>
|
||||
<filesmatch "\.(js)$">
|
||||
Header set Cache-Control "max-age=2678400, private"
|
||||
</filesmatch>
|
||||
</IfModule>
|
||||
@@ -0,0 +1,46 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="multiselectcomponent.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div style="width: 50%; float: left;" id="left"></div>
|
||||
<div style="width: 50%; float: right;" id="right"></div>
|
||||
|
||||
<script type="application/javascript">
|
||||
// Sample data.
|
||||
var data1 = [
|
||||
{id: 1, name: 'Peter', surname: 'Hrdlica'},
|
||||
{id: 6, name: 'Vojtěch', surname: 'Hrdlica'},
|
||||
{id: 2, name: 'Michal', surname: 'Kváček'},
|
||||
{id: 19, name: 'Miček', surname: 'Je kokot!'},
|
||||
{id: 13, name: 'Mirek', surname: 'Mráček'},
|
||||
{id: 11, name: 'Naděžda', surname: 'Zímová'}
|
||||
];
|
||||
|
||||
var data2 = [
|
||||
{id: 2, name: 'wanna', surname: 'know'},
|
||||
{id: 6, name: 'Batman', surname: 'insulted'},
|
||||
{id: 58, name: 'scream', surname: 'pushed'},
|
||||
{id: 19, name: 'Mob', surname: 'why'},
|
||||
{id: 63, name: 'eight', surname: 'begin'},
|
||||
{id: 4, name: 'you', surname: 'emotions'}
|
||||
];
|
||||
data1.sort(function(a, b) { return a.id > b.id; }); // Sort by ID, lowest first.
|
||||
data2.sort(function(a, b) { return a.id > b.id; }); // Sort by ID, lowest first.
|
||||
|
||||
// Js component instance
|
||||
var mscL = new MultiSelectComponent('contacts', data1, 'id', function(item) { return item.name + ' ' + item.surname; });
|
||||
var mscR = new MultiSelectComponent('users', data2, 'id', function(item) { return item.name + ' ' + item.surname; });
|
||||
// Generated data added to template
|
||||
var left = document.getElementById('left');
|
||||
var right = document.getElementById('right');
|
||||
left.innerHTML = mscL.createTemplate();
|
||||
right.innerHTML = mscR.createTemplate();
|
||||
|
||||
// Initialization. May be done in more elegant way if the template is created ie. on server.
|
||||
mscL.init();
|
||||
mscR.init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,121 @@
|
||||
|
||||
/**
|
||||
* Multi-select component
|
||||
*
|
||||
* @param identifier Identifier parameter that makes each instance unique and is used in DOM datasets used by this component.
|
||||
* @param data Data (array of objects)
|
||||
* @param field Field parameter that will be send as list with form
|
||||
* @param itemString String that should be shown as selector in select and a item label in item list
|
||||
*/
|
||||
function MultiSelectComponent(identifier, data, field, itemString) {
|
||||
|
||||
// Private variables
|
||||
var selectBox, addButton, itemHolder;
|
||||
|
||||
/**
|
||||
* This is the method that generates template for multiselect component.
|
||||
* This method is easily rewriteable into PHP.
|
||||
* It is necessary to keep html datasets with prefix data-ms- intact for the javascript component to work.
|
||||
* ECMAScript 5 version, does not need any compilers, nor jQuery or other framework.
|
||||
* This method is not required to stay inside the MultiSelectComponent and may be rendered in any way;
|
||||
*
|
||||
* identifier is inherited from the instance creation.
|
||||
* data is inherited from the instance creation.
|
||||
* field is inherited from the instance creation.
|
||||
* itemString is inherited from the instance creation.
|
||||
*
|
||||
*/
|
||||
function createTemplate () {
|
||||
var html = '<select data-ms-select="'+identifier+'">';
|
||||
|
||||
for(var idx in data) {
|
||||
if(data.hasOwnProperty(idx)) {
|
||||
html += '<option value="'+data[idx][field]+'">'+itemString(data[idx])+'</value>' //createSelectOption(data[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
return html += '</select><input type="button" data-ms-add="'+identifier+'" value="add"><div data-ms-list="'+identifier+'"></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method creates an item list item that works as visual representation of selected values.
|
||||
*/
|
||||
function createItemListItem (value) {
|
||||
return html = '<span>'+itemString(getDataItem(value))+
|
||||
' <span data-ms-remove="'+identifier+'" data-ms-value="'+value+'">x</span>'+
|
||||
'<input style="display: none" type="checkbox" value="'+value+'" name="'+identifier+'[]" checked>'+
|
||||
'</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a requested data item that matches <field> (class instance variable field) value of passed value.
|
||||
*/
|
||||
function getDataItem (value) {
|
||||
for(var idx in data) {
|
||||
if(data.hasOwnProperty(idx)){
|
||||
if(data[idx][field] == value){
|
||||
return data[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* By selecting an item, that item is removed from list of available items and is added to list of selected items.
|
||||
* If no more items are available for adding, add button and selectbox are disabled and list gets an informative item.
|
||||
*/
|
||||
function selectItem (value) {
|
||||
selectBox.removeChild(selectBox.querySelector('option[value="' + value + '"]'));
|
||||
itemHolder.innerHTML += createItemListItem(value);
|
||||
|
||||
if(selectBox.children.length === 0) {
|
||||
selectBox.options.add(new Option('no more items to add', 0));
|
||||
addButton.setAttribute('disabled', 'disabled');
|
||||
selectBox.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* By deselecting (removing) an item, that item is removed from selected list and added again into select box of available items;
|
||||
* If add button was disabled (mostly because of no selectable items in list) it and the selectbox are now re-enabled and informative item is removed.
|
||||
*/
|
||||
function deselectItem (element) {
|
||||
var id = element.getAttribute('data-ms-value');
|
||||
selectBox.options.add(new Option(itemString(getDataItem(id)), id));
|
||||
itemHolder.removeChild(element.parentNode);
|
||||
|
||||
if(addButton.getAttribute('disabled')) {
|
||||
selectBox.options.remove(0);
|
||||
addButton.removeAttribute('disabled');
|
||||
selectBox.removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes javascript part of the component.
|
||||
*/
|
||||
function init () {
|
||||
// Cached elements only for JS to be a little more optimized.
|
||||
selectBox = document.querySelector('[data-ms-select="'+identifier+'"]');
|
||||
addButton = document.querySelector('[data-ms-add="'+identifier+'"]');
|
||||
itemHolder = document.querySelector('[data-ms-list="'+identifier+'"]');
|
||||
|
||||
// Add button
|
||||
addButton.addEventListener('click', function() {
|
||||
selectItem(selectBox.value);
|
||||
});
|
||||
|
||||
// Remove item button
|
||||
itemHolder.addEventListener('click', function(e) {
|
||||
if(e.target.getAttribute('data-ms-remove')) {
|
||||
deselectItem(e.target);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Publicly exposed static methods and variables.
|
||||
return {
|
||||
init: init,
|
||||
createTemplate: createTemplate
|
||||
};
|
||||
}
|
||||
Regular → Executable
+52
-30
@@ -18,6 +18,11 @@ a:focus {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.collapse.in {
|
||||
margin-bottom: 20px !important;
|
||||
border-bottom: solid 2px !important;
|
||||
}
|
||||
|
||||
.nav.nav-tabs-bordered + .tab-content {
|
||||
padding-bottom: 20px !important;
|
||||
}
|
||||
@@ -324,7 +329,7 @@ img {
|
||||
|
||||
.card.file-preview .card-block {
|
||||
padding: 0;
|
||||
height: 6em;
|
||||
height: 9em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -341,78 +346,95 @@ img {
|
||||
}
|
||||
|
||||
/*.tag {*/
|
||||
/*display: inline-block;*/
|
||||
/*padding: 0.25em 0.4em;*/
|
||||
/*font-size: 75%;*/
|
||||
/*font-weight: bold;*/
|
||||
/*line-height: 1;*/
|
||||
/*color: #FFF;*/
|
||||
/*text-align: center;*/
|
||||
/*white-space: nowrap;*/
|
||||
/*vertical-align: baseline;*/
|
||||
/*border-radius: 0.25rem;*/
|
||||
/*display: inline-block;*/
|
||||
/*padding: 0.25em 0.4em;*/
|
||||
/*font-size: 75%;*/
|
||||
/*font-weight: bold;*/
|
||||
/*line-height: 1;*/
|
||||
/*color: #FFF;*/
|
||||
/*text-align: center;*/
|
||||
/*white-space: nowrap;*/
|
||||
/*vertical-align: baseline;*/
|
||||
/*border-radius: 0.25rem;*/
|
||||
/*}*/
|
||||
|
||||
/*.tag:empty {*/
|
||||
/*display: none;*/
|
||||
/*display: none;*/
|
||||
/*}*/
|
||||
|
||||
/*.btn .tag {*/
|
||||
/*position: relative;*/
|
||||
/*top: -1px;*/
|
||||
/*position: relative;*/
|
||||
/*top: -1px;*/
|
||||
/*}*/
|
||||
|
||||
/*a.tag:focus, a.tag:hover {*/
|
||||
/*color: #FFF;*/
|
||||
/*text-decoration: none;*/
|
||||
/*cursor: pointer;*/
|
||||
/*color: #FFF;*/
|
||||
/*text-decoration: none;*/
|
||||
/*cursor: pointer;*/
|
||||
/*}*/
|
||||
|
||||
/*.tag-pill {*/
|
||||
/*padding-right: 0.6em;*/
|
||||
/*padding-left: 0.6em;*/
|
||||
/*border-radius: 10rem;*/
|
||||
/*padding-right: 0.6em;*/
|
||||
/*padding-left: 0.6em;*/
|
||||
/*border-radius: 10rem;*/
|
||||
/*}*/
|
||||
|
||||
/*.tag-default {*/
|
||||
/*background-color: #818A91;*/
|
||||
/*background-color: #818A91;*/
|
||||
/*}*/
|
||||
|
||||
/*.tag-default[href]:focus, .tag-default[href]:hover {*/
|
||||
/*background-color: #687077;*/
|
||||
/*background-color: #687077;*/
|
||||
/*}*/
|
||||
|
||||
/*.tag-success {*/
|
||||
/*background-color: #5CB85C;*/
|
||||
/*background-color: #5CB85C;*/
|
||||
/*}*/
|
||||
|
||||
/*.tag-success[href]:focus, .tag-success[href]:hover {*/
|
||||
/*background-color: #449D44;*/
|
||||
/*background-color: #449D44;*/
|
||||
/*}*/
|
||||
|
||||
/*.tag-info {*/
|
||||
/*background-color: #5BC0DE;*/
|
||||
/*background-color: #5BC0DE;*/
|
||||
/*}*/
|
||||
|
||||
/*.tag-info[href]:focus, .tag-info[href]:hover {*/
|
||||
/*background-color: #31B0D5;*/
|
||||
/*background-color: #31B0D5;*/
|
||||
/*}*/
|
||||
|
||||
/*.tag-warning {*/
|
||||
/*background-color: #F0AD4E;*/
|
||||
/*background-color: #F0AD4E;*/
|
||||
/*}*/
|
||||
|
||||
/*.tag-warning[href]:focus, .tag-warning[href]:hover {*/
|
||||
/*background-color: #EC971F;*/
|
||||
/*background-color: #EC971F;*/
|
||||
/*}*/
|
||||
|
||||
/*.tag-danger {*/
|
||||
/*background-color: #D9534F;*/
|
||||
/*background-color: #D9534F;*/
|
||||
/*}*/
|
||||
|
||||
/*.tag-danger[href]:focus, .tag-danger[href]:hover {*/
|
||||
/*background-color: #C9302C;*/
|
||||
/*background-color: #C9302C;*/
|
||||
/*}*/
|
||||
|
||||
.has-subpages:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#orderSetup .subpage td:first-child {
|
||||
padding-left: 35px;
|
||||
}
|
||||
|
||||
.nav-profile .dropdown-menu > li {
|
||||
width: 100%;
|
||||
padding: 3px 1.5rem;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
color: #373a3c;
|
||||
text-align: inherit;
|
||||
white-space: nowrap;
|
||||
background: none;
|
||||
border: 0;
|
||||
}
|
||||
@@ -1,429 +1,9 @@
|
||||
var config = window.config = {};
|
||||
|
||||
// Config reference element
|
||||
var $ref = $ ("#ref");
|
||||
|
||||
// Configure responsive bootstrap toolkit
|
||||
config.ResponsiveBootstrapToolkitVisibilityDivs = {
|
||||
'xs': $ ('<div class="device-xs hidden-sm-up"></div>'),
|
||||
'sm': $ ('<div class="device-sm hidden-xs-down hidden-md-up"></div>'),
|
||||
'md': $ ('<div class="device-md hidden-sm-down hidden-lg-up"></div>'),
|
||||
'lg': $ ('<div class="device-lg hidden-md-down hidden-xl-up"></div>'),
|
||||
'xl': $ ('<div class="device-xl hidden-lg-down"></div>')
|
||||
};
|
||||
|
||||
ResponsiveBootstrapToolkit.use ('Custom', config.ResponsiveBootstrapToolkitVisibilityDivs);
|
||||
|
||||
//validation configuration
|
||||
config.validations = {
|
||||
debug: true,
|
||||
errorClass: 'has-error',
|
||||
validClass: 'success',
|
||||
errorElement: "span",
|
||||
|
||||
// add error class
|
||||
highlight: function (element, errorClass, validClass) {
|
||||
$ (element).parents ("div.form-group")
|
||||
.addClass (errorClass)
|
||||
.removeClass (validClass);
|
||||
},
|
||||
|
||||
// add error class
|
||||
unhighlight: function (element, errorClass, validClass) {
|
||||
$ (element).parents (".has-error")
|
||||
.removeClass (errorClass)
|
||||
.addClass (validClass);
|
||||
},
|
||||
|
||||
// submit handler
|
||||
submitHandler: function (form) {
|
||||
form.submit ();
|
||||
}
|
||||
};
|
||||
|
||||
//delay time configuration
|
||||
config.delayTime = 50;
|
||||
|
||||
// chart configurations
|
||||
config.chart = {};
|
||||
|
||||
/***********************************************
|
||||
* Animation Settings
|
||||
***********************************************/
|
||||
function animate(options) {
|
||||
var animationName = "animated " + options.name;
|
||||
var animationEnd = "webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend";
|
||||
$ (options.selector)
|
||||
.addClass (animationName)
|
||||
.one (animationEnd,
|
||||
function () {
|
||||
$ (this).removeClass (animationName);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$ (function () {
|
||||
var $itemActions = $ (".item-actions-dropdown");
|
||||
|
||||
$ (document).on ('click', function (e) {
|
||||
if (!$ (e.target).closest ('.item-actions-dropdown').length) {
|
||||
$itemActions.removeClass ('active');
|
||||
}
|
||||
});
|
||||
|
||||
$ ('.item-actions-toggle-btn').on ('click', function (e) {
|
||||
e.preventDefault ();
|
||||
|
||||
var $thisActionList = $ (this).closest ('.item-actions-dropdown');
|
||||
|
||||
$itemActions.not ($thisActionList).removeClass ('active');
|
||||
|
||||
$thisActionList.toggleClass ('active');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function setSameHeights($container) {
|
||||
|
||||
$container = $container || $ ('.sameheight-container');
|
||||
|
||||
var viewport = ResponsiveBootstrapToolkit.current ();
|
||||
|
||||
$container.each (function () {
|
||||
|
||||
var $items = $ (this).find (".sameheight-item");
|
||||
|
||||
// Get max height of items in container
|
||||
var maxHeight = 0;
|
||||
|
||||
$items.each (function () {
|
||||
$ (this).css ({height: 'auto'});
|
||||
maxHeight = Math.max (maxHeight, $ (this).innerHeight ());
|
||||
});
|
||||
|
||||
// Set heights of items
|
||||
$items.each (function () {
|
||||
// Ignored viewports for item
|
||||
var excludedStr = $ (this).data ('exclude') || '';
|
||||
var excluded = excludedStr.split (',');
|
||||
|
||||
// Set height of element if it's not excluded on
|
||||
if (excluded.indexOf (viewport) === -1) {
|
||||
$ (this).innerHeight (maxHeight);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$ (function () {
|
||||
animate ({
|
||||
name: 'flipInY',
|
||||
selector: '.error-card > .error-title-block'
|
||||
});
|
||||
|
||||
setTimeout (function () {
|
||||
var $el = $ ('.error-card > .error-container');
|
||||
|
||||
animate ({
|
||||
name: 'fadeInUp',
|
||||
selector: $el
|
||||
});
|
||||
|
||||
$el.addClass ('visible');
|
||||
}, 500);
|
||||
});
|
||||
|
||||
$ (function () {
|
||||
|
||||
$ ('#sidebar-menu, #customize-menu').metisMenu ({
|
||||
activeClass: 'open'
|
||||
});
|
||||
|
||||
$ ('#sidebar-collapse-btn').on ('click', function (event) {
|
||||
event.preventDefault ();
|
||||
|
||||
$ ("#app").toggleClass ("sidebar-open");
|
||||
});
|
||||
|
||||
$ ("#sidebar-overlay").on ('click', function () {
|
||||
$ ("#app").removeClass ("sidebar-open");
|
||||
});
|
||||
|
||||
});
|
||||
$ (function () {
|
||||
|
||||
$ ('.actions-list > li').on ('click', '.check', function (e) {
|
||||
e.preventDefault ();
|
||||
|
||||
$ (this).parents ('.tasks-item')
|
||||
.find ('.checkbox')
|
||||
.prop ("checked", true);
|
||||
|
||||
// removeActionList ();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$ (function () {
|
||||
$ ('.nav-profile > li > a').on ('click', function () {
|
||||
var $el = $ (this).next ();
|
||||
|
||||
animate ({
|
||||
name: 'flipInX',
|
||||
selector: $el
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$ (function () {
|
||||
// Local storage settings
|
||||
var themeSettings = getThemeSettings ();
|
||||
|
||||
// Elements
|
||||
|
||||
var $app = $ ('#app');
|
||||
var $styleLink = $ ('#theme-style');
|
||||
var $customizeMenu = $ ('#customize-menu');
|
||||
|
||||
// Color switcher
|
||||
var $customizeMenuColorBtns = $customizeMenu.find ('.color-item');
|
||||
|
||||
// Position switchers
|
||||
var $customizeMenuRadioBtns = $customizeMenu.find ('.radio');
|
||||
|
||||
/************************************************
|
||||
* Initial State
|
||||
*************************************************/
|
||||
|
||||
setThemeSettings ();
|
||||
|
||||
setSameHeights();
|
||||
|
||||
/************************************************
|
||||
* Events
|
||||
*************************************************/
|
||||
|
||||
// set theme type
|
||||
$customizeMenuColorBtns.on ('click', function () {
|
||||
themeSettings.themeName = $ (this).data ('theme');
|
||||
|
||||
setThemeSettings ();
|
||||
});
|
||||
|
||||
$customizeMenuRadioBtns.on ('click', function () {
|
||||
|
||||
var optionName = $ (this).prop ('name');
|
||||
themeSettings[optionName] = $ (this).val ();
|
||||
|
||||
setThemeSettings ();
|
||||
});
|
||||
|
||||
function setThemeSettings() {
|
||||
setThemeState ()
|
||||
.delay (config.delayTime)
|
||||
.queue (function (next) {
|
||||
|
||||
setThemeColor ();
|
||||
setThemeControlsState ();
|
||||
saveThemeSettings ();
|
||||
|
||||
$ (document).trigger ("themechange");
|
||||
|
||||
next ();
|
||||
});
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Update theme based on options
|
||||
*************************************************/
|
||||
|
||||
function setThemeState() {
|
||||
// set theme type
|
||||
if (themeSettings.themeName) {
|
||||
$styleLink.attr ('href', window.cssPath + '/app-' + themeSettings.themeName + '.css');
|
||||
}
|
||||
else {
|
||||
$styleLink.attr ('href', window.cssPath + '/app.css');
|
||||
}
|
||||
// App classes
|
||||
$app.removeClass ('header-fixed footer-fixed sidebar-fixed');
|
||||
|
||||
// set header
|
||||
$app.addClass (themeSettings.headerPosition);
|
||||
|
||||
// set footer
|
||||
$app.addClass (themeSettings.footerPosition);
|
||||
|
||||
// set footer
|
||||
$app.addClass (themeSettings.sidebarPosition);
|
||||
|
||||
return $app;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Update theme controls based on options
|
||||
*************************************************/
|
||||
|
||||
function setThemeControlsState() {
|
||||
// set color switcher
|
||||
$customizeMenuColorBtns.each (function () {
|
||||
if ($ (this).data ('theme') === themeSettings.themeName) {
|
||||
$ (this).addClass ('active');
|
||||
}
|
||||
else {
|
||||
$ (this).removeClass ('active');
|
||||
}
|
||||
});
|
||||
|
||||
// set radio buttons
|
||||
$customizeMenuRadioBtns.each (function () {
|
||||
var name = $ (this).prop ('name');
|
||||
var value = $ (this).val ();
|
||||
|
||||
if (themeSettings[name] === value) {
|
||||
$ (this).prop ("checked", true);
|
||||
}
|
||||
else {
|
||||
$ (this).prop ("checked", false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Update theme color
|
||||
*************************************************/
|
||||
function setThemeColor() {
|
||||
config.chart.colorPrimary = tinycolor ($ref.find (".chart .color-primary").css ("color"));
|
||||
config.chart.colorSecondary = tinycolor ($ref.find (".chart .color-secondary").css ("color"));
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Storage Functions
|
||||
*************************************************/
|
||||
|
||||
function getThemeSettings() {
|
||||
var settings = (localStorage.getItem ('themeSettings')) ? JSON.parse (localStorage.getItem ('themeSettings')) : {};
|
||||
|
||||
settings.headerPosition = settings.headerPosition || '';
|
||||
settings.sidebarPosition = settings.sidebarPosition || '';
|
||||
settings.footerPosition = settings.footerPosition || '';
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
function saveThemeSettings() {
|
||||
localStorage.setItem ('themeSettings', JSON.stringify (themeSettings));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Makes slug from input string
|
||||
*
|
||||
* @param text
|
||||
* @param trimSpaces
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
function slugify(text, trimSpaces) {
|
||||
text = text.toString ().toLowerCase ();
|
||||
|
||||
if (trimSpaces)
|
||||
text = text.trim ();
|
||||
|
||||
return text.replace (/ /g, '-')
|
||||
.replace (/[àáâãäå]/g, "a")
|
||||
.replace (/[čç]/g, "c")
|
||||
.replace (/[ěèéêë]/g, "e")
|
||||
.replace (/[ùúûüů]/g, "u")
|
||||
.replace (/[ìíîï]/g, "i")
|
||||
.replace (/[ňńñ]/g, "n")
|
||||
.replace (/[òóôõö]/g, "o")
|
||||
.replace (/[ýÿ]/g, "y")
|
||||
.replace (/œ/g, "oe")
|
||||
.replace (/ď/g, "d")
|
||||
.replace (/ř/g, "r")
|
||||
.replace (/š/g, "s")
|
||||
.replace (/ž/g, "z")
|
||||
.replace (/ť/g, "t")
|
||||
.replace (/æ/g, "ae")
|
||||
.replace (/[^a-z0-9\-]+/g, '')
|
||||
.replace (/--+/g, '-');
|
||||
}
|
||||
|
||||
// bind some custom methods
|
||||
$ (document).ready (function () {
|
||||
|
||||
$ ("body").addClass ("loaded");
|
||||
|
||||
var notification = $ ('.notification');
|
||||
if (notification.length) {
|
||||
|
||||
notification.click (function () {
|
||||
$ (this).fadeOut ('fast');
|
||||
});
|
||||
|
||||
setTimeout (function () {
|
||||
notification.fadeOut (1500)
|
||||
}, 5 * 1000)
|
||||
}
|
||||
|
||||
var previousTitle, previousMessage;
|
||||
|
||||
$ ('.confirm').click (function (e) {
|
||||
|
||||
var titleElement = $ ('#confirmation-title');
|
||||
var messageElement = $ ('#confirmation-message');
|
||||
|
||||
previousTitle = titleElement.text ();
|
||||
previousMessage = messageElement.text ();
|
||||
|
||||
var confirmTitle = $ (this).data ('title');
|
||||
var confirmMessage = $ (this).data ('message');
|
||||
var href = $ (this).attr ('href');
|
||||
|
||||
$ ('#confirm-button').attr ('href', href);
|
||||
|
||||
if (confirmTitle)
|
||||
titleElement.text (confirmTitle);
|
||||
|
||||
if (confirmMessage)
|
||||
messageElement.text (confirmMessage);
|
||||
|
||||
$ ('#confirm-modal').modal ('show');
|
||||
|
||||
$('.main-wrapper').addClass('blur');
|
||||
|
||||
e.preventDefault ();
|
||||
});
|
||||
|
||||
$ ('#confirm-modal').on ('hidden.bs.modal', function () {
|
||||
$ ('#confirmation-title').text (previousTitle);
|
||||
$ ('#confirmation-message').text (previousMessage);
|
||||
$('.main-wrapper').removeClass('blur');
|
||||
});
|
||||
|
||||
$ ('#lockUrl').change (function () {
|
||||
$ ('.slug-source').keyup ();
|
||||
});
|
||||
|
||||
$ ('.slug-target').keyup (function () {
|
||||
$ (this).val (slugify ($ (this).val (), false));
|
||||
$ ('#lockUrl').prop ('checked', true);
|
||||
});
|
||||
|
||||
$ ('.slug-source').keyup (function () {
|
||||
var source = $ (this);
|
||||
var target = $ ('.slug-target');
|
||||
var locked = $ ('#lockUrl');
|
||||
|
||||
if (!locked.is (':checked'))
|
||||
target.val (slugify (source.val (), true));
|
||||
});
|
||||
|
||||
$ (function () {
|
||||
$ ('[data-toggle="tooltip"]').tooltip ()
|
||||
});
|
||||
$ ('[data-toggle="tooltip"]').tooltip ()
|
||||
|
||||
// find if any error occured and mark tab with that error
|
||||
$ ('.has-error').each (function () {
|
||||
@@ -433,24 +13,35 @@ $ (document).ready (function () {
|
||||
$ ("#" + tabCard.prop ('id') + "-tab").addClass ('tab-with-error');
|
||||
});
|
||||
|
||||
$('#removeTitlePhoto').click(function () {
|
||||
$('#titlePhotoContainer').remove();
|
||||
// $('existingTitlePhoto').val(null);
|
||||
$ ('#removeTitlePhoto').click (function () {
|
||||
$ ('#titlePhotoContainer').remove ();
|
||||
});
|
||||
|
||||
$('form').areYouSure();
|
||||
$ ('form').areYouSure ();
|
||||
});
|
||||
|
||||
$('.checkbox').change(function() {
|
||||
var e = $(this);
|
||||
var checked = e.is(':checked');
|
||||
var id = e.data('id');
|
||||
$ ('.date').datepicker ({
|
||||
format: 'dd. mm. yyyy'
|
||||
});
|
||||
|
||||
$('.child-'+id).prop('checked', checked);
|
||||
$ ('.checkbox').change (function () {
|
||||
var e = $ (this);
|
||||
var checked = e.is (':checked');
|
||||
var id = e.data ('id');
|
||||
|
||||
$ ('.child-' + id).prop ('checked', checked);
|
||||
|
||||
// check also parent
|
||||
if (e.hasClass('child-checkbox') && checked){
|
||||
var parent = e.data('parent');
|
||||
$('#checkbox-parent-'+parent).prop('checked', true);
|
||||
if (e.hasClass ('child-checkbox') && checked) {
|
||||
var parent = e.data ('parent');
|
||||
$ ('#checkbox-parent-' + parent).prop ('checked', true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$ ('.has-subpages').click (function (e) {
|
||||
if (!$ (e.target).is ("div"))
|
||||
return;
|
||||
|
||||
var id = $ (this).data ('id');
|
||||
$ ('.parent-page-' + id).toggleClass ('hidden-xs-up');
|
||||
})
|
||||
@@ -0,0 +1,43 @@
|
||||
var previousTitle, previousMessage;
|
||||
|
||||
$ ('.confirm').click (function (e) {
|
||||
|
||||
var titleElement = $ ('#confirmation-title');
|
||||
var messageElement = $ ('#confirmation-message');
|
||||
|
||||
previousTitle = titleElement.text ();
|
||||
previousMessage = messageElement.text ();
|
||||
|
||||
var confirmTitle = $ (this).data ('title');
|
||||
var confirmMessage = $ (this).data ('message');
|
||||
|
||||
var tag = $ (this).prop ('tagName');
|
||||
if (tag === 'A') {
|
||||
var href = $ (this).attr ('href');
|
||||
|
||||
$ ('#confirm-button').attr ('href', href);
|
||||
} else if (tag === 'BUTTON') {
|
||||
var form = $ (this).closest ('form');
|
||||
$ ('#confirm-button').click (function () {
|
||||
form.submit ();
|
||||
})
|
||||
}
|
||||
|
||||
if (confirmTitle)
|
||||
titleElement.text (confirmTitle);
|
||||
|
||||
if (confirmMessage)
|
||||
messageElement.text (confirmMessage);
|
||||
|
||||
$ ('#confirm-modal').modal ('show');
|
||||
|
||||
$ ('.main-wrapper').addClass ('blur');
|
||||
|
||||
e.preventDefault ();
|
||||
});
|
||||
|
||||
$ ('#confirm-modal').on ('hidden.bs.modal', function () {
|
||||
$ ('#confirmation-title').text (previousTitle);
|
||||
$ ('#confirmation-message').text (previousMessage);
|
||||
$ ('.main-wrapper').removeClass ('blur');
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
var notification = $ ('.notification');
|
||||
if (notification.length) {
|
||||
|
||||
notification.click (function () {
|
||||
$ (this).fadeOut ('fast');
|
||||
});
|
||||
|
||||
setTimeout (function () {
|
||||
notification.fadeOut (1500)
|
||||
}, 5 * 1000)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Makes slug from input string
|
||||
*
|
||||
* @param text
|
||||
* @param trimSpaces
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
function slugify(text, trimSpaces) {
|
||||
text = text.toString ().toLowerCase ();
|
||||
|
||||
if (trimSpaces)
|
||||
text = text.trim ();
|
||||
|
||||
return text.replace (/ /g, '-')
|
||||
.replace (/[àáâãäå]/g, "a")
|
||||
.replace (/[čç]/g, "c")
|
||||
.replace (/[ěèéêë]/g, "e")
|
||||
.replace (/[ùúûüů]/g, "u")
|
||||
.replace (/[ìíîï]/g, "i")
|
||||
.replace (/[ňńñ]/g, "n")
|
||||
.replace (/[òóôõö]/g, "o")
|
||||
.replace (/[ýÿ]/g, "y")
|
||||
.replace (/œ/g, "oe")
|
||||
.replace (/ď/g, "d")
|
||||
.replace (/ř/g, "r")
|
||||
.replace (/š/g, "s")
|
||||
.replace (/ž/g, "z")
|
||||
.replace (/ť/g, "t")
|
||||
.replace (/æ/g, "ae")
|
||||
.replace (/[^a-z0-9\-]+/g, '')
|
||||
.replace (/--+/g, '-');
|
||||
}
|
||||
|
||||
$ ('#lockUrl').change (function () {
|
||||
$ ('.slug-source').keyup ();
|
||||
});
|
||||
|
||||
$ ('.slug-target').keyup (function () {
|
||||
$ (this).val (slugify ($ (this).val (), false));
|
||||
$ ('#lockUrl').prop ('checked', true);
|
||||
});
|
||||
|
||||
$ ('.slug-source').keyup (function () {
|
||||
var source = $ (this);
|
||||
var target = $ ('.slug-target');
|
||||
var locked = $ ('#lockUrl');
|
||||
|
||||
if (!locked.is (':checked'))
|
||||
target.val (slugify (source.val (), true));
|
||||
});
|
||||
@@ -0,0 +1,316 @@
|
||||
var config = window.config = {};
|
||||
|
||||
// Config reference element
|
||||
var $ref = $ ("#ref");
|
||||
|
||||
// Configure responsive bootstrap toolkit
|
||||
config.ResponsiveBootstrapToolkitVisibilityDivs = {
|
||||
'xs': $ ('<div class="device-xs hidden-sm-up"></div>'),
|
||||
'sm': $ ('<div class="device-sm hidden-xs-down hidden-md-up"></div>'),
|
||||
'md': $ ('<div class="device-md hidden-sm-down hidden-lg-up"></div>'),
|
||||
'lg': $ ('<div class="device-lg hidden-md-down hidden-xl-up"></div>'),
|
||||
'xl': $ ('<div class="device-xl hidden-lg-down"></div>')
|
||||
};
|
||||
|
||||
ResponsiveBootstrapToolkit.use ('Custom', config.ResponsiveBootstrapToolkitVisibilityDivs);
|
||||
|
||||
//validation configuration
|
||||
config.validations = {
|
||||
debug: true,
|
||||
errorClass: 'has-error',
|
||||
validClass: 'success',
|
||||
errorElement: "span",
|
||||
|
||||
// add error class
|
||||
highlight: function (element, errorClass, validClass) {
|
||||
$ (element).parents ("div.form-group")
|
||||
.addClass (errorClass)
|
||||
.removeClass (validClass);
|
||||
},
|
||||
|
||||
// add error class
|
||||
unhighlight: function (element, errorClass, validClass) {
|
||||
$ (element).parents (".has-error")
|
||||
.removeClass (errorClass)
|
||||
.addClass (validClass);
|
||||
},
|
||||
|
||||
// submit handler
|
||||
submitHandler: function (form) {
|
||||
form.submit ();
|
||||
}
|
||||
};
|
||||
|
||||
//delay time configuration
|
||||
config.delayTime = 50;
|
||||
|
||||
// chart configurations
|
||||
config.chart = {};
|
||||
|
||||
/***********************************************
|
||||
* Animation Settings
|
||||
***********************************************/
|
||||
function animate(options) {
|
||||
var animationName = "animated " + options.name;
|
||||
var animationEnd = "webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend";
|
||||
$ (options.selector)
|
||||
.addClass (animationName)
|
||||
.one (animationEnd,
|
||||
function () {
|
||||
$ (this).removeClass (animationName);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$ (function () {
|
||||
var $itemActions = $ (".item-actions-dropdown");
|
||||
|
||||
$ (document).on ('click', function (e) {
|
||||
if (!$ (e.target).closest ('.item-actions-dropdown').length) {
|
||||
$itemActions.removeClass ('active');
|
||||
}
|
||||
});
|
||||
|
||||
$ ('.item-actions-toggle-btn').on ('click', function (e) {
|
||||
e.preventDefault ();
|
||||
|
||||
var $thisActionList = $ (this).closest ('.item-actions-dropdown');
|
||||
|
||||
$itemActions.not ($thisActionList).removeClass ('active');
|
||||
|
||||
$thisActionList.toggleClass ('active');
|
||||
});
|
||||
});
|
||||
|
||||
function setSameHeights($container) {
|
||||
|
||||
$container = $container || $ ('.sameheight-container');
|
||||
|
||||
var viewport = ResponsiveBootstrapToolkit.current ();
|
||||
|
||||
$container.each (function () {
|
||||
|
||||
var $items = $ (this).find (".sameheight-item");
|
||||
|
||||
// Get max height of items in container
|
||||
var maxHeight = 0;
|
||||
|
||||
$items.each (function () {
|
||||
$ (this).css ({height: 'auto'});
|
||||
maxHeight = Math.max (maxHeight, $ (this).innerHeight ());
|
||||
});
|
||||
|
||||
// Set heights of items
|
||||
$items.each (function () {
|
||||
// Ignored viewports for item
|
||||
var excludedStr = $ (this).data ('exclude') || '';
|
||||
var excluded = excludedStr.split (',');
|
||||
|
||||
// Set height of element if it's not excluded on
|
||||
if (excluded.indexOf (viewport) === -1) {
|
||||
$ (this).innerHeight (maxHeight);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$ (function () {
|
||||
animate ({
|
||||
name: 'flipInY',
|
||||
selector: '.error-card > .error-title-block'
|
||||
});
|
||||
|
||||
setTimeout (function () {
|
||||
var $el = $ ('.error-card > .error-container');
|
||||
|
||||
animate ({
|
||||
name: 'fadeInUp',
|
||||
selector: $el
|
||||
});
|
||||
|
||||
$el.addClass ('visible');
|
||||
}, 500);
|
||||
});
|
||||
|
||||
$ (function () {
|
||||
|
||||
$ ('#sidebar-menu, #customize-menu').metisMenu ({
|
||||
activeClass: 'open'
|
||||
});
|
||||
|
||||
$ ('#sidebar-collapse-btn').on ('click', function (event) {
|
||||
event.preventDefault ();
|
||||
|
||||
$ ("#app").toggleClass ("sidebar-open");
|
||||
});
|
||||
|
||||
$ ("#sidebar-overlay").on ('click', function () {
|
||||
$ ("#app").removeClass ("sidebar-open");
|
||||
});
|
||||
|
||||
});
|
||||
$ (function () {
|
||||
|
||||
$ ('.actions-list > li').on ('click', '.check', function (e) {
|
||||
e.preventDefault ();
|
||||
|
||||
$ (this).parents ('.tasks-item')
|
||||
.find ('.checkbox')
|
||||
.prop ("checked", true);
|
||||
|
||||
// removeActionList ();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$ (function () {
|
||||
$ ('.nav-profile > li > a').on ('click', function () {
|
||||
var $el = $ (this).next ();
|
||||
|
||||
animate ({
|
||||
name: 'flipInX',
|
||||
selector: $el
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$ (function () {
|
||||
// Local storage settings
|
||||
var themeSettings = getThemeSettings ();
|
||||
|
||||
// Elements
|
||||
|
||||
var $app = $ ('#app');
|
||||
var $styleLink = $ ('#theme-style');
|
||||
var $customizeMenu = $ ('#customize-menu');
|
||||
|
||||
// Color switcher
|
||||
var $customizeMenuColorBtns = $customizeMenu.find ('.color-item');
|
||||
|
||||
// Position switchers
|
||||
var $customizeMenuRadioBtns = $customizeMenu.find ('.radio');
|
||||
|
||||
/************************************************
|
||||
* Initial State
|
||||
*************************************************/
|
||||
|
||||
setThemeSettings ();
|
||||
|
||||
setSameHeights ();
|
||||
|
||||
/************************************************
|
||||
* Events
|
||||
*************************************************/
|
||||
|
||||
// set theme type
|
||||
$customizeMenuColorBtns.on ('click', function () {
|
||||
themeSettings.themeName = $ (this).data ('theme');
|
||||
|
||||
setThemeSettings ();
|
||||
});
|
||||
|
||||
$customizeMenuRadioBtns.on ('click', function () {
|
||||
|
||||
var optionName = $ (this).prop ('name');
|
||||
themeSettings[optionName] = $ (this).val ();
|
||||
|
||||
setThemeSettings ();
|
||||
});
|
||||
|
||||
function setThemeSettings() {
|
||||
setThemeState ()
|
||||
.delay (config.delayTime)
|
||||
.queue (function (next) {
|
||||
|
||||
setThemeColor ();
|
||||
setThemeControlsState ();
|
||||
saveThemeSettings ();
|
||||
|
||||
$ (document).trigger ("themechange");
|
||||
|
||||
next ();
|
||||
});
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Update theme based on options
|
||||
*************************************************/
|
||||
|
||||
function setThemeState() {
|
||||
// set theme type
|
||||
if (themeSettings.themeName) {
|
||||
$styleLink.attr ('href', window.cssPath + '/app-' + themeSettings.themeName + '.css');
|
||||
}
|
||||
else {
|
||||
$styleLink.attr ('href', window.cssPath + '/app.css');
|
||||
}
|
||||
// App classes
|
||||
$app.removeClass ('header-fixed footer-fixed sidebar-fixed');
|
||||
|
||||
// set header
|
||||
$app.addClass (themeSettings.headerPosition);
|
||||
|
||||
// set footer
|
||||
$app.addClass (themeSettings.footerPosition);
|
||||
|
||||
// set footer
|
||||
$app.addClass (themeSettings.sidebarPosition);
|
||||
|
||||
return $app;
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Update theme controls based on options
|
||||
*************************************************/
|
||||
|
||||
function setThemeControlsState() {
|
||||
// set color switcher
|
||||
$customizeMenuColorBtns.each (function () {
|
||||
if ($ (this).data ('theme') === themeSettings.themeName) {
|
||||
$ (this).addClass ('active');
|
||||
}
|
||||
else {
|
||||
$ (this).removeClass ('active');
|
||||
}
|
||||
});
|
||||
|
||||
// set radio buttons
|
||||
$customizeMenuRadioBtns.each (function () {
|
||||
var name = $ (this).prop ('name');
|
||||
var value = $ (this).val ();
|
||||
|
||||
if (themeSettings[name] === value) {
|
||||
$ (this).prop ("checked", true);
|
||||
}
|
||||
else {
|
||||
$ (this).prop ("checked", false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Update theme color
|
||||
*************************************************/
|
||||
function setThemeColor() {
|
||||
config.chart.colorPrimary = tinycolor ($ref.find (".chart .color-primary").css ("color"));
|
||||
config.chart.colorSecondary = tinycolor ($ref.find (".chart .color-secondary").css ("color"));
|
||||
}
|
||||
|
||||
/************************************************
|
||||
* Storage Functions
|
||||
*************************************************/
|
||||
|
||||
function getThemeSettings() {
|
||||
var settings = (localStorage.getItem ('themeSettings')) ? JSON.parse (localStorage.getItem ('themeSettings')) : {};
|
||||
|
||||
settings.headerPosition = settings.headerPosition || '';
|
||||
settings.sidebarPosition = settings.sidebarPosition || '';
|
||||
settings.footerPosition = settings.footerPosition || '';
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
function saveThemeSettings() {
|
||||
localStorage.setItem ('themeSettings', JSON.stringify (themeSettings));
|
||||
}
|
||||
});
|
||||
@@ -24,13 +24,14 @@
|
||||
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
|
||||
&.striped {
|
||||
& > div {
|
||||
.striped {
|
||||
& > div, & tr {
|
||||
border-bottom: 1px solid $item-list-color-border;
|
||||
}
|
||||
|
||||
& > div:nth-child(2n + 1) {
|
||||
& > div:nth-child(2n + 1), & tr:nth-child(2n + 1) {
|
||||
background-color: lighten($color-divider, 12%);
|
||||
|
||||
@include media-down(sm) {
|
||||
@@ -42,13 +43,20 @@
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* *******************************************
|
||||
* Item general styles
|
||||
********************************************** */
|
||||
|
||||
.item-list {
|
||||
.thumbnail {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -16,6 +16,10 @@ body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.p-l-25 {
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.main-wrapper {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
|
||||
@@ -7,5 +7,6 @@ return [
|
||||
"logoutSuccessfull" => "Odhlášení proběhlo úspěšně",
|
||||
"no" => "Ne",
|
||||
"yes" => "Ano",
|
||||
'empty' => 'Žádná data nebyla nalezena.',
|
||||
"noSearchEngine" => 'Není dostupný žádný vyhledávač.'
|
||||
];
|
||||
|
||||
@@ -35,7 +35,7 @@ return [
|
||||
"noHomepage" => "Domovská stránka není nastavena (a nebo je nastavena jako Koncept)! Vyberte prosím jinou stránku, nebo stávající publikujte.",
|
||||
"noPagesAvailable" => "Nejsou dostupné žádné stránky. Pro zobrazení přehledu nejprve nějakou vytvořte.",
|
||||
"noParentPage" => "-- Nadřazená stránka nevybrána --",
|
||||
"noTemplates" => "Nelze vytvořit stránku. Pro tento modul neexistuje žádná šablona, která by šla použít pro vykreslení obsahu.",
|
||||
"noTemplates" => "Pro tento modul neexistuje žádná šablona, která by šla použít pro vykreslení obsahu. Nebude možné tuto stránku zobrazit!",
|
||||
"nothingToEdit" => "Nebyly provedeny žádné změny, proto nedošlo k editaci.",
|
||||
"overview" => "Seznam všech obsahových stránek",
|
||||
"pageDeleted" => "Stránka :name úspěšně smazána.",
|
||||
@@ -56,4 +56,6 @@ return [
|
||||
"title" => "Titulek",
|
||||
"uploadTitleImage" => "Nahrát titulní obrázek",
|
||||
"viewPage" => "Zobrazit",
|
||||
'setOrder' => 'Nastavení pořadí stránek',
|
||||
'orderSuccessfullySet' => 'Pořadí stránek úspěšně nastaveno',
|
||||
];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user