Commit 1cc483fe authored by Nicolas Widart's avatar Nicolas Widart

Merge commit '8cc33230' as 'Modules/Menu'

parents eb8e1114 8cc33230
.idea/
.php_cs.cache
vendor/
composer.lock
Modules/
build/
<?php
$finder = Symfony\CS\Finder\DefaultFinder::create()
->exclude('Modules')
->exclude('vendor')
->in(__DIR__)
;
return Symfony\CS\Config\Config::create()
->setUsingCache(true)
->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
->fixers(array(
// Concatenation should be used with at least one whitespace around.
'concat_with_spaces',
// Unused use statements must be removed.
'ordered_use',
// Removes extra empty lines.
'extra_empty_lines',
// Removes line breaks between use statements.
'remove_lines_between_uses',
// An empty line feed should precede a return statement.
'return',
// Unused use statements must be removed.
'unused_use',
// Remove trailing whitespace at the end of blank lines.
'whitespacy_lines',
// There MUST be one blank line after the namespace declaration.
'line_after_namespace',
// There should be exactly one blank line before a namespace declaration.
'single_blank_line_before_namespace',
// Each namespace use MUST go on its own line and there MUST be one blank line after the use statements block.
'single_line_after_imports',
// Ensure there is no code on the same line as the PHP open tag and it is followed by a blankline.
'blankline_after_open_tag',
// Remove duplicated semicolons.
'duplicate_semicolon',
// PHP multi-line arrays should have a trailing comma.
'multiline_array_trailing_comma',
// There should be no empty lines after class opening brace.
'no_blank_lines_after_class_opening',
// There should not be blank lines between docblock and the documented element.
'no_empty_lines_after_phpdocs',
// Phpdocs should start and end with content, excluding the very first and last line of the docblocks.
'phpdoc_trim',
// Removes line breaks between use statements.
'remove_lines_between_uses',
))
->finder($finder);
rules:
php.interface_has_no_interface_suffix:
enabled: false
language: php
php:
- 7
- 5.6
- hhvm
env:
- LARAVEL_VERSION="~5.2" TESTBENCH_VERSION="~3.2"
before_script:
- travis_retry composer install --no-interaction --prefer-source
script: phpunit
sudo: false
notifications:
slack: asgardcms:p4i0eRhlenQrEOQDUba8NQyu
email:
- n.widart@gmail.com
- josh@joshbrown.me
matrix:
allow_failures:
- php: hhvm
.dd {
position: relative;
display: block;
margin: 0;
padding: 0;
list-style: none;
font-size: 13px;
line-height: 29px;
}
.dd-list {
display: block;
position: relative;
margin: 0;
padding: 0;
list-style: none;
}
.dd-list .dd-list {
padding-left: 30px;
}
.dd-collapsed .dd-list {
display: none;
}
.dd-item,
.dd-item-root,
.dd-empty,
.dd-placeholder {
display: block;
position: relative;
margin: 0;
padding: 0;
min-height: 20px;
font-size: 13px;
line-height: 20px;
}
.dd-handle-root,
.dd-handle {
display: block;
margin: 5px 0;
padding: 4px 10px;
color: #333;
text-decoration: none;
font-weight: bold;
border: 1px solid #ccc;
background: #fafafa;
-webkit-border-radius: 3px;
border-radius: 3px;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
.dd-handle:hover {
color: #2ea8e5;
background: #fff;
cursor: move;
}
.dd-item-root > .btn {
display: none;
}
.dd-item-root > button,
.dd-item > button {
display: block;
position: relative;
cursor: pointer;
float: right;
width: 25px;
height: 20px;
margin: 5px 0;
padding: 0;
text-indent: 100%;
white-space: nowrap;
overflow: hidden;
border: 0;
background: transparent;
font-size: 12px;
line-height: 1;
text-align: center;
font-weight: bold;
}
.dd-item-root > button:before,
.dd-item > button:before {
content: '+';
display: block;
position: absolute;
width: 100%;
text-align: center;
text-indent: 0;
font-size: 20px;
line-height: 9px;
}
.dd-item-root > button[data-action="collapse"]:before,
.dd-item > button[data-action="collapse"]:before {
content: '-';
font-size: 20px;
line-height: 9px;
}
.dd-placeholder,
.dd-empty {
margin: 5px 0;
padding: 0;
min-height: 30px;
background: #f2fbff;
border: 1px dashed #b6bcbf;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
.dd-empty {
border: 1px dashed #bbb;
min-height: 100px;
background-color: #e5e5e5;
background-image: -webkit-linear-gradient(45deg, #ffffff 25%, transparent 25%, transparent 75%, #ffffff 75%, #ffffff), -webkit-linear-gradient(45deg, #ffffff 25%, transparent 25%, transparent 75%, #ffffff 75%, #ffffff);
background-image: -moz-linear-gradient(45deg, #ffffff 25%, transparent 25%, transparent 75%, #ffffff 75%, #ffffff), -moz-linear-gradient(45deg, #ffffff 25%, transparent 25%, transparent 75%, #ffffff 75%, #ffffff);
background-image: linear-gradient(45deg, #ffffff 25%, transparent 25%, transparent 75%, #ffffff 75%, #ffffff), linear-gradient(45deg, #ffffff 25%, transparent 25%, transparent 75%, #ffffff 75%, #ffffff);
background-size: 60px 60px;
background-position: 0 0, 30px 30px;
}
.dd-dragel {
position: absolute;
pointer-events: none;
z-index: 9999;
}
.dd-dragel > .dd-item .dd-handle {
margin-top: 0;
}
.dd-dragel .dd-handle {
-webkit-box-shadow: 2px 4px 6px 0 rgba(0, 0, 0, 0.1);
box-shadow: 2px 4px 6px 0 rgba(0, 0, 0, 0.1);
}
/**
* Nestable Extras
*/
.nestable-lists {
display: block;
clear: both;
padding: 30px 0;
width: 100%;
border: 0;
border-top: 2px solid #ddd;
border-bottom: 2px solid #ddd;
}
#nestable-menu {
padding: 0;
margin: 20px 0;
}
#nestable-output,
#nestable2-output {
width: 100%;
height: 7em;
font-size: 0.75em;
line-height: 1.333333em;
font-family: Consolas, monospace;
padding: 5px;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
#nestable2 .dd-handle {
color: #fff;
border: 1px solid #999;
background: #bbb;
background: -webkit-linear-gradient(top, #bbbbbb 0%, #999999 100%);
background: -moz-linear-gradient(top, #bbbbbb 0%, #999999 100%);
background: linear-gradient(top, #bbbbbb 0%, #999999 100%);
}
#nestable2 .dd-handle:hover {
background: #bbb;
}
#nestable2 .dd-item > button:before {
color: #fff;
}
@media only screen and (min-width: 700px) {
.dd {
float: left;
width: 100%;
}
.dd + .dd {
margin-left: 2%;
}
}
.dd-hover > .dd-handle {
background: #2ea8e5 !important;
}
This diff is collapsed.
.dd {
position: relative;
display: block;
margin: 0;
padding: 0;
list-style: none;
font-size: 13px;
line-height: 29px;
}
.dd-list {
display: block;
position: relative;
margin: 0;
padding: 0;
list-style: none;
}
.dd-list .dd-list {
padding-left: 30px;
}
.dd-collapsed .dd-list {
display: none;
}
.dd-item,
.dd-item-root,
.dd-empty,
.dd-placeholder {
display: block;
position: relative;
margin: 0;
padding: 0;
min-height: 20px;
font-size: 13px;
line-height: 20px;
}
.dd-handle-root,
.dd-handle {
display: block;
margin: 5px 0;
padding: 4px 10px;
color: #333;
text-decoration: none;
font-weight: bold;
border: 1px solid #ccc;
background: #fafafa;
-webkit-border-radius: 3px;
border-radius: 3px;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
.dd-handle:hover {
color: #2ea8e5;
background: #fff;
cursor: move;
}
.dd-item-root > .btn {
display: none;
}
.dd-item-root > button,
.dd-item > button {
display: block;
position: relative;
cursor: pointer;
float: right;
width: 25px;
height: 20px;
margin: 5px 0;
padding: 0;
text-indent: 100%;
white-space: nowrap;
overflow: hidden;
border: 0;
background: transparent;
font-size: 12px;
line-height: 1;
text-align: center;
font-weight: bold;
}
.dd-item-root > button:before,
.dd-item > button:before {
content: '+';
display: block;
position: absolute;
width: 100%;
text-align: center;
text-indent: 0;
font-size: 20px;
line-height: 9px;
}
.dd-item-root > button[data-action="collapse"]:before,
.dd-item > button[data-action="collapse"]:before {
content: '-';
font-size: 20px;
line-height: 9px;
}
.dd-placeholder,
.dd-empty {
margin: 5px 0;
padding: 0;
min-height: 30px;
background: #f2fbff;
border: 1px dashed #b6bcbf;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
.dd-empty {
border: 1px dashed #bbb;
min-height: 100px;
background-color: #e5e5e5;
background-image: -webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
-webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
background-image: -moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
-moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
background-image: linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
background-size: 60px 60px;
background-position: 0 0, 30px 30px;
}
.dd-dragel {
position: absolute;
pointer-events: none;
z-index: 9999;
}
.dd-dragel > .dd-item .dd-handle {
margin-top: 0;
}
.dd-dragel .dd-handle {
-webkit-box-shadow: 2px 4px 6px 0 rgba(0, 0, 0, .1);
box-shadow: 2px 4px 6px 0 rgba(0, 0, 0, .1);
}
/**
* Nestable Extras
*/
.nestable-lists {
display: block;
clear: both;
padding: 30px 0;
width: 100%;
border: 0;
border-top: 2px solid #ddd;
border-bottom: 2px solid #ddd;
}
#nestable-menu {
padding: 0;
margin: 20px 0;
}
#nestable-output,
#nestable2-output {
width: 100%;
height: 7em;
font-size: 0.75em;
line-height: 1.333333em;
font-family: Consolas, monospace;
padding: 5px;
box-sizing: border-box;
-moz-box-sizing: border-box;
}
#nestable2 .dd-handle {
color: #fff;
border: 1px solid #999;
background: #bbb;
background: -webkit-linear-gradient(top, #bbb 0%, #999 100%);
background: -moz-linear-gradient(top, #bbb 0%, #999 100%);
background: linear-gradient(top, #bbb 0%, #999 100%);
}
#nestable2 .dd-handle:hover {
background: #bbb;
}
#nestable2 .dd-item > button:before {
color: #fff;
}
@media only screen and (min-width: 700px) {
.dd {
float: left;
width: 100%;
}
.dd + .dd {
margin-left: 2%;
}
}
.dd-hover > .dd-handle {
background: #2ea8e5 !important;
}
<?php
namespace Modules\Menu\Blade\Facades;
use Illuminate\Support\Facades\Facade;
final class MenuDirective extends Facade
{
protected static function getFacadeAccessor()
{
return 'menu.menu.directive';
}
}
<?php
namespace Modules\Menu\Blade;
final class MenuDirective
{
private $name;
private $presenter;
private $bindings;
public function show($arguments)
{
$this->extractArguments($arguments);
return $this->returnMenu();
}
/**
* Extract the possible arguments as class properties
* @param array $arguments
*/
private function extractArguments(array $arguments)
{
$this->name = array_get($arguments, 0);
$this->presenter = array_get($arguments, 1);
$this->bindings = array_get($arguments, 2, []);
}
/**
* Prepare arguments and return menu
* @return string|null
*/
private function returnMenu()
{
$customPresenter = config('asgard.menu.config.default_menu_presenter');
if ($this->presenter === null && $customPresenter !== null) {
$this->presenter = $customPresenter;
}
return app('menus')->get($this->name, $this->presenter, $this->bindings);
}
public function __toString()
{
return $this->returnMenu();
}
}
<?php
namespace Modules\Menu\Composers;
use Modules\Menu\Entities\Menuitem as MenuitemEntity;
use Modules\Menu\Repositories\MenuItemRepository;
use Modules\Menu\Repositories\MenuRepository;
use Nwidart\Menus\Builder;
use Nwidart\Menus\Facades\Menu;
use Nwidart\Menus\MenuItem;
class NavigationViewComposer
{
/**
* @var MenuRepository
*/
private $menu;
/**
* @var MenuItemRepository
*/
private $menuItem;
public function __construct(MenuRepository $menu, MenuItemRepository $menuItem)
{
$this->menu = $menu;
$this->menuItem = $menuItem;
}
public function compose()
{
foreach ($this->menu->all() as $menu) {
$menuTree = $this->menuItem->getTreeForMenu($menu->id);
Menu::create($menu->name, function (Builder $menu) use ($menuTree) {
foreach ($menuTree as $menuItem) {
$this->addItemToMenu($menuItem, $menu);
}
});
}
}
/**
* Add a menu item to the menu
* @param MenuitemEntity $item
* @param Builder $menu
*/
public function addItemToMenu(MenuitemEntity $item, Builder $menu)
{
if ($this->hasChildren($item)) {
$this->addChildrenToMenu($item->title, $item->items, $menu);
} else {
$target = $item->uri ?: $item->url;
$menu->url(
$target,
$item->title,
['target' => $item->target]
);
}
}
/**
* Add children to menu under the give name
*
* @param string $name
* @param object $children
* @param Builder|MenuItem $menu
*/
private function addChildrenToMenu($name, $children, $menu)
{
$menu->dropdown($name, function (MenuItem $subMenu) use ($children) {
foreach ($children as $child) {
$this->addSubItemToMenu($child, $subMenu);
}
});
}
/**
* Add children to the given menu recursively
*
* @param MenuitemEntity $child
* @param MenuItem $sub
*/
private function addSubItemToMenu(MenuitemEntity $child, MenuItem $sub)
{
$sub->url($child->uri, $child->title);
if ($this->hasChildren($child)) {
$this->addChildrenToMenu($child->title, $child->items, $sub);
}
}
/**
* Check if the given menu item has children
*
* @param object $item
* @return bool
*/
private function hasChildren($item)
{
return $item->items->count() > 0;
}
}
<?php
return [
/*
|--------------------------------------------------------------------------
| Custom Sidebar Class
|--------------------------------------------------------------------------
| If you want to customise the admin sidebar ordering or grouping
| You can define your own sidebar class for this module.
| No custom sidebar: null
*/
'custom-sidebar' => null,
/*
| Default Menu Presenter
|--------------------------------------------------------------------------
| Configure which Menu presenter will be used by default without
| having to send it via the views
*/
'default_menu_presenter' => null,
];
<?php
return [
'menu.menus' => [
'index' => 'menu::menu.list resource',
'create' => 'menu::menu.create resource',
'edit' => 'menu::menu.edit resource',
'destroy' => 'menu::menu.destroy resource',
],
'menu.menuitems' => [
'index' => 'menu::menu-items.list resource',
'create' => 'menu::menu-items.create resource',
'edit' => 'menu::menu-items.edit resource',
'destroy' => 'menu::menu-items.destroy resource',
],
];
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateMenusTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('menu__menus', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->string('name');
$table->tinyInteger('primary')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('menu__menus');
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateMenuTranslationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('menu__menu_translations', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('menu_id')->unsigned();
$table->string('locale')->index();
$table->tinyInteger('status')->default(0);
$table->string('title');
$table->unique(['menu_id', 'locale']);
$table->foreign('menu_id')->references('id')->on('menu__menus')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('menu__menu_translations');
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateMenuitemsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('menu__menuitems', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('menu_id')->unsigned();
$table->foreign('menu_id')->references('id')->on('menu__menus')->onDelete('cascade');
$table->integer('page_id')->unsigned()->nullable();
$table->integer('position')->unsigned()->default(0);
$table->string('target', 10)->nullable();
$table->string('module_name')->nullable();
/* Nested Sets */
$table->integer('parent_id')->nullable();
$table->integer('lft')->nullable();
$table->integer('rgt')->nullable();
$table->integer('depth')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('menu__menuitems');
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateMenuitemTranslationTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('menu__menuitem_translations', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('menuitem_id')->unsigned();
$table->string('locale')->index();
$table->tinyInteger('status')->default(0);
$table->string('title');
$table->string('url')->nullable();
$table->string('uri')->nullable();
$table->unique(['menuitem_id', 'locale']);
$table->foreign('menuitem_id')->references('id')->on('menu__menuitems')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('menu__menuitem_translations');
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddRootColumnToMenusTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('menu__menuitems', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->boolean('is_root')->default(0);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('menu__menuitems', function (Blueprint $table) {
$table->dropColumn('is_root');
});
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddIconColumnToMenuitemsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('menu__menuitems', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->string('icon')->default('');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('menu__menuitems', function (Blueprint $table) {
$table->dropColumn('icon');
});
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class UpdateIconColumnOnMenuitemsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('menu__menuitems', function (Blueprint $table) {
$table->string('icon')->nullable()->default(null)->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('menu__menuitems', function (Blueprint $table) {
$table->string('icon')->default('')->nullable(false)->change();
});
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddLinkTypeColymnToMenuitemsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('menu__menuitems', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->string('link_type')->after('target')->default('page');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('menu__menuitems', function (Blueprint $table) {
$table->dropColumn('link_type');
});
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddClassColumnToMenuitemsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('menu__menuitems', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->string('class')->after('link_type')->default('');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('menu__menuitems', function (Blueprint $table) {
$table->dropColumn('class');
});
}
}
<?php
namespace Modules\Menu\Database\Seeders;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Seeder;
class MenuDatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
}
}
<?php
namespace Modules\Menu\Entities;
use Dimsav\Translatable\Translatable;
use Illuminate\Database\Eloquent\Model;
class Menu extends Model
{
use Translatable;
protected $fillable = [
'name',
'title',
'status',
'primary',
];
public $translatedAttributes = ['title', 'status'];
protected $table = 'menu__menus';
public function menuitems()
{
return $this->hasMany('Modules\Menu\Entities\Menuitem')->orderBy('position', 'asc');
}
}
<?php
namespace Modules\Menu\Entities;
use Illuminate\Database\Eloquent\Model;
class MenuTranslation extends Model
{
protected $fillable = ['title', 'status'];
protected $table = 'menu__menu_translations';
}
<?php
namespace Modules\Menu\Entities;
use Dimsav\Translatable\Translatable;
use Illuminate\Database\Eloquent\Model;
use TypiCMS\NestableTrait;
class Menuitem extends Model
{
use Translatable, NestableTrait;
public $translatedAttributes = ['title', 'uri', 'url', 'status', 'locale'];
protected $fillable = [
'menu_id',
'page_id',
'parent_id',
'position',
'target',
'module_name',
'title',
'uri',
'url',
'status',
'is_root',
'icon',
'link_type',
'locale',
'class',
];
protected $table = 'menu__menuitems';
/**
* For nested collection
*
* @var array
*/
public $children = [];
public function menu()
{
return $this->belongsTo(Menu::class);
}
/**
* Make the current menu item child of the given root item
* @param Menuitem $rootItem
*/
public function makeChildOf(Menuitem $rootItem)
{
$this->parent_id = $rootItem->id;
$this->save();
}
/**
* Check if the current menu item is the root
* @return bool
*/
public function isRoot()
{
return (bool) $this->is_root;
}
/**
* Check if page_id is empty and returning null instead empty string
* @return number
*/
public function setPageIdAttribute($value)
{
$this->attributes['page_id'] = ! empty($value) ? $value : null;
}
/**
* Check if parent_id is empty and returning null instead empty string
* @return number
*/
public function setParentIdAttribute($value)
{
$this->attributes['parent_id'] = ! empty($value) ? $value : null;
}
}
<?php
namespace Modules\Menu\Entities;
use Illuminate\Database\Eloquent\Model;
class MenuitemTranslation extends Model
{
public $fillable = ['title', 'uri', 'url', 'status', 'locale'];
protected $table = 'menu__menuitem_translations';
}
<?php
namespace Modules\Menu\Events\Handlers;
use Modules\Menu\Events\MenuItemWasCreated;
use Modules\Menu\Repositories\MenuItemRepository;
class MakeMenuItemChildOfRoot
{
/**
* @var MenuItemRepository
*/
private $menuItem;
public function __construct(MenuItemRepository $menuItem)
{
$this->menuItem = $menuItem;
}
public function handle(MenuItemWasCreated $event)
{
$root = $this->menuItem->getRootForMenu($event->menuItem->menu_id);
if (! $this->isRoot($event->menuItem)) {
$event->menuItem->makeChildOf($root);
}
}
/**
* Check if the given menu item is not already a root menu item
* @param object $menuItem
* @return bool
*/
private function isRoot($menuItem)
{
return (bool) $menuItem->is_root;
}
}
<?php
namespace Modules\Menu\Events\Handlers;
use Modules\Menu\Events\MenuWasCreated;
use Modules\Menu\Repositories\MenuItemRepository;
use Modules\Setting\Contracts\Setting;
class RootMenuItemCreator
{
/**
* @var MenuItemRepository
*/
private $menuItem;
/**
* @var Setting
*/
private $setting;
public function __construct(MenuItemRepository $menuItem, Setting $setting)
{
$this->menuItem = $menuItem;
$this->setting = $setting;
}
public function handle(MenuWasCreated $event)
{
$data = [
'menu_id' => $event->menu->id,
'position' => 0,
'is_root' => true,
];
foreach ($this->getEnabledLocales() as $locale) {
$data[$locale]['title'] = 'root';
}
$this->menuItem->create($data);
}
/**
* Return an array of enabled locales
* @return array
*/
private function getEnabledLocales()
{
return json_decode($this->setting->get('core::locales', '{"en"}'));
}
}
<?php
namespace Modules\Menu\Events;
class MenuItemWasCreated
{
public $menuItem;
public function __construct($menuItem)
{
$this->menuItem = $menuItem;
}
}
<?php
namespace Modules\Menu\Events;
class MenuWasCreated
{
public $menu;
public function __construct($menu)
{
$this->menu = $menu;
}
}
<?php
namespace Modules\Menu\Http\Controllers\Admin;
use Modules\Core\Http\Controllers\Admin\AdminBaseController;
use Modules\Menu\Entities\Menu;
use Modules\Menu\Http\Requests\CreateMenuRequest;
use Modules\Menu\Http\Requests\UpdateMenuRequest;
use Modules\Menu\Repositories\MenuItemRepository;
use Modules\Menu\Repositories\MenuRepository;
use Modules\Menu\Services\MenuRenderer;
class MenuController extends AdminBaseController
{
/**
* @var MenuRepository
*/
private $menu;
/**
* @var MenuItemRepository
*/
private $menuItem;
/**
* @var MenuRenderer
*/
private $menuRenderer;
public function __construct(
MenuRepository $menu,
MenuItemRepository $menuItem,
MenuRenderer $menuRenderer
) {
parent::__construct();
$this->menu = $menu;
$this->menuItem = $menuItem;
$this->menuRenderer = $menuRenderer;
}
public function index()
{
$menus = $this->menu->all();
return view('menu::admin.menus.index', compact('menus'));
}
public function create()
{
return view('menu::admin.menus.create');
}
public function store(CreateMenuRequest $request)
{
$this->menu->create($request->all());
return redirect()->route('admin.menu.menu.index')
->withSuccess(trans('menu::messages.menu created'));
}
public function edit(Menu $menu)
{
$menuItems = $this->menuItem->allRootsForMenu($menu->id);
$menuStructure = $this->menuRenderer->renderForMenu($menu->id, $menuItems->nest());
return view('menu::admin.menus.edit', compact('menu', 'menuStructure'));
}
public function update(Menu $menu, UpdateMenuRequest $request)
{
$this->menu->update($menu, $request->all());
return redirect()->route('admin.menu.menu.index')
->withSuccess(trans('menu::messages.menu updated'));
}
public function destroy(Menu $menu)
{
$this->menu->destroy($menu);
return redirect()->route('admin.menu.menu.index')
->withSuccess(trans('menu::messages.menu deleted'));
}
}
<?php
namespace Modules\Menu\Http\Controllers\Admin;
use Illuminate\Foundation\Http\FormRequest;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
use Modules\Core\Http\Controllers\Admin\AdminBaseController;
use Modules\Menu\Entities\Menu;
use Modules\Menu\Entities\Menuitem;
use Modules\Menu\Http\Requests\CreateMenuItemRequest;
use Modules\Menu\Http\Requests\UpdateMenuItemRequest;
use Modules\Menu\Repositories\MenuItemRepository;
use Modules\Menu\Services\MenuItemUriGenerator;
use Modules\Page\Repositories\PageRepository;
class MenuItemController extends AdminBaseController
{
/**
* @var MenuItemRepository
*/
private $menuItem;
/**
* @var PageRepository
*/
private $page;
/**
* @var MenuItemUriGenerator
*/
private $menuItemUriGenerator;
public function __construct(MenuItemRepository $menuItem, PageRepository $page, MenuItemUriGenerator $menuItemUriGenerator)
{
parent::__construct();
$this->menuItem = $menuItem;
$this->page = $page;
$this->menuItemUriGenerator = $menuItemUriGenerator;
}
public function create(Menu $menu)
{
$pages = $this->page->all();
$menuSelect = $this->getMenuSelect($menu);
return view('menu::admin.menuitems.create', compact('menu', 'pages', 'menuSelect'));
}
public function store(Menu $menu, CreateMenuItemRequest $request)
{
$this->menuItem->create($this->addMenuId($menu, $request));
return redirect()->route('admin.menu.menu.edit', [$menu->id])
->withSuccess(trans('menu::messages.menuitem created'));
}
public function edit(Menu $menu, Menuitem $menuItem)
{
$pages = $this->page->all();
$menuSelect = $this->getMenuSelect($menu);
return view('menu::admin.menuitems.edit', compact('menu', 'menuItem', 'pages', 'menuSelect'));
}
public function update(Menu $menu, Menuitem $menuItem, UpdateMenuItemRequest $request)
{
$this->menuItem->update($menuItem, $this->addMenuId($menu, $request));
return redirect()->route('admin.menu.menu.edit', [$menu->id])
->withSuccess(trans('menu::messages.menuitem updated'));
}
public function destroy(Menu $menu, Menuitem $menuItem)
{
$this->menuItem->destroy($menuItem);
return redirect()->route('admin.menu.menu.edit', [$menu->id])
->withSuccess(trans('menu::messages.menuitem deleted'));
}
/**
* @param Menu, $menuItemId
* @return array
*/
private function getMenuSelect($menu)
{
return $menu->menuitems()->where('is_root', '!=', true)->get()->nest()->listsFlattened('title');
}
/**
* @param Menu $menu
* @param \Illuminate\Foundation\Http\FormRequest $request
* @return array
*/
private function addMenuId(Menu $menu, FormRequest $request)
{
$data = $request->all();
foreach (LaravelLocalization::getSupportedLanguagesKeys() as $lang) {
if ($data['link_type'] === 'page' && ! empty($data['page_id'])) {
$data[$lang]['uri'] = $this->menuItemUriGenerator->generateUri($data['page_id'], $data['parent_id'], $lang);
}
}
if (empty($data['parent_id'])) {
$data['parent_id'] = $this->menuItem->getRootForMenu($menu->id)->id;
}
return array_merge($data, ['menu_id' => $menu->id]);
}
}
<?php
namespace Modules\Menu\Http\Controllers\Api;
use Illuminate\Contracts\Cache\Repository;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Response;
use Modules\Menu\Repositories\MenuItemRepository;
use Modules\Menu\Services\MenuOrdener;
class MenuItemController extends Controller
{
/**
* @var Repository
*/
private $cache;
/**
* @var MenuOrdener
*/
private $menuOrdener;
/**
* @var MenuItemRepository
*/
private $menuItem;
public function __construct(MenuOrdener $menuOrdener, Repository $cache, MenuItemRepository $menuItem)
{
$this->cache = $cache;
$this->menuOrdener = $menuOrdener;
$this->menuItem = $menuItem;
}
/**
* Update all menu items
* @param Request $request
*/
public function update(Request $request)
{
$this->cache->tags('menuItems')->flush();
$this->menuOrdener->handle($request->get('menu'));
}
/**
* Delete a menu item
* @param Request $request
* @return mixed
*/
public function delete(Request $request)
{
$menuItem = $this->menuItem->find($request->get('menuitem'));
if (! $menuItem) {
return Response::json(['errors' => true]);
}
$this->menuItem->destroy($menuItem);
return Response::json(['errors' => false]);
}
}
<?php
namespace Modules\Menu\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreateMenuItemRequest extends FormRequest
{
public function rules()
{
return [];
}
public function authorize()
{
return true;
}
public function messages()
{
return [];
}
}
<?php
namespace Modules\Menu\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CreateMenuRequest extends FormRequest
{
public function rules()
{
return [
'name' => 'required',
'primary' => 'unique:menu__menus',
];
}
public function authorize()
{
return true;
}
public function messages()
{
return [
'name.required' => trans('menu::validation.name is required'),
'primary.unique' => trans('menu::validation.only one primary menu'),
];
}
}
<?php
namespace Modules\Menu\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateMenuItemRequest extends FormRequest
{
public function rules()
{
return [];
}
public function authorize()
{
return true;
}
public function messages()
{
return [];
}
}
<?php
namespace Modules\Menu\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateMenuRequest extends FormRequest
{
public function rules()
{
$menu = $this->route()->getParameter('menu');
return [
'name' => 'required',
'primary' => "unique:menu__menus,primary,{$menu->id}",
];
}
public function authorize()
{
return true;
}
public function messages()
{
return [
'name.required' => trans('menu::validation.name is required'),
'primary.unique' => trans('menu::validation.only one primary menu'),
];
}
}
<?php
use Illuminate\Routing\Router;
/** @var Router $router */
$router->group(['prefix' => '/menuitem', 'middleware' => 'api.token'], function (Router $router) {
$router->post('/update', [
'as' => 'api.menuitem.update',
'uses' => 'MenuItemController@update',
'middleware' => 'token-can:menu.menuitems.edit',
]);
$router->post('/delete', [
'as' => 'api.menuitem.delete',
'uses' => 'MenuItemController@delete',
'middleware' => 'token-can:menu.menuitems.destroy',
]);
});
<?php
use Illuminate\Routing\Router;
/** @var Router $router */
$router->bind('menu', function ($id) {
return app(\Modules\Menu\Repositories\MenuRepository::class)->find($id);
});
$router->bind('menuitem', function ($id) {
return app(\Modules\Menu\Repositories\MenuItemRepository::class)->find($id);
});
$router->group(['prefix' => '/menu'], function (Router $router) {
$router->get('menus', [
'as' => 'admin.menu.menu.index',
'uses' => 'MenuController@index',
'middleware' => 'can:menu.menus.index',
]);
$router->get('menus/create', [
'as' => 'admin.menu.menu.create',
'uses' => 'MenuController@create',
'middleware' => 'can:menu.menus.create',
]);
$router->post('menus', [
'as' => 'admin.menu.menu.store',
'uses' => 'MenuController@store',
'middleware' => 'can:menu.menus.create',
]);
$router->get('menus/{menu}/edit', [
'as' => 'admin.menu.menu.edit',
'uses' => 'MenuController@edit',
'middleware' => 'can:menu.menus.edit',
]);
$router->put('menus/{menu}', [
'as' => 'admin.menu.menu.update',
'uses' => 'MenuController@update',
'middleware' => 'can:menu.menus.edit',
]);
$router->delete('menus/{menu}', [
'as' => 'admin.menu.menu.destroy',
'uses' => 'MenuController@destroy',
'middleware' => 'can:menu.menus.destroy',
]);
$router->get('menus/{menu}/menuitem', [
'as' => 'dashboard.menuitem.index',
'uses' => 'MenuItemController@index',
'middleware' => 'can:menu.menuitems.index',
]);
$router->get('menus/{menu}/menuitem/create', [
'as' => 'dashboard.menuitem.create',
'uses' => 'MenuItemController@create',
'middleware' => 'can:menu.menuitems.create',
]);
$router->post('menus/{menu}/menuitem', [
'as' => 'dashboard.menuitem.store',
'uses' => 'MenuItemController@store',
'middleware' => 'can:menu.menuitems.create',
]);
$router->get('menus/{menu}/menuitem/{menuitem}/edit', [
'as' => 'dashboard.menuitem.edit',
'uses' => 'MenuItemController@edit',
'middleware' => 'can:menu.menuitems.edit',
]);
$router->put('menus/{menu}/menuitem/{menuitem}', [
'as' => 'dashboard.menuitem.update',
'uses' => 'MenuItemController@update',
'middleware' => 'can:menu.menuitems.edit',
]);
$router->delete('menus/{menu}/menuitem/{menuitem}', [
'as' => 'dashboard.menuitem.destroy',
'uses' => 'MenuItemController@destroy',
'middleware' => 'can:menu.menuitems.destroy',
]);
});
# License (MIT)
Copyright (c) 2016 Nicolas Widart , n.widart@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
<?php
namespace Modules\Menu\Presenters;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
use Nwidart\Menus\MenuItem;
use Nwidart\Menus\Presenters\Presenter;
class MenuPresenter extends Presenter
{
public function setLocale($item)
{
if (starts_with($item->url, 'http')) {
return;
}
if (LaravelLocalization::hideDefaultLocaleInURL() === false) {
$item->url = locale() . '/' . preg_replace('%^/?' . locale() . '/%', '$1', $item->url);
}
}
/**
* {@inheritdoc }.
*/
public function getOpenTagWrapper()
{
return PHP_EOL . '<ul class="nav navbar-nav">' . PHP_EOL;
}
/**
* {@inheritdoc }.
*/
public function getCloseTagWrapper()
{
return PHP_EOL . '</ul>' . PHP_EOL;
}
/**
* {@inheritdoc }.
*/
public function getMenuWithoutDropdownWrapper($item)
{
$this->setLocale($item);
return '<li' . $this->getActiveState($item) . '><a href="' . $item->getUrl() . '" ' . $item->getAttributes() . '>' . $item->getIcon() . ' ' . $item->title . '</a></li>' . PHP_EOL;
}
/**
* {@inheritdoc }.
*/
public function getActiveState($item, $state = ' class="active"')
{
return $item->isActive() ? $state : null;
}
/**
* Get active state on child items.
*
* @param $item
* @param string $state
*
* @return null|string
*/
public function getActiveStateOnChild($item, $state = 'active')
{
return $item->hasActiveOnChild() ? $state : null;
}
/**
* {@inheritdoc }.
*/
public function getMenuWithDropDownWrapper($item)
{
return '<li class="dropdown' . $this->getActiveStateOnChild($item, ' active') . '">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
' . $item->getIcon() . ' ' . $item->title . '
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
' . $this->getChildMenuItems($item) . '
</ul>
</li>'
. PHP_EOL;
}
/**
* Get multilevel menu wrapper.
*
* @param MenuItem $item
*
* @return string`
*/
public function getMultiLevelDropdownWrapper($item)
{
return '<li class="dropdown' . $this->getActiveStateOnChild($item, ' active') . '">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
' . $item->getIcon() . ' ' . $item->title . '
<b class="caret pull-right caret-right"></b>
</a>
<ul class="dropdown-menu">
' . $this->getChildMenuItems($item) . '
</ul>
</li>'
. PHP_EOL;
}
}
<?php
namespace Modules\Menu\Providers;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Modules\Menu\Events\Handlers\RootMenuItemCreator;
use Modules\Menu\Events\MenuWasCreated;
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
MenuWasCreated::class => [
RootMenuItemCreator::class,
],
];
}
<?php
namespace Modules\Menu\Providers;
use Illuminate\Support\ServiceProvider;
use Modules\Core\Traits\CanPublishConfiguration;
use Modules\Menu\Blade\MenuDirective;
use Modules\Menu\Entities\Menu;
use Modules\Menu\Entities\Menuitem;
use Modules\Menu\Repositories\Cache\CacheMenuDecorator;
use Modules\Menu\Repositories\Cache\CacheMenuItemDecorator;
use Modules\Menu\Repositories\Eloquent\EloquentMenuItemRepository;
use Modules\Menu\Repositories\Eloquent\EloquentMenuRepository;
use Nwidart\Menus\MenuBuilder as Builder;
use Nwidart\Menus\Facades\Menu as MenuFacade;
use Nwidart\Menus\MenuItem as PingpongMenuItem;
class MenuServiceProvider extends ServiceProvider
{
use CanPublishConfiguration;
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->registerBindings();
$this->app->bind('menu.menu.directive', function () {
return new MenuDirective();
});
}
/**
* Register all online menus on the Pingpong/Menu package
*/
public function boot()
{
$this->registerMenus();
$this->registerBladeTags();
$this->publishConfig('menu', 'permissions');
$this->publishConfig('menu', 'config');
$this->loadMigrationsFrom(__DIR__.'/../Database/Migrations');
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return array();
}
/**
* Register class binding
*/
private function registerBindings()
{
$this->app->bind(
'Modules\Menu\Repositories\MenuRepository',
function () {
$repository = new EloquentMenuRepository(new Menu());
if (! config('app.cache')) {
return $repository;
}
return new CacheMenuDecorator($repository);
}
);
$this->app->bind(
'Modules\Menu\Repositories\MenuItemRepository',
function () {
$repository = new EloquentMenuItemRepository(new Menuitem());
if (! config('app.cache')) {
return $repository;
}
return new CacheMenuItemDecorator($repository);
}
);
}
/**
* Add a menu item to the menu
* @param Menuitem $item
* @param Builder $menu
*/
public function addItemToMenu(Menuitem $item, Builder $menu)
{
if ($this->hasChildren($item)) {
$this->addChildrenToMenu($item->title, $item->items, $menu, ['icon' => $item->icon, 'target' => $item->target]);
} else {
$target = $item->link_type != 'external' ? $item->locale . '/' . $item->uri : $item->url;
$menu->url(
$target,
$item->title,
[
'target' => $item->target,
'icon' => $item->icon,
'class' => $item->class,
]
);
}
}
/**
* Add children to menu under the give name
*
* @param string $name
* @param object $children
* @param Builder|MenuItem $menu
*/
private function addChildrenToMenu($name, $children, $menu, $attribs = [])
{
$menu->dropdown($name, function (PingpongMenuItem $subMenu) use ($children) {
foreach ($children as $child) {
$this->addSubItemToMenu($child, $subMenu);
}
}, 0, $attribs);
}
/**
* Add children to the given menu recursively
* @param Menuitem $child
* @param PingpongMenuItem $sub
*/
private function addSubItemToMenu(Menuitem $child, PingpongMenuItem $sub)
{
if ($this->hasChildren($child)) {
$this->addChildrenToMenu($child->title, $child->items, $sub);
} else {
$target = $child->link_type != 'external' ? $child->locale . '/' . $child->uri : $child->url;
$sub->url($target, $child->title, 0, ['icon' => $child->icon, 'target' => $child->target, 'class' => $child->class]);
}
}
/**
* Check if the given menu item has children
*
* @param object $item
* @return bool
*/
private function hasChildren($item)
{
return $item->items->count() > 0;
}
/**
* Register the active menus
*/
private function registerMenus()
{
if (! $this->app['asgard.isInstalled']) {
return;
}
$menu = $this->app->make('Modules\Menu\Repositories\MenuRepository');
$menuItem = $this->app->make('Modules\Menu\Repositories\MenuItemRepository');
foreach ($menu->allOnline() as $menu) {
$menuTree = $menuItem->getTreeForMenu($menu->id);
MenuFacade::create($menu->name, function (Builder $menu) use ($menuTree) {
foreach ($menuTree as $menuItem) {
$this->addItemToMenu($menuItem, $menu);
}
});
}
}
/**
* Register menu blade tags
*/
protected function registerBladeTags()
{
if (app()->environment() === 'testing') {
return;
}
$this->app['blade.compiler']->directive('menu', function ($arguments) {
return "<?php echo MenuDirective::show([$arguments]); ?>";
});
}
}
<?php
namespace Modules\Menu\Providers;
use Modules\Core\Providers\RoutingServiceProvider as CoreRoutingServiceProvider;
class RouteServiceProvider extends CoreRoutingServiceProvider
{
/**
* The root namespace to assume when generating URLs to actions.
* @var string
*/
protected $namespace = 'Modules\Menu\Http\Controllers';
/**
* @return string
*/
protected function getFrontendRoute()
{
return false;
}
/**
* @return string
*/
protected function getBackendRoute()
{
return __DIR__ . '/../Http/backendRoutes.php';
}
/**
* @return string
*/
protected function getApiRoute()
{
return __DIR__ . '/../Http/apiRoutes.php';
}
}
<?php
namespace Modules\Menu\Repositories\Cache;
use Modules\Core\Repositories\Cache\BaseCacheDecorator;
use Modules\Menu\Repositories\MenuRepository;
class CacheMenuDecorator extends BaseCacheDecorator implements MenuRepository
{
/**
* @var MenuRepository
*/
protected $repository;
public function __construct(MenuRepository $menu)
{
parent::__construct();
$this->entityName = 'menus';
$this->repository = $menu;
}
/**
* Get all online menus
* @return object
*/
public function allOnline()
{
return $this->cache
->tags([$this->entityName, 'global'])
->remember("{$this->locale}.{$this->entityName}.allOnline", $this->cacheTime,
function () {
return $this->repository->allOnline();
}
);
}
}
<?php
namespace Modules\Menu\Repositories\Cache;
use Modules\Core\Repositories\Cache\BaseCacheDecorator;
use Modules\Menu\Repositories\MenuItemRepository;
class CacheMenuItemDecorator extends BaseCacheDecorator implements MenuItemRepository
{
/**
* @var MenuItemRepository
*/
protected $repository;
public function __construct(MenuItemRepository $menuItem)
{
parent::__construct();
$this->entityName = 'menusItems';
$this->repository = $menuItem;
}
/**
* Get all root elements
*
* @param int $menuId
* @return mixed
*/
public function rootsForMenu($menuId)
{
return $this->cache
->tags([$this->entityName, 'global'])
->remember("{$this->locale}.{$this->entityName}.rootsForMenu.{$menuId}", $this->cacheTime,
function () use ($menuId) {
return $this->repository->rootsForMenu($menuId);
}
);
}
/**
* Get the menu items ready for routes
*
* @return mixed
*/
public function getForRoutes()
{
return $this->cache
->tags([$this->entityName, 'global'])
->remember("{$this->locale}.{$this->entityName}.getForRoutes", $this->cacheTime,
function () {
return $this->repository->getForRoutes();
}
);
}
/**
* Get the root menu item for the given menu id
*
* @param int $menuId
* @return object
*/
public function getRootForMenu($menuId)
{
return $this->cache
->tags([$this->entityName, 'global'])
->remember("{$this->locale}.{$this->entityName}.getRootForMenu.{$menuId}", $this->cacheTime,
function () use ($menuId) {
return $this->repository->getRootForMenu($menuId);
}
);
}
/**
* Return a complete tree for the given menu id
*
* @param int $menuId
* @return object
*/
public function getTreeForMenu($menuId)
{
return $this->cache
->tags([$this->entityName, 'global'])
->remember("{$this->locale}.{$this->entityName}.getTreeForMenu.{$menuId}", $this->cacheTime,
function () use ($menuId) {
return $this->repository->getTreeForMenu($menuId);
}
);
}
/**
* Get all root elements
*
* @param int $menuId
* @return object
*/
public function allRootsForMenu($menuId)
{
return $this->cache
->tags([$this->entityName, 'global'])
->remember("{$this->locale}.{$this->entityName}.allRootsForMenu.{$menuId}", $this->cacheTime,
function () use ($menuId) {
return $this->repository->allRootsForMenu($menuId);
}
);
}
/**
* @param string $uri
* @param string $locale
* @return object
*/
public function findByUriInLanguage($uri, $locale)
{
return $this->cache
->tags([$this->entityName, 'global'])
->remember("{$this->locale}.{$this->entityName}.findByUriInLanguage.{$uri}.{$locale}", $this->cacheTime,
function () use ($uri, $locale) {
return $this->repository->findByUriInLanguage($uri, $locale);
}
);
}
}
<?php
namespace Modules\Menu\Repositories\Eloquent;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Modules\Core\Repositories\Eloquent\EloquentBaseRepository;
use Modules\Menu\Events\MenuItemWasCreated;
use Modules\Menu\Repositories\MenuItemRepository;
class EloquentMenuItemRepository extends EloquentBaseRepository implements MenuItemRepository
{
public function create($data)
{
$menuItem = $this->model->create($data);
event(new MenuItemWasCreated($menuItem));
return $menuItem;
}
public function update($menuItem, $data)
{
$menuItem->update($data);
return $menuItem;
}
/**
* Get online root elements
*
* @param int $menuId
* @return object
*/
public function rootsForMenu($menuId)
{
return $this->model->whereHas('translations', function (Builder $q) {
$q->where('status', 1);
$q->where('locale', App::getLocale());
})->with('translations')->whereMenuId($menuId)->orderBy('position')->get();
}
/**
* Get all root elements
*
* @param int $menuId
* @return object
*/
public function allRootsForMenu($menuId)
{
return $this->model->with('translations')->whereMenuId($menuId)->orderBy('parent_id')->orderBy('position')->get();
}
/**
* Get Items to build routes
*
* @return Array
*/
public function getForRoutes()
{
$menuitems = DB::table('menu__menus')
->select(
'primary',
'menu__menuitems.id',
'menu__menuitems.parent_id',
'menu__menuitems.module_name',
'menu__menuitem_translations.uri',
'menu__menuitem_translations.locale'
)
->join('menu__menuitems', 'menu__menus.id', '=', 'menu__menuitems.menu_id')
->join('menu__menuitem_translations', 'menu__menuitems.id', '=', 'menu__menuitem_translations.menuitem_id')
->where('uri', '!=', '')
->where('module_name', '!=', '')
->where('status', '=', 1)
->where('primary', '=', 1)
->orderBy('module_name')
->get();
$menuitemsArray = [];
foreach ($menuitems as $menuitem) {
$menuitemsArray[$menuitem->module_name][$menuitem->locale] = $menuitem->uri;
}
return $menuitemsArray;
}
/**
* Get the root menu item for the given menu id
*
* @param int $menuId
* @return object
*/
public function getRootForMenu($menuId)
{
return $this->model->with('translations')->where(['menu_id' => $menuId, 'is_root' => true])->firstOrFail();
}
/**
* Return a complete tree for the given menu id
*
* @param int $menuId
* @return object
*/
public function getTreeForMenu($menuId)
{
$items = $this->rootsForMenu($menuId);
return $items->nest();
}
/**
* @param string $uri
* @param string $locale
* @return object
*/
public function findByUriInLanguage($uri, $locale)
{
return $this->model->whereHas('translations', function (Builder $q) use ($locale, $uri) {
$q->where('status', 1);
$q->where('locale', $locale);
$q->where('uri', $uri);
})->with('translations')->first();
}
}
<?php
namespace Modules\Menu\Repositories\Eloquent;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\App;
use Modules\Core\Repositories\Eloquent\EloquentBaseRepository;
use Modules\Menu\Events\MenuWasCreated;
use Modules\Menu\Repositories\MenuRepository;
class EloquentMenuRepository extends EloquentBaseRepository implements MenuRepository
{
public function create($data)
{
$menu = $this->model->create($data);
event(new MenuWasCreated($menu));
return $menu;
}
public function update($menu, $data)
{
$menu->update($data);
return $menu;
}
/**
* Get all online menus
* @return object
*/
public function allOnline()
{
$locale = App::getLocale();
return $this->model->whereHas('translations', function (Builder $q) use ($locale) {
$q->where('locale', "$locale");
$q->where('status', 1);
})->with('translations')->orderBy('created_at', 'DESC')->get();
}
}
<?php
namespace Modules\Menu\Repositories;
use Modules\Core\Repositories\BaseRepository;
interface MenuItemRepository extends BaseRepository
{
/**
* Get online root elements
*
* @param int $menuId
* @return object
*/
public function rootsForMenu($menuId);
/**
* Get all root elements
*
* @param int $menuId
* @return object
*/
public function allRootsForMenu($menuId);
/**
* Get the menu items ready for routes
* @return mixed
*/
public function getForRoutes();
/**
* Get the root menu item for the given menu id
* @param int $menuId
* @return object
*/
public function getRootForMenu($menuId);
/**
* Return a complete tree for the given menu id
*
* @param int $menuId
* @return object
*/
public function getTreeForMenu($menuId);
/**
* @param string $uri
* @param string $locale
* @return object
*/
public function findByUriInLanguage($uri, $locale);
}
<?php
namespace Modules\Menu\Repositories;
use Modules\Core\Repositories\BaseRepository;
interface MenuRepository extends BaseRepository
{
/**
* Get all online menus
* @return object
*/
public function allOnline();
}
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('menu::menu.titles.create menu item') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ URL::route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('core::core.breadcrumb.home') }}</a></li>
<li><a href="{{ URL::route('admin.menu.menu.index') }}">{{ trans('menu::menu.breadcrumb.menu') }}</a></li>
<li>{{ trans('menu::menu.breadcrumb.create menu item') }}</li>
</ol>
@stop
@section('styles')
@stop
@section('content')
{!! Form::open(['route' => ['dashboard.menuitem.store', $menu->id], 'method' => 'post']) !!}
<div class="row">
<div class="col-md-9">
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">{{ trans('core::core.title.translatable fields') }}</h3>
</div>
<div class="box-body">
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<?php $i = 0; ?>
<?php foreach (LaravelLocalization::getSupportedLocales() as $locale => $language): ?>
<?php $i++; ?>
<li class="{{ App::getLocale() == $locale ? 'active' : '' }}">
<a href="#tab_{{ $i }}" data-toggle="tab">{{ trans('core::core.tab.'. strtolower($language['name'])) }}</a>
</li>
<?php endforeach; ?>
</ul>
<div class="tab-content">
<?php $i = 0; ?>
<?php foreach (LaravelLocalization::getSupportedLocales() as $locale => $language): ?>
<?php $i++; ?>
<div class="tab-pane {{ App::getLocale() == $locale ? 'active' : '' }}" id="tab_{{ $i }}">
@include('menu::admin.menuitems.partials.create-trans-fields', ['lang' => $locale])
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">{{ trans('core::core.title.non translatable fields') }}</h3>
</div>
<div class="box-body">
@include('menu::admin.menuitems.partials.create-fields')
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat">{{ trans('core::core.button.create') }}</button>
<button class="btn btn-default btn-flat" name="button" type="reset">{{ trans('core::core.button.reset') }}</button>
<a class="btn btn-danger pull-right btn-flat" href="{{ URL::route('admin.menu.menu.edit', [$menu->id])}}"><i class="fa fa-times"></i> {{ trans('core::core.button.cancel') }}</a>
</div>
</div>
<div class="col-md-3">
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">{{ trans('menu::menu-items.link-type.link type') }}</h3>
</div>
<div class="box-body">
<div class="radio">
<input type="radio" id="link-page" name="link_type" value="page" checked><label for="link-page">{{ trans('menu::menu-items.link-type.page') }}</label>
</div>
<div class="radio">
<input type="radio" id="link-internal" name="link_type" value="internal"><label for="link-internal">{{ trans('menu::menu-items.link-type.internal') }}</label>
</div>
<div class="radio">
<input type="radio" id="link-external" name="link_type" value="external"><label for="link-external">{{ trans('menu::menu-items.link-type.external') }}</label>
</div>
</div>
</div>
</div>
</div>
{!! Form::close() !!}
@stop
@section('footer')
<a data-toggle="modal" data-target="#keyboardShortcutsModal"><i class="fa fa-keyboard-o"></i></a> &nbsp;
@stop
@section('shortcuts')
<dl class="dl-horizontal">
<dt><code>b</code></dt>
<dd>{{ trans('core::core.back to index', ['name' => 'menu']) }}</dd>
</dl>
@stop
@section('scripts')
<script>
$( document ).ready(function() {
$(document).keypressAction({
actions: [
{ key: 'b', route: "<?= route('admin.menu.menu.edit', [$menu->id]) ?>" }
]
});
$('.link-type-depended').hide();
$('.link-page').fadeIn();
$('[name="link_type"]').iCheck({
checkboxClass: 'icheckbox_minimal',
radioClass: 'iradio_flat-blue'
}).on('ifChecked',function(){
$('.link-type-depended').hide();
$('.link-'+$(this).val()).fadeIn();
});
$('input[type="checkbox"].flat-blue, input[type="radio"].flat-blue').iCheck({
checkboxClass: 'icheckbox_flat-blue',
radioClass: 'iradio_flat-blue'
});
});
</script>
@stop
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('menu::menu.titles.edit menu item') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ URL::route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('core::core.breadcrumb.home') }}</a></li>
<li><a href="{{ URL::route('admin.menu.menu.index') }}">{{ trans('menu::menu.breadcrumb.menu') }}</a></li>
<li>{{ trans('menu::menu.breadcrumb.edit menu item') }}</li>
</ol>
@stop
@section('styles')
@stop
@section('content')
{!! Form::open(['route' => ['dashboard.menuitem.update', $menu->id, $menuItem->id], 'method' => 'put']) !!}
<div class="row">
<div class="col-md-9">
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">{{ trans('core::core.title.translatable fields') }}</h3>
</div>
<div class="box-body">
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<?php $i = 0; ?>
<?php foreach (LaravelLocalization::getSupportedLocales() as $locale => $language): ?>
<?php $i++; ?>
<li class="{{ App::getLocale() == $locale ? 'active' : '' }}">
<a href="#tab_{{ $i }}" data-toggle="tab">{{ trans('core::core.tab.'. strtolower($language['name'])) }}</a>
</li>
<?php endforeach; ?>
</ul>
<div class="tab-content">
<?php $i = 0; ?>
<?php foreach (LaravelLocalization::getSupportedLocales() as $locale => $language): ?>
<?php $i++; ?>
<div class="tab-pane {{ App::getLocale() == $locale ? 'active' : '' }}" id="tab_{{ $i }}">
@include('menu::admin.menuitems.partials.edit-trans-fields', ['lang' => $locale])
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">{{ trans('core::core.title.non translatable fields') }}</h3>
</div>
<div class="box-body">
@include('menu::admin.menuitems.partials.edit-fields')
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat">{{ trans('core::core.button.update') }}</button>
<button class="btn btn-default btn-flat" name="button" type="reset">{{ trans('core::core.button.reset') }}</button>
<a class="btn btn-danger pull-right btn-flat" href="{{ URL::route('admin.menu.menu.edit', [$menu->id])}}"><i class="fa fa-times"></i> {{ trans('core::core.button.cancel') }}</a>
</div>
</div>
<div class="col-md-3">
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">{{ trans('menu::menu-items.link-type.link type') }}</h3>
</div>
<div class="box-body">
<div class="radio">
<input type="radio" id="link-page" name="link_type"
value="page"{{ $menuItem->link_type === 'page' ? ' checked' : '' }}>
<label for="link-page">{{ trans('menu::menu-items.link-type.page') }}</label>
</div>
<div class="radio">
<input type="radio" id="link-internal" name="link_type"
value="internal"{{ $menuItem->link_type === 'internal' ? ' checked' : '' }}>
<label for="link-internal">{{ trans('menu::menu-items.link-type.internal') }}</label>
</div>
<div class="radio">
<input type="radio" id="link-external" name="link_type"
value="external"{{ $menuItem->link_type === 'external' ? ' checked' : '' }}>
<label for="link-external">{{ trans('menu::menu-items.link-type.external') }}</label>
</div>
</div>
</div>
</div>
</div>
{!! Form::close() !!}
@stop
@section('footer')
<a data-toggle="modal" data-target="#keyboardShortcutsModal"><i class="fa fa-keyboard-o"></i></a> &nbsp;
@stop
@section('shortcuts')
<dl class="dl-horizontal">
<dt><code>b</code></dt>
<dd>{{ trans('core::core.back to index', ['name' => 'menu']) }}</dd>
</dl>
@stop
@section('scripts')
<script>
$( document ).ready(function() {
$(document).keypressAction({
actions: [
{ key: 'b', route: "<?= route('admin.menu.menu.edit', [$menu->id]) ?>" }
]
});
$('input[type="checkbox"].flat-blue, input[type="radio"].flat-blue').iCheck({
checkboxClass: 'icheckbox_flat-blue',
radioClass: 'iradio_flat-blue'
});
$('input[type="checkbox"]').on('ifChecked', function(){
$(this).parent().find('input[type=hidden]').remove();
});
$('input[type="checkbox"]').on('ifUnchecked', function(){
var name = $(this).attr('name'),
input = '<input type="hidden" name="' + name + '" value="0" />';
$(this).parent().append(input);
});
$('.link-type-depended').hide();
$('.link-{{ $menuItem->link_type }}').fadeIn();
$('[name="link_type"]').iCheck({
checkboxClass: 'icheckbox_minimal',
radioClass: 'iradio_flat-blue'
}).on('ifChecked',function(){
$('.link-type-depended').hide();
$('.link-'+$(this).val()).fadeIn();
});
});
</script>
@stop
<div class="form-group{{ $errors->has("icon") ? ' has-error' : '' }}">
{!! Form::label("icon", trans('menu::menu-items.form.icon')) !!}
{!! Form::text("icon", old("icon"), ['class' => 'form-control', 'placeholder' => trans('menu::menu-items.form.icon')]) !!}
{!! $errors->first("icon", '<span class="help-block">:message</span>') !!}
</div>
<div class="form-group{{ $errors->has('class') ? ' has-error' : '' }}">
{!! Form::label('class', trans('menu::menu-items.form.class')) !!}
{!! Form::text('class', old('class'), ['class' => 'form-control']) !!}
{!! $errors->first('class', '<span class="help-block">:message</span>') !!}
</div>
<div class="form-group link-type-depended link-page">
<label for="page">{{ trans('menu::menu-items.form.page') }}</label>
<select class="form-control" name="page_id" id="page">
<option value=""></option>
<?php foreach ($pages as $page): ?>
<option value="{{ $page->id }}">{{ $page->title }}</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="parent_id">{{ trans('menu::menu-items.form.parent menu item') }}</label>
<select class="form-control" name="parent_id" id="parent_id">
<option value=""></option>
<?php foreach ($menuSelect as $parentMenuItemId => $parentMenuItemName): ?>
<option value="{{ $parentMenuItemId }}">{{ $parentMenuItemName }}</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="target">{{ trans('menu::menu-items.form.target') }}</label>
<select class="form-control" name="target" id="target">
<option value="_self">{{ trans('menu::menu-items.form.same tab') }}</option>
<option value="_blank">{{ trans('menu::menu-items.form.new tab') }}</option>
</select>
</div>
<div class='form-group{{ $errors->has("{$lang}[title]") ? ' has-error' : '' }}'>
{!! Form::label("{$lang}[title]", trans('menu::menu.form.title')) !!}
{!! Form::text("{$lang}[title]", old("{$lang}[title]"), ['class' => 'form-control', 'placeholder' => trans('menu::menu.form.title'), 'autofocus']) !!}
{!! $errors->first("{$lang}[title]", '<span class="help-block">:message</span>') !!}
</div>
<div class="form-group link-type-depended link-internal">
{!! Form::label("{$lang}[uri]", trans('menu::menu.form.uri')) !!}
<div class='input-group{{ $errors->has("{$lang}[uri]") ? ' has-error' : '' }}'>
<span class="input-group-addon">/{{ $lang }}/</span>
{!! Form::text("{$lang}[uri]", old("{$lang}[uri]"), ['class' => 'form-control', 'placeholder' => trans('menu::menu.form.uri')]) !!}
{!! $errors->first("{$lang}[uri]", '<span class="help-block">:message</span>') !!}
</div>
</div>
<div class="form-group{{ $errors->has("{$lang}[url]") ? ' has-error' : '' }} link-type-depended link-external">
{!! Form::label("{$lang}[url]", trans('menu::menu.form.url')) !!}
{!! Form::text("{$lang}[url]", old("{$lang}[url]"), ['class' => 'form-control', 'placeholder' => trans('menu::menu.form.url')]) !!}
{!! $errors->first("{$lang}[url]", '<span class="help-block">:message</span>') !!}
</div>
<div class="checkbox">
<label for="{{$lang}}[status]">
<input id="{{$lang}}[status]"
name="{{$lang}}[status]"
type="checkbox"
class="flat-blue"
value="1" />
{{ trans('menu::menu.form.status') }}
</label>
</div>
<div class="form-group{{ $errors->has('icon') ? ' has-error' : '' }}">
{!! Form::label('icon', trans('menu::menu-items.form.icon')) !!}
{!! Form::text('icon', old('icon', $menuItem->icon), ['class' => 'form-control', 'placeholder' => trans('menu::menu-items.form.icon')]) !!}
{!! $errors->first('icon', '<span class="help-block">:message</span>') !!}
</div>
<div class="form-group{{ $errors->has('class') ? ' has-error' : '' }}">
{!! Form::label('class', trans('menu::menu-items.form.class')) !!}
{!! Form::text('class', old('class',$menuItem->class), ['class' => 'form-control']) !!}
{!! $errors->first('class', '<span class="help-block">:message</span>') !!}
</div>
<div class="form-group link-type-depended link-page">
<label for="page">{{ trans('menu::menu-items.form.page') }}</label>
<select class="form-control" name="page_id" id="page">
<option value=""></option>
<?php foreach ($pages as $page): ?>
<option value="{{ $page->id }}" {{ $menuItem->page_id == $page->id ? 'selected' : '' }}>
{{ $page->title }}
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="parent_id">{{ trans('menu::menu-items.form.parent menu item') }}</label>
<select class="form-control" name="parent_id" id="parent_id">
<option value=""></option>
<?php foreach ($menuSelect as $parentMenuItemId => $parentMenuItemName): ?>
<?php if ($menuItem->id != $parentMenuItemId): ?>
<option value="{{ $parentMenuItemId }}" {{ $menuItem->parent_id == $parentMenuItemId ? ' selected' : '' }}>{{ $parentMenuItemName }}</option>
<?php endif; ?>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="target">{{ trans('menu::menu-items.form.target') }}</label>
<select class="form-control" name="target" id="target">
<option value="_self" {{ $menuItem->target === '_self' ? 'selected' : '' }}>{{ trans('menu::menu-items.form.same tab') }}</option>
<option value="_blank" {{ $menuItem->target === '_blank' ? 'selected' : '' }}>{{ trans('menu::menu-items.form.new tab') }}</option>
</select>
</div>
<div class='form-group{{ $errors->has("{$lang}[title]") ? ' has-error' : '' }}'>
{!! Form::label("{$lang}[title]", trans('menu::menu.form.title')) !!}
<?php $old = $menuItem->hasTranslation($lang) ? $menuItem->translate($lang)->title : '' ?>
{!! Form::text("{$lang}[title]", old("{$lang}[title]", $old), ['class' => 'form-control', 'placeholder' => trans('menu::menu.form.title')]) !!}
{!! $errors->first("{$lang}[title]", '<span class="help-block">:message</span>') !!}
</div>
<div class="form-group link-type-depended link-internal">
{!! Form::label("{$lang}[uri]", trans('menu::menu.form.uri')) !!}
<div class='input-group{{ $errors->has("{$lang}[uri]") ? ' has-error' : '' }}'>
<span class="input-group-addon">/{{ $lang }}/</span>
<?php $old = $menuItem->hasTranslation($lang) ? $menuItem->translate($lang)->uri : '' ?>
{!! Form::text("{$lang}[uri]", old("{$lang}[uri]", $old), ['class' => 'form-control', 'placeholder' => trans('menu::menu.form.uri')]) !!}
{!! $errors->first("{$lang}[uri]", '<span class="help-block">:message</span>') !!}
</div>
</div>
<div class="form-group{{ $errors->has("{$lang}[url]") ? ' has-error' : '' }} link-type-depended link-external">
{!! Form::label("{$lang}[url]", trans('menu::menu.form.url')) !!}
<?php $old = $menuItem->hasTranslation($lang) ? $menuItem->translate($lang)->url : '' ?>
{!! Form::text("{$lang}[url]", old("{$lang}[url]", $old), ['class' => 'form-control', 'placeholder' => trans('menu::menu.form.url')]) !!}
{!! $errors->first("{$lang}[url]", '<span class="help-block">:message</span>') !!}
</div>
<div class="checkbox">
<?php $old = $menuItem->hasTranslation($lang) ? $menuItem->translate($lang)->status : false ?>
<label for="{{$lang}}[status]">
<input id="{{$lang}}[status]"
name="{{$lang}}[status]"
type="checkbox"
class="flat-blue"
{{ (bool) $old ? 'checked' : '' }}
value="1" />
{{ trans('menu::menu.form.status') }}
</label>
</div>
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('menu::menu.titles.create menu') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ URL::route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('core::core.breadcrumb.home') }}</a></li>
<li><a href="{{ URL::route('admin.menu.menu.index') }}">{{ trans('menu::menu.breadcrumb.menu') }}</a></li>
<li>{{ trans('menu::menu.breadcrumb.create menu') }}</li>
</ol>
@stop
@section('styles')
@stop
@section('content')
{!! Form::open(['route' => ['admin.menu.menu.store'], 'method' => 'post']) !!}
<div class="row">
<div class="col-md-12">
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">{{ trans('core::core.title.translatable fields') }}</h3>
</div>
<div class="box-body">
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<?php $i = 0; ?>
<?php foreach (LaravelLocalization::getSupportedLocales() as $locale => $language): ?>
<?php $i++; ?>
<li class="{{ App::getLocale() == $locale ? 'active' : '' }}">
<a href="#tab_{{ $i }}" data-toggle="tab">{{ trans('core::core.tab.'. strtolower($language['name'])) }}</a>
</li>
<?php endforeach; ?>
</ul>
<div class="tab-content">
<?php $i = 0; ?>
<?php foreach (LaravelLocalization::getSupportedLocales() as $locale => $language): ?>
<?php $i++; ?>
<div class="tab-pane {{ App::getLocale() == $locale ? 'active' : '' }}" id="tab_{{ $i }}">
@include('menu::admin.menus.partials.create-trans-fields', ['lang' => $locale])
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">{{ trans('core::core.title.non translatable fields') }}</h3>
</div>
<div class="box-body">
@include('menu::admin.menus.partials.create-fields')
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat">{{ trans('core::core.button.create') }}</button>
<button class="btn btn-default btn-flat" name="button" type="reset">{{ trans('core::core.button.reset') }}</button>
<a class="btn btn-danger pull-right btn-flat" href="{{ URL::route('admin.menu.menu.index')}}"><i class="fa fa-times"></i> {{ trans('core::core.button.cancel') }}</a>
</div>
</div>
</div>
{!! Form::close() !!}
@stop
@section('footer')
<a data-toggle="modal" data-target="#keyboardShortcutsModal"><i class="fa fa-keyboard-o"></i></a> &nbsp;
@stop
@section('shortcuts')
<dl class="dl-horizontal">
<dt><code>b</code></dt>
<dd>{{ trans('core::core.back to index', ['name' => 'menu']) }}</dd>
</dl>
@stop
@section('scripts')
<script>
$( document ).ready(function() {
$(document).keypressAction({
actions: [
{ key: 'b', route: "<?= route('admin.menu.menu.index') ?>" }
]
});
$('input[type="checkbox"].flat-blue, input[type="radio"].flat-blue').iCheck({
checkboxClass: 'icheckbox_flat-blue',
radioClass: 'iradio_flat-blue'
});
});
</script>
@stop
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('menu::menu.titles.edit menu') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ URL::route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('core::core.breadcrumb.home') }}</a></li>
<li><a href="{{ URL::route('admin.menu.menu.index') }}">{{ trans('menu::menu.breadcrumb.menu') }}</a></li>
<li>{{ trans('menu::menu.breadcrumb.edit menu') }}</li>
</ol>
@stop
@section('styles')
<link href="{!! Module::asset('menu:css/nestable.css') !!}" rel="stylesheet" type="text/css" />
@stop
@section('content')
{!! Form::open(['route' => ['admin.menu.menu.update', $menu->id], 'method' => 'put']) !!}
<div class="row">
<div class="col-md-6">
<div class="row">
<div class="btn-group pull-right" style="margin: 0 15px 15px 0;">
<a href="{{ URL::route('dashboard.menuitem.create', [$menu->id]) }}" class="btn btn-primary btn-flat">
<i class="fa fa-pencil"></i> {{ trans('menu::menu.button.create menu item') }}
</a>
</div>
</div>
<div class="box box-primary" style="overflow: hidden;">
<div class="box-body">
{!! $menuStructure !!}
</div>
</div>
</div>
<div class="col-md-6">
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">{{ trans('core::core.title.translatable fields') }}</h3>
</div>
<div class="box-body">
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<?php $i = 0; ?>
<?php foreach (LaravelLocalization::getSupportedLocales() as $locale => $language): ?>
<?php $i++; ?>
<li class="{{ App::getLocale() == $locale ? 'active' : '' }}">
<a href="#tab_{{ $i }}" data-toggle="tab">{{ trans('core::core.tab.'. strtolower($language['name'])) }}</a>
</li>
<?php endforeach; ?>
</ul>
<div class="tab-content">
<?php $i = 0; ?>
<?php foreach (LaravelLocalization::getSupportedLocales() as $locale => $language): ?>
<?php $i++; ?>
<div class="tab-pane {{ App::getLocale() == $locale ? 'active' : '' }}" id="tab_{{ $i }}">
@include('menu::admin.menus.partials.edit-trans-fields', ['lang' => $locale])
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">{{ trans('core::core.title.non translatable fields') }}</h3>
</div>
<div class="box-body">
@include('menu::admin.menus.partials.edit-fields')
</div>
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat">{{ trans('core::core.button.update') }}</button>
<button class="btn btn-default btn-flat" name="button" type="reset">{{ trans('core::core.button.reset') }}</button>
<a class="btn btn-danger pull-right btn-flat" href="{{ URL::route('admin.menu.menu.index')}}"><i class="fa fa-times"></i> {{ trans('core::core.button.cancel') }}</a>
</div>
</div>
</div>
{!! Form::close() !!}
@stop
@section('footer')
<a data-toggle="modal" data-target="#keyboardShortcutsModal"><i class="fa fa-keyboard-o"></i></a> &nbsp;
@stop
@section('shortcuts')
<dl class="dl-horizontal">
<dt><code>c</code></dt>
<dd>{{ trans('menu::menu.titles.create menu item') }}</dd>
<dt><code>b</code></dt>
<dd>{{ trans('menu::menu.navigation.back to index') }}</dd>
</dl>
@stop
@section('scripts')
<script>
$( document ).ready(function() {
$(document).keypressAction({
actions: [
{ key: 'c', route: "<?= route('dashboard.menuitem.create', [$menu->id]) ?>" },
{ key: 'b', route: "<?= route('admin.menu.menu.index') ?>" }
]
});
$('input[type="checkbox"].flat-blue, input[type="radio"].flat-blue').iCheck({
checkboxClass: 'icheckbox_flat-blue',
radioClass: 'iradio_flat-blue'
});
$('input[type="checkbox"]').on('ifChecked', function(){
$(this).parent().find('input[type=hidden]').remove();
});
$('input[type="checkbox"]').on('ifUnchecked', function(){
var name = $(this).attr('name'),
input = '<input type="hidden" name="' + name + '" value="0" />';
$(this).parent().append(input);
});
});
</script>
<script src="{!! Module::asset('menu:js/jquery.nestable.js') !!}"></script>
<script>
$( document ).ready(function() {
$('.dd').nestable();
$('.dd').on('change', function() {
var data = $('.dd').nestable('serialize');
$.ajax({
type: 'POST',
url: '{{ route('api.menuitem.update') }}',
data: {'menu': JSON.stringify(data), '_token': '<?php echo csrf_token(); ?>'},
dataType: 'json',
success: function(data) {
},
error:function (xhr, ajaxOptions, thrownError){
}
});
});
});
</script>
<script>
$( document ).ready(function() {
$('.jsDeleteMenuItem').on('click', function(e) {
var self = $(this),
menuItemId = self.data('item-id');
$.ajax({
type: 'POST',
url: '{{ route('api.menuitem.delete') }}',
data: {
_token: '{{ csrf_token() }}',
menuitem: menuItemId
},
success: function(data) {
if (! data.errors) {
var elem = self.closest('li');
elem.fadeOut()
setTimeout(function(){
elem.remove()
}, 300);
}
}
});
});
});
</script>
@stop
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('menu::menu.titles.menu') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ URL::route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('core::core.breadcrumb.home') }}</a></li>
<li class="active">{{ trans('menu::menu.breadcrumb.menu') }}</li>
</ol>
@stop
@section('content')
<div class="row">
<div class="col-md-12">
<div class="row">
<div class="btn-group pull-right" style="margin: 0 15px 15px 0;">
<a href="{{ URL::route('admin.menu.menu.create') }}" class="btn btn-primary btn-flat">
<i class="fa fa-pencil"></i> {{ trans('menu::menu.button.create menu') }}
</a>
</div>
</div>
<div class="box box-primary">
<div class="box-header">
</div>
<!-- /.box-header -->
<div class="box-body">
<table class="data-table table table-bordered table-hover">
<thead>
<tr>
<th>{{ trans('menu::menu.table.name') }}</th>
<th>{{ trans('menu::menu.table.title') }}</th>
<th data-sortable="false">{{ trans('core::core.table.actions') }}</th>
</tr>
</thead>
<tbody>
<?php if (isset($menus)): ?>
<?php foreach ($menus as $menu): ?>
<tr>
<td>
<a href="{{ URL::route('admin.menu.menu.edit', [$menu->id]) }}">
{{ $menu->name }}
</a>
</td>
<td>
<a href="{{ URL::route('admin.menu.menu.edit', [$menu->id]) }}">
{{ $menu->title }}
</a>
</td>
<td>
<div class="btn-group">
<a href="{{ URL::route('admin.menu.menu.edit', [$menu->id]) }}" class="btn btn-default btn-flat"><i class="glyphicon glyphicon-pencil"></i></a>
<button class="btn btn-danger btn-flat" data-toggle="modal" data-target="#confirmation-{{ $menu->id }}"><i class="glyphicon glyphicon-trash"></i></button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
<tfoot>
<tr>
<th>{{ trans('menu::menu.table.name') }}</th>
<th>{{ trans('menu::menu.table.title') }}</th>
<th>{{ trans('core::core.table.actions') }}</th>
</tr>
</tfoot>
</table>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
</div>
</div>
<?php if (isset($menus)): ?>
<?php foreach ($menus as $menu): ?>
<!-- Modal -->
<div class="modal fade modal-danger" id="confirmation-{{ $menu->id }}" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="myModalLabel">{{ trans('core::core.modal.title') }}</h4>
</div>
<div class="modal-body">
{{ trans('core::core.modal.confirmation-message') }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline btn-flat" data-dismiss="modal">{{ trans('core::core.button.cancel') }}</button>
{!! Form::open(['route' => ['admin.menu.menu.destroy', $menu->id], 'method' => 'delete', 'class' => 'pull-left']) !!}
<button type="submit" class="btn btn-outline btn-flat"><i class="glyphicon glyphicon-trash"></i> {{ trans('core::core.button.delete') }}</button>
{!! Form::close() !!}
</div>
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
@stop
@section('footer')
<a data-toggle="modal" data-target="#keyboardShortcutsModal"><i class="fa fa-keyboard-o"></i></a> &nbsp;
@stop
@section('shortcuts')
<dl class="dl-horizontal">
<dt><code>c</code></dt>
<dd>{{ trans('menu::menu.titles.create menu') }}</dd>
</dl>
@stop
@section('scripts')
<?php $locale = App::getLocale(); ?>
<script type="text/javascript">
$( document ).ready(function() {
$(document).keypressAction({
actions: [
{ key: 'c', route: "<?= route('admin.menu.menu.create') ?>" }
]
});
});
$(function () {
$('.data-table').dataTable({
"paginate": true,
"lengthChange": true,
"filter": true,
"sort": true,
"info": true,
"autoWidth": true,
"order": [[ 0, "asc" ]],
"language": {
"url": '<?php echo Module::asset("core:js/vendor/datatables/{$locale}.json") ?>'
}
});
});
</script>
@stop
<div class='form-group{{ $errors->has('name') ? ' has-error' : '' }}'>
{!! Form::label('name', trans('menu::menu.form.name')) !!}
{!! Form::text('name', old('name'), ['class' => 'form-control', 'placeholder' => trans('menu::menu.form.name')]) !!}
{!! $errors->first('Name', '<span class="help-block">:message</span>') !!}
</div>
<div class='form-group{{ $errors->has("{$lang}[title]") ? ' has-error' : '' }}'>
{!! Form::label("{$lang}[title]", trans('menu::menu.form.title')) !!}
{!! Form::text("{$lang}[title]", old("$lang.title"), ['class' => 'form-control', 'placeholder' => trans('menu::menu.form.title')]) !!}
{!! $errors->first("{$lang}[title]", '<span class="help-block">:message</span>') !!}
</div>
<div class="checkbox">
<label for="{{$lang}}[status]">
<input id="{{$lang}}[status]"
name="{{$lang}}[status]"
type="checkbox"
class="flat-blue"
{{ (is_null(old("$lang.status"))) ?: 'checked' }}
value="1" />
{{ trans('menu::menu.form.status') }}
</label>
</div>
<div class='form-group{{ $errors->has('name') ? ' has-error' : '' }}'>
{!! Form::label('name', trans('menu::menu.form.name')) !!}
{!! Form::text('name', old('name', $menu->name), ['class' => 'form-control', 'placeholder' => trans('menu::menu.form.name')]) !!}
{!! $errors->first('name', '<span class="help-block">:message</span>') !!}
</div>
<div class='form-group{{ $errors->has("{$lang}[title]") ? ' has-error' : '' }}'>
{!! Form::label("{$lang}[title]", trans('menu::menu.form.title')) !!}
<?php $old = $menu->hasTranslation($lang) ? $menu->translate($lang)->title : '' ?>
{!! Form::text("{$lang}[title]", old("{$lang}[title]", $old), ['class' => 'form-control', 'placeholder' => trans('menu::menu.form.title')]) !!}
{!! $errors->first("{$lang}[title]", '<span class="help-block">:message</span>') !!}
</div>
<div class="checkbox">
<?php $old = $menu->hasTranslation($lang) ? $menu->translate($lang)->status : false ?>
<label for="{{$lang}}[status]">
<input id="{{$lang}}[status]"
name="{{$lang}}[status]"
type="checkbox"
class="flat-blue"
{{ ((bool) $old) ? 'checked' : '' }}
value="1" />
{{ trans('menu::menu.form.status') }}
</label>
</div>
<?php
namespace Modules\Menu\Services;
use Modules\Menu\Repositories\MenuItemRepository;
use Modules\Page\Repositories\PageRepository;
final class MenuItemUriGenerator
{
/**
* @var MenuItemRepository
*/
private $menuItem;
/**
* @var PageRepository
*/
private $page;
public function __construct(MenuItemRepository $menuItem, PageRepository $page)
{
$this->menuItem = $menuItem;
$this->page = $page;
}
/**
* Generate a URI based of the given page and and the parent id recursively
* @param string $pageId
* @param string $parentId
* @param string $lang
* @return string
*/
public function generateUri($pageId, $parentId, $lang)
{
$linkPathArray = [];
$linkPathArray[] = $this->getPageSlug($pageId, $lang);
if ($parentId !== '') {
$hasParentItem = !(is_null($parentId)) ? true : false;
while ($hasParentItem) {
$parentItemId = isset($parentItem) ? $parentItem->parent_id : $parentId;
$parentItem = $this->menuItem->find($parentItemId);
if ((int) $parentItem->is_root === 0) {
if ($parentItem->page_id != '') {
$linkPathArray[] = $this->getPageSlug($parentItem->page_id, $lang);
} else {
$linkPathArray[] = $this->getParentUri($parentItem, $linkPathArray);
}
$hasParentItem = !is_null($parentItem->parent_id) ? true : false;
} else {
$hasParentItem = false;
}
}
}
$parentLinkPath = implode('/', array_reverse($linkPathArray));
return $parentLinkPath;
}
/**
* Get page slug
* @param $id
* @param $lang
* @return string
*/
private function getPageSlug($id, $lang)
{
$page = $this->page->find($id);
$translation = $page->translate($lang);
if ($translation === null) {
return $page->translate(config('app.fallback_locale'))->slug;
}
return $translation->slug;
}
/**
* Get parent uri
*
* @params $pageId, $lang
* @return string
*/
private function getParentUri($item, $linkPathArray)
{
if ($item->uri === null) {
return implode('/', $linkPathArray);
}
return $item->uri;
}
}
<?php
namespace Modules\Menu\Services;
use Modules\Menu\Entities\Menuitem;
use Modules\Menu\Repositories\MenuItemRepository;
class MenuOrdener
{
/**
* @var MenuItemRepository
*/
private $menuItemRepository;
/**
* @param MenuItemRepository $menuItem
*/
public function __construct(MenuItemRepository $menuItem)
{
$this->menuItemRepository = $menuItem;
}
/**
* @param $data
*/
public function handle($data)
{
$data = $this->convertToArray(json_decode($data));
foreach ($data as $position => $item) {
$this->order($position, $item);
}
}
/**
* Order recursively the menu items
* @param int $position
* @param array $item
*/
private function order($position, $item)
{
$menuItem = $this->menuItemRepository->find($item['id']);
if (0 === $position && false === $menuItem->isRoot()) {
return;
}
$this->savePosition($menuItem, $position);
$this->makeItemChildOf($menuItem, null);
if ($this->hasChildren($item)) {
$this->handleChildrenForParent($menuItem, $item['children']);
}
}
/**
* @param Menuitem $parent
* @param array $children
*/
private function handleChildrenForParent(Menuitem $parent, array $children)
{
foreach ($children as $position => $item) {
$menuItem = $this->menuItemRepository->find($item['id']);
$this->savePosition($menuItem, $position);
$this->makeItemChildOf($menuItem, $parent->id);
if ($this->hasChildren($item)) {
$this->handleChildrenForParent($menuItem, $item['children']);
}
}
}
/**
* Save the given position on the menu item
* @param object $menuItem
* @param int $position
*/
private function savePosition($menuItem, $position)
{
$this->menuItemRepository->update($menuItem, compact('position'));
}
/**
* Check if the item has children
*
* @param array $item
* @return bool
*/
private function hasChildren($item)
{
return isset($item['children']);
}
/**
* Set the given parent id on the given menu item
*
* @param object $item
* @param int $parent_id
*/
private function makeItemChildOf($item, $parent_id)
{
$this->menuItemRepository->update($item, compact('parent_id'));
}
/**
* Convert the object to array
* @param $data
* @return array
*/
private function convertToArray($data)
{
$data = json_decode(json_encode($data), true);
return $data;
}
}
<?php
namespace Modules\Menu\Services;
use Illuminate\Support\Facades\URL;
class MenuRenderer
{
/**
* @var int Id of the menu to render
*/
protected $menuId;
/**
* @var string
*/
private $startTag = '<div class="dd">';
/**
* @var string
*/
private $endTag = '</div>';
/**
* @var string
*/
private $menu = '';
/**
* @param $menuId
* @param $menuItems
* @return string
*/
public function renderForMenu($menuId, $menuItems)
{
$this->menuId = $menuId;
$this->menu .= $this->startTag;
$this->generateHtmlFor($menuItems);
$this->menu .= $this->endTag;
return $this->menu;
}
/**
* Generate the html for the given items
* @param $items
*/
private function generateHtmlFor($items)
{
$this->menu .= '<ol class="dd-list">';
foreach ($items as $item) {
$this->menu .= "<li class=\"dd-item\" data-id=\"{$item->id}\">";
$editLink = URL::route('dashboard.menuitem.edit', [$this->menuId, $item->id]);
$style = $item->isRoot() ? 'none' : 'inline';
$this->menu .= <<<HTML
<div class="btn-group" role="group" aria-label="Action buttons" style="display: {$style}">
<a class="btn btn-sm btn-info" style="float:left;" href="{$editLink}">
<i class="fa fa-pencil"></i>
</a>
<a class="btn btn-sm btn-danger jsDeleteMenuItem" style="float:left; margin-right: 15px;" data-item-id="{$item->id}">
<i class="fa fa-times"></i>
</a>
</div>
HTML;
$handleClass = $item->isRoot() ? 'dd-handle-root' : 'dd-handle';
if (isset($item->icon) && $item->icon != '') {
$this->menu .= "<div class=\"{$handleClass}\"><i class=\"{$item->icon}\" ></i> {$item->title}</div>";
} else {
$this->menu .= "<div class=\"{$handleClass}\">{$item->title}</div>";
}
if ($this->hasChildren($item)) {
$this->generateHtmlFor($item->items);
}
$this->menu .= '</li>';
}
$this->menu .= '</ol>';
}
/**
* @param $item
* @return bool
*/
private function hasChildren($item)
{
return count($item->items);
}
}
<?php
namespace Modules\Menu\Sidebar;
use Maatwebsite\Sidebar\Group;
use Maatwebsite\Sidebar\Item;
use Maatwebsite\Sidebar\Menu;
use Modules\User\Contracts\Authentication;
class SidebarExtender implements \Maatwebsite\Sidebar\SidebarExtender
{
/**
* @var Authentication
*/
protected $auth;
/**
* @param Authentication $auth
*
* @internal param Guard $guard
*/
public function __construct(Authentication $auth)
{
$this->auth = $auth;
}
/**
* @param Menu $menu
*
* @return Menu
*/
public function extendWith(Menu $menu)
{
$menu->group(trans('core::sidebar.content'), function (Group $group) {
$group->weight(90);
$group->item(trans('menu::menu.title'), function (Item $item) {
$item->weight(3);
$item->icon('fa fa-bars');
$item->route('admin.menu.menu.index');
$item->authorize(
$this->auth->hasAccess('menu.menus.index')
);
});
});
return $menu;
}
}
<?php
namespace Modules\Menu\Tests;
use Faker\Factory;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Maatwebsite\Sidebar\SidebarServiceProvider;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
use Mcamara\LaravelLocalization\LaravelLocalizationServiceProvider;
use Modules\Core\Providers\CoreServiceProvider;
use Modules\Menu\Providers\MenuServiceProvider;
use Modules\Menu\Repositories\MenuItemRepository;
use Modules\Menu\Repositories\MenuRepository;
use Modules\Page\Providers\PageServiceProvider;
use Modules\Tag\Providers\TagServiceProvider;
use Nwidart\Modules\LaravelModulesServiceProvider;
use Orchestra\Testbench\TestCase;
abstract class BaseMenuTest extends TestCase
{
/**
* @var MenuRepository
*/
protected $menu;
/**
* @var MenuItemRepository
*/
protected $menuItem;
/**
*
*/
public function setUp()
{
parent::setUp();
$this->resetDatabase();
$this->menu = app(MenuRepository::class);
$this->menuItem = app(MenuItemRepository::class);
}
protected function getPackageProviders($app)
{
return [
LaravelModulesServiceProvider::class,
CoreServiceProvider::class,
TagServiceProvider::class,
PageServiceProvider::class,
MenuServiceProvider::class,
LaravelLocalizationServiceProvider::class,
SidebarServiceProvider::class,
];
}
protected function getPackageAliases($app)
{
return [
'Eloquent' => Model::class,
'LaravelLocalization' => LaravelLocalization::class,
];
}
protected function getEnvironmentSetUp($app)
{
$app['path.base'] = __DIR__ . '/..';
$app['config']->set('database.default', 'sqlite');
$app['config']->set('database.connections.sqlite', array(
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
));
$app['config']->set('translatable.locales', ['en', 'fr']);
}
private function resetDatabase()
{
// Makes sure the migrations table is created
$this->artisan('migrate', [
'--database' => 'sqlite',
]);
// We empty all tables
$this->artisan('migrate:reset', [
'--database' => 'sqlite',
]);
// Migrate
$this->artisan('migrate', [
'--database' => 'sqlite',
]);
$this->artisan('migrate', [
'--database' => 'sqlite',
'--path' => 'Modules/Page/Database/Migrations',
]);
$this->artisan('migrate', [
'--database' => 'sqlite',
'--path' => 'Modules/Tag/Database/Migrations',
]);
}
public function createMenu($name, $title)
{
$data = [
'name' => $name,
'primary' => true,
'en' => [
'title' => $title,
'status' => 1,
],
];
return $this->menu->create($data);
}
/**
* Create a menu item for the given menu and position
*
* @param int $menuId
* @param int $position
* @param null $parentId
* @return object
*/
protected function createMenuItemForMenu($menuId, $position, $parentId = null)
{
$faker = Factory::create();
$title = implode(' ', $faker->words(3));
$slug = Str::slug($title);
$data = [
'menu_id' => $menuId,
'position' => $position,
'parent_id' => $parentId,
'target' => '_self',
'module_name' => 'blog',
'en' => [
'status' => 1,
'title' => $title,
'uri' => $slug,
],
'fr' => [
'status' => 1,
'title' => $title,
'uri' => $slug,
],
];
return $this->menuItem->create($data);
}
}
<?php
namespace Modules\Menu\Tests;
class EloquentMenuItemRepositoryTest extends BaseMenuTest
{
public function setUp()
{
parent::setUp();
}
/**
* New menu item should be created
* @test
*/
public function it_should_create_menu_item_as_root()
{
$menu = $this->createMenu('second', 'Second Menu');
$data = [
'menu_id' => $menu->id,
'position' => 0,
'target' => '_self',
'module_name' => 'blog',
'en' => [
'status' => 1,
'title' => 'First Menu Item',
'uri' => 'item1',
],
'fr' => [
'status' => 1,
'title' => 'Premier item de menu',
'uri' => 'item1',
],
];
$menuItem = $this->menuItem->create($data);
$this->assertEquals(null, $menuItem->parent_id);
}
/** @test */
public function it_destroys_menu_item()
{
// Prepare
$menu1 = $this->createMenu('main', 'Main menu');
$item1 = $this->createMenuItemForMenu($menu1->id, 0);
$item2 = $this->createMenuItemForMenu($menu1->id, 1);
$item3 = $this->createMenuItemForMenu($menu1->id, 3);
$menu2 = $this->createMenu('footer', 'Footer menu');
$secondaryItem1 = $this->createMenuItemForMenu($menu2->id, 0);
$secondaryItem2 = $this->createMenuItemForMenu($menu2->id, 1);
$secondaryItem3 = $this->createMenuItemForMenu($menu2->id, 3);
$this->assertEquals(6, $this->menuItem->all()->count());
$this->menuItem->destroy($item2);
$this->assertEquals(5, $this->menuItem->all()->count());
}
}
<?php
namespace Modules\Menu\Tests;
use Modules\Menu\Services\MenuItemUriGenerator;
use Modules\Page\Repositories\PageRepository;
class MenuItemUriGeneratorTest extends BaseMenuTest
{
/**
* @var PageRepository
*/
private $page;
/**
* @var MenuItemUriGenerator
*/
private $menuItemUriGenerator;
public function setUp()
{
parent::setUp();
$this->page = app(PageRepository::class);
$this->menuItemUriGenerator = app(MenuItemUriGenerator::class);
}
/** @test */
public function it_generates_basic_uri_without_parent()
{
$this->page->create([
'is_home' => 1,
'template' => 'default',
'en' => [
'title' => 'Awesome Page',
'slug' => 'awesome-page',
'body' => 'My Page Body',
],
]);
self::assertEquals('awesome-page', $this->menuItemUriGenerator->generateUri(1, '', 'en'));
}
/** @test */
public function it_generates_uri_with_the_parents_slug()
{
$this->page->create([
'is_home' => 1,
'template' => 'default',
'en' => [
'title' => 'Awesome Page',
'slug' => 'awesome-page',
'body' => 'My Page Body',
],
]);
$this->page->create([
'is_home' => 0,
'template' => 'default',
'en' => [
'title' => 'About',
'slug' => 'about',
'body' => 'My Page Body',
],
]);
$menu = $this->createMenu('main', 'Main');
$data = [
'menu_id' => $menu->id,
'position' => 0,
'target' => '_self',
'page_id' => 1,
'en' => [
'status' => 1,
'title' => 'First Menu Item',
'uri' => 'awesome-page',
],
];
$this->menuItem->create($data);
self::assertEquals('awesome-page/about', $this->menuItemUriGenerator->generateUri(2, '1', 'en'));
}
/** @test */
public function it_generates_uri_with_multiple_parents()
{
$this->page->create([
'is_home' => 1,
'template' => 'default',
'en' => [
'title' => 'Awesome Page',
'slug' => 'awesome-page',
'body' => 'My Page Body',
],
]);
$this->page->create([
'is_home' => 0,
'template' => 'default',
'en' => [
'title' => 'Mid Page',
'slug' => 'mid-page',
'body' => 'My Page Body',
],
]);
$this->page->create([
'is_home' => 0,
'template' => 'default',
'en' => [
'title' => 'About',
'slug' => 'about',
'body' => 'My Page Body',
],
]);
$menu = $this->createMenu('main', 'Main');
$data = [
'menu_id' => $menu->id,
'position' => 0,
'target' => '_self',
'page_id' => 1,
'en' => [
'status' => 1,
'title' => 'First Menu Item',
'uri' => 'awesome-page',
],
];
$this->menuItem->create($data);
$data = [
'menu_id' => $menu->id,
'position' => 0,
'target' => '_self',
'page_id' => 2,
'parent_id' => 1,
'en' => [
'status' => 1,
'title' => 'Second Menu Item',
'uri' => 'awesome-page/mid-page',
],
];
$this->menuItem->create($data);
self::assertEquals('awesome-page/mid-page/about', $this->menuItemUriGenerator->generateUri(3, '2', 'en'));
}
/** @test */
public function it_generates_a_uri_if_parent_isnt_a_page()
{
$this->page->create([
'is_home' => 0,
'template' => 'default',
'en' => [
'title' => 'About',
'slug' => 'about',
'body' => 'My Page Body',
],
]);
$menu = $this->createMenu('main', 'Main');
$data = [
'menu_id' => $menu->id,
'position' => 0,
'target' => '_self',
'page_id' => null,
'en' => [
'status' => 1,
'title' => 'First Menu Item',
'uri' => 'awesome-page',
],
];
$this->menuItem->create($data);
self::assertEquals('awesome-page/about', $this->menuItemUriGenerator->generateUri(1, '1', 'en'));
}
}
<?php
namespace Modules\Menu\Tests;
class MenuOrdenerTest extends BaseMenuTest
{
/**
* @var \Modules\Menu\Services\MenuOrdener
*/
protected $menuOrdener;
public function setUp()
{
parent::setUp();
$this->createMenu('main', 'Main Menu');
$this->menuOrdener = app('Modules\Menu\Services\MenuOrdener');
}
/** @test */
public function it_makes_item_child_of()
{
// Prepare
$menu = $this->createMenu('main', 'Main Menu');
$menuItem1 = $this->createMenuItemForMenu($menu->id, 1);
$menuItem2 = $this->createMenuItemForMenu($menu->id, 2);
$request = [
1 => [
'id' => $menuItem1->id,
'children' => [
0 => [
'id' => $menuItem2->id,
],
],
],
];
$request = json_encode($request);
// Run
$this->menuOrdener->handle($request);
// Assert
$child = $this->menuItem->find($menuItem2->id);
$this->assertEquals($menuItem1->id, $child->parent_id);
}
/** @test */
public function it_makes_items_child_of_recursively()
{
// Prepare
$menu = $this->createMenu('main', 'Main Menu');
$menuItem1 = $this->createMenuItemForMenu($menu->id, 0);
$menuItem2 = $this->createMenuItemForMenu($menu->id, 0, $menuItem1->id);
$menuItem3 = $this->createMenuItemForMenu($menu->id, 1, $menuItem1->id);
$request = [
1 => [
'id' => $menuItem1->id,
'children' => [
0 => [
'id' => $menuItem2->id,
'children' => [
0 => [
'id' => $menuItem3->id,
],
],
],
],
],
];
$request = json_encode($request);
// Run
$this->menuOrdener->handle($request);
// Assert
$child = $this->menuItem->find($menuItem2->id);
$this->assertEquals($menuItem1->id, $child->parent_id);
$child2 = $this->menuItem->find($menuItem3->id);
$this->assertEquals($menuItem2->id, $child2->parent_id);
}
/** @test */
public function it_reorders_items()
{
// Prepare
$menu = $this->createMenu('main', 'Main Menu');
$menuItem1 = $this->createMenuItemForMenu($menu->id, 1);
$menuItem2 = $this->createMenuItemForMenu($menu->id, 2);
$request = [
1 => [
'id' => $menuItem2->id,
],
2 => [
'id' => $menuItem1->id,
],
];
$request = json_encode($request);
// Run
$this->menuOrdener->handle($request);
// Assert
$item1 = $this->menuItem->find($menuItem1->id);
$this->assertEquals(2, $item1->position);
$item2 = $this->menuItem->find($menuItem2->id);
$this->assertEquals(1, $item2->position);
}
/** @test */
public function it_moves_items_to_root()
{
// Prepare
$menu = $this->createMenu('main', 'Main Menu');
$menuItem1 = $this->createMenuItemForMenu($menu->id, 0);
$menuItem2 = $this->createMenuItemForMenu($menu->id, 1, $menuItem1->id);
$request = [
0 => [
'id' => $menuItem1->id,
],
1 => [
'id' => $menuItem2->id,
],
];
$request = json_encode($request);
// Run
$this->menuOrdener->handle($request);
// Assert
$item1 = $this->menuItem->find($menuItem1->id);
$this->assertEquals(null, $item1->parent_id);
$item2 = $this->menuItem->find($menuItem2->id);
$this->assertEquals(null, $item2->parent_id);
}
}
<?php
namespace Modules\Menu\Tests;
class MenuRepositoryTest extends BaseMenuTest
{
/** @test */
public function it_creates_menu()
{
$menu = $this->createMenu('main', 'Main Menu');
$this->assertEquals(1, $this->menu->find($menu->id)->count());
$this->assertEquals($menu->name, $this->menu->find($menu->id)->name);
}
public function it_should_create_root_item_when_creating_new_menu()
{
$menu = $this->createMenu('main', 'Main Menu');
$items = $this->menuItem->allRootsForMenu($menu->id);
}
}
url: https://github.com/AsgardCms/Menu
versions:
"2.0@unreleased":
added:
- Laravel 5.2 compatibility
changed:
- Using new more flexible way of handle permissions via middleware
deleted:
- Removing laracasts/flash dependency
"1.17.0":
changed:
- Use <code>$router</code> variable in routes file
"1.16.0":
changed:
- Using new authorise middleware for more flexibility
- Added migration to alter menu item icon field so that Pingpong MenuItem correctly renders empty icon
"1.15.0":
changed:
- Submenu items now have their target attribute set
"1.14.0":
changed:
- Removed language files, they are now in the translation module
"1.13.0":
changed:
- Manual inclusing of iCheck css file has been removed
- Use the new datatables way of marking column not sortable
"1.12.0":
changed:
- Fix submenu items not being linked
- Fixing display of collapse menu item without sub-items
- Don't allow menu items to be placed before the root item
"1.11.0":
changed:
- Better German translations
- Fixing duplication of sub menu items
"1.10.0":
added:
- Added Russian translations
changed:
- Improving german translations
"1.9.0":
added:
- Icon on menu
"1.8.0":
added:
- Dutch and Portuguese language
"1.7.0":
changes:
- Root menu item now is set in all available languages instead of hardcoded
"1.6.0":
added:
- Spanish translations
"1.5.0":
changed:
- Added Brazilian Portuguese translations
"1.4.0":
changed:
- Autofocus the <code>title</code> field
"1.3.0":
changed:
- Using manual route binding.
- Setting route parameter to singular.
"1.2.0":
changed:
- Use the flash helper function in controller
- Use manual route definition over the route resource shortcut
"1.1.1":
removed:
- Removed unused loaded files
"1.1.0":
changed:
- Adding table engine into migrations
"1.0.3":
added:
- Using new sidebar extender class
removed:
- Old SidebarViewComposer
"1.0.2":
changed:
- Adding changelog file
"1.0.1":
changed:
- Using tagged versions of modules
"1.0.0":
changed:
- Initial release
machine:
php:
version: 5.6.5
{
"name": "asgardcms/menu-module",
"type": "asgard-module",
"description": "Menu module for AsgardCMS. Handles all the menus.",
"keywords": [
"asgardcms",
"menu",
"tree",
"nested sets"
],
"license": "MIT",
"authors": [
{
"name": "Nicolas Widart",
"email": "info@asgardcms.com",
"role": "Developer"
}
],
"support": {
"email": "support@asgardcms.com",
"issues": "https://github.com/AsgardCms/Menu/issues",
"source": "https://github.com/AsgardCms/Menu"
},
"require": {
"php": ">=5.6",
"composer/installers": "~1.0",
"nwidart/laravel-menus": "~0.2",
"typicms/nestablecollection": "1.1.1",
"asgardcms/core-module": "~2.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0",
"orchestra/testbench": "3.3.*",
"fzaninotto/faker": "1.4.0",
"doctrine/dbal": "~2.5",
"asgardcms/page-module": "~2.0"
},
"autoload-dev": {
"psr-4": {
"Modules\\Menu\\": ".",
"Modules\\": "Modules/"
}
},
"extra": {
"branch-alias": {
"dev-2.0": "2.0.x-dev"
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
{
"name": "Menu",
"alias": "menu",
"description": "Managing menus.",
"keywords": [
],
"version": "2.0.0",
"active": 1,
"order": 1,
"providers": [
"Modules\\Menu\\Providers\\RouteServiceProvider",
"Modules\\Menu\\Providers\\MenuServiceProvider",
"Modules\\Menu\\Providers\\EventServiceProvider",
"Nwidart\\Menus\\MenusServiceProvider"
],
"aliases": {
"Menu": "Nwidart\\Menus\\Facades\\Menu",
"MenuDirective": "Modules\\Menu\\Blade\\Facades\\MenuDirective"
},
"files": []
}
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix=".php">./Tests/</directory>
</testsuite>
</testsuites>
<filter>
<blacklist>
<directory>vendor/</directory>
<directory>Modules/</directory>
</blacklist>
</filter>
<logging>
<log type="tap" target="build/report.tap"/>
<log type="junit" target="build/report.junit.xml"/>
<log type="coverage-html" target="build/coverage" charset="UTF-8" yui="true" highlight="true"/>
<log type="coverage-text" target="build/coverage.txt"/>
<log type="coverage-clover" target="build/logs/clover.xml"/>
</logging>
</phpunit>
# Menu Module
[![Latest Version](https://img.shields.io/github/release/asgardcms/menu.svg?style=flat-square)](https://github.com/asgardcms/menu/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
[![Quality Score](https://img.shields.io/scrutinizer/g/asgardcms/menu.svg?style=flat-square)](https://scrutinizer-ci.com/g/asgardcms/menu)
[![SensioLabs Insight](https://img.shields.io/sensiolabs/i/f6ca068c-662b-4606-9bee-262abc858f02.svg)](https://insight.sensiolabs.com/projects/f6ca068c-662b-4606-9bee-262abc858f02)
[![CodeClimate](https://img.shields.io/codeclimate/github/AsgardCms/Menu.svg)](https://codeclimate.com/github/AsgardCms/Menu)
[![Total Downloads](https://img.shields.io/packagist/dd/asgardcms/menu-module.svg?style=flat-square)](https://packagist.org/packages/asgardcms/menu-module)
[![Total Downloads](https://img.shields.io/packagist/dm/asgardcms/menu-module.svg?style=flat-square)](https://packagist.org/packages/asgardcms/menu-module)
[![Total Downloads](https://img.shields.io/packagist/dt/asgardcms/menu-module.svg?style=flat-square)](https://packagist.org/packages/asgardcms/menu-module)
[![Slack](http://slack.asgardcms.com/badge.svg)](http://slack.asgardcms.com/)
| Branch | Travis-ci |
| ---------------- | --------------- |
| master | [![Build Status](https://travis-ci.org/AsgardCms/Menu.svg?branch=master)](https://travis-ci.org/AsgardCms/Menu) |
## Resources
- [Contribute to AsgardCMS](https://asgardcms.com/en/docs/getting-started/contributing)
- [License](LICENSE.md)
- [Documentation](http://asgardcms.com/docs/menu-module/managing-menus)
## Info
All AsgardCMS modules respect [Semantic Versioning](http://semver.org/).
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment