Commit f433735f authored by Nicolas Widart's avatar Nicolas Widart

Add 'Modules/Menu/' from commit '4f6cdddf'

git-subtree-dir: Modules/Menu
git-subtree-mainline: 27b56c29
git-subtree-split: 4f6cdddf
parents 27b56c29 4f6cdddf
rules:
php.interface_has_no_interface_suffix:
enabled: false
.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-empty,
.dd-placeholder {
display: block;
position: relative;
margin: 0;
padding: 0;
min-height: 20px;
font-size: 13px;
line-height: 20px;
}
.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 > 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 > button:before {
content: '+';
display: block;
position: absolute;
width: 100%;
text-align: center;
text-indent: 0;
}
.dd-item > button[data-action="collapse"]:before {
content: '-';
}
.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;
}
/*!
* Nestable jQuery Plugin - Copyright (c) 2012 David Bushell - http://dbushell.com/
* Dual-licensed under the BSD or MIT licenses
*/
;(function($, window, document, undefined)
{
var hasTouch = 'ontouchstart' in document;
/**
* Detect CSS pointer-events property
* events are normally disabled on the dragging element to avoid conflicts
* https://github.com/ausi/Feature-detection-technique-for-pointer-events/blob/master/modernizr-pointerevents.js
*/
var hasPointerEvents = (function()
{
var el = document.createElement('div'),
docEl = document.documentElement;
if (!('pointerEvents' in el.style)) {
return false;
}
el.style.pointerEvents = 'auto';
el.style.pointerEvents = 'x';
docEl.appendChild(el);
var supports = window.getComputedStyle && window.getComputedStyle(el, '').pointerEvents === 'auto';
docEl.removeChild(el);
return !!supports;
})();
var defaults = {
listNodeName : 'ol',
itemNodeName : 'li',
rootClass : 'dd',
listClass : 'dd-list',
itemClass : 'dd-item',
dragClass : 'dd-dragel',
handleClass : 'dd-handle',
collapsedClass : 'dd-collapsed',
placeClass : 'dd-placeholder',
noDragClass : 'dd-nodrag',
emptyClass : 'dd-empty',
expandBtnHTML : '<button data-action="expand" type="button">Expand</button>',
collapseBtnHTML : '<button data-action="collapse" type="button">Collapse</button>',
group : 0,
maxDepth : 5,
threshold : 20
};
function Plugin(element, options)
{
this.w = $(document);
this.el = $(element);
this.options = $.extend({}, defaults, options);
this.init();
}
Plugin.prototype = {
init: function()
{
var list = this;
list.reset();
list.el.data('nestable-group', this.options.group);
list.placeEl = $('<div class="' + list.options.placeClass + '"/>');
$.each(this.el.find(list.options.itemNodeName), function(k, el) {
list.setParent($(el));
});
list.el.on('click', 'button', function(e) {
if (list.dragEl) {
return;
}
var target = $(e.currentTarget),
action = target.data('action'),
item = target.parent(list.options.itemNodeName);
if (action === 'collapse') {
list.collapseItem(item);
}
if (action === 'expand') {
list.expandItem(item);
}
});
var onStartEvent = function(e)
{
var handle = $(e.target);
if (!handle.hasClass(list.options.handleClass)) {
if (handle.closest('.' + list.options.noDragClass).length) {
return;
}
handle = handle.closest('.' + list.options.handleClass);
}
if (!handle.length || list.dragEl) {
return;
}
list.isTouch = /^touch/.test(e.type);
if (list.isTouch && e.touches.length !== 1) {
return;
}
e.preventDefault();
list.dragStart(e.touches ? e.touches[0] : e);
};
var onMoveEvent = function(e)
{
if (list.dragEl) {
e.preventDefault();
list.dragMove(e.touches ? e.touches[0] : e);
}
};
var onEndEvent = function(e)
{
if (list.dragEl) {
e.preventDefault();
list.dragStop(e.touches ? e.touches[0] : e);
}
};
if (hasTouch) {
list.el[0].addEventListener('touchstart', onStartEvent, false);
window.addEventListener('touchmove', onMoveEvent, false);
window.addEventListener('touchend', onEndEvent, false);
window.addEventListener('touchcancel', onEndEvent, false);
}
list.el.on('mousedown', onStartEvent);
list.w.on('mousemove', onMoveEvent);
list.w.on('mouseup', onEndEvent);
},
serialize: function()
{
var data,
depth = 0,
list = this;
step = function(level, depth)
{
var array = [ ],
items = level.children(list.options.itemNodeName);
items.each(function()
{
var li = $(this),
item = $.extend({}, li.data()),
sub = li.children(list.options.listNodeName);
if (sub.length) {
item.children = step(sub, depth + 1);
}
array.push(item);
});
return array;
};
data = step(list.el.find(list.options.listNodeName).first(), depth);
return data;
},
serialise: function()
{
return this.serialize();
},
reset: function()
{
this.mouse = {
offsetX : 0,
offsetY : 0,
startX : 0,
startY : 0,
lastX : 0,
lastY : 0,
nowX : 0,
nowY : 0,
distX : 0,
distY : 0,
dirAx : 0,
dirX : 0,
dirY : 0,
lastDirX : 0,
lastDirY : 0,
distAxX : 0,
distAxY : 0
};
this.isTouch = false;
this.moving = false;
this.dragEl = null;
this.dragRootEl = null;
this.dragDepth = 0;
this.hasNewRoot = false;
this.pointEl = null;
},
expandItem: function(li)
{
li.removeClass(this.options.collapsedClass);
li.children('[data-action="expand"]').hide();
li.children('[data-action="collapse"]').show();
li.children(this.options.listNodeName).show();
},
collapseItem: function(li)
{
var lists = li.children(this.options.listNodeName);
if (lists.length) {
li.addClass(this.options.collapsedClass);
li.children('[data-action="collapse"]').hide();
li.children('[data-action="expand"]').show();
li.children(this.options.listNodeName).hide();
}
},
expandAll: function()
{
var list = this;
list.el.find(list.options.itemNodeName).each(function() {
list.expandItem($(this));
});
},
collapseAll: function()
{
var list = this;
list.el.find(list.options.itemNodeName).each(function() {
list.collapseItem($(this));
});
},
setParent: function(li)
{
if (li.children(this.options.listNodeName).length) {
li.prepend($(this.options.expandBtnHTML));
li.prepend($(this.options.collapseBtnHTML));
}
li.children('[data-action="expand"]').hide();
},
unsetParent: function(li)
{
li.removeClass(this.options.collapsedClass);
li.children('[data-action]').remove();
li.children(this.options.listNodeName).remove();
},
dragStart: function(e)
{
var mouse = this.mouse,
target = $(e.target),
dragItem = target.closest(this.options.itemNodeName);
this.placeEl.css('height', dragItem.height());
mouse.offsetX = e.offsetX !== undefined ? e.offsetX : e.pageX - target.offset().left;
mouse.offsetY = e.offsetY !== undefined ? e.offsetY : e.pageY - target.offset().top;
mouse.startX = mouse.lastX = e.pageX;
mouse.startY = mouse.lastY = e.pageY;
this.dragRootEl = this.el;
this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass);
this.dragEl.css('width', dragItem.width());
dragItem.after(this.placeEl);
dragItem[0].parentNode.removeChild(dragItem[0]);
dragItem.appendTo(this.dragEl);
$(document.body).append(this.dragEl);
this.dragEl.css({
'left' : e.pageX - mouse.offsetX,
'top' : e.pageY - mouse.offsetY
});
// total depth of dragging item
var i, depth,
items = this.dragEl.find(this.options.itemNodeName);
for (i = 0; i < items.length; i++) {
depth = $(items[i]).parents(this.options.listNodeName).length;
if (depth > this.dragDepth) {
this.dragDepth = depth;
}
}
},
dragStop: function(e)
{
var el = this.dragEl.children(this.options.itemNodeName).first();
el[0].parentNode.removeChild(el[0]);
this.placeEl.replaceWith(el);
this.dragEl.remove();
this.el.trigger('change');
if (this.hasNewRoot) {
this.dragRootEl.trigger('change');
}
this.reset();
},
dragMove: function(e)
{
var list, parent, prev, next, depth,
opt = this.options,
mouse = this.mouse;
this.dragEl.css({
'left' : e.pageX - mouse.offsetX,
'top' : e.pageY - mouse.offsetY
});
// mouse position last events
mouse.lastX = mouse.nowX;
mouse.lastY = mouse.nowY;
// mouse position this events
mouse.nowX = e.pageX;
mouse.nowY = e.pageY;
// distance mouse moved between events
mouse.distX = mouse.nowX - mouse.lastX;
mouse.distY = mouse.nowY - mouse.lastY;
// direction mouse was moving
mouse.lastDirX = mouse.dirX;
mouse.lastDirY = mouse.dirY;
// direction mouse is now moving (on both axis)
mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1;
mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1;
// axis mouse is now moving on
var newAx = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0;
// do nothing on first move
if (!mouse.moving) {
mouse.dirAx = newAx;
mouse.moving = true;
return;
}
// calc distance moved on this axis (and direction)
if (mouse.dirAx !== newAx) {
mouse.distAxX = 0;
mouse.distAxY = 0;
} else {
mouse.distAxX += Math.abs(mouse.distX);
if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {
mouse.distAxX = 0;
}
mouse.distAxY += Math.abs(mouse.distY);
if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {
mouse.distAxY = 0;
}
}
mouse.dirAx = newAx;
/**
* move horizontal
*/
if (mouse.dirAx && mouse.distAxX >= opt.threshold) {
// reset move distance on x-axis for new phase
mouse.distAxX = 0;
prev = this.placeEl.prev(opt.itemNodeName);
// increase horizontal level if previous sibling exists and is not collapsed
if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass)) {
// cannot increase level when item above is collapsed
list = prev.find(opt.listNodeName).last();
// check if depth limit has reached
depth = this.placeEl.parents(opt.listNodeName).length;
if (depth + this.dragDepth <= opt.maxDepth) {
// create new sub-level if one doesn't exist
if (!list.length) {
list = $('<' + opt.listNodeName + '/>').addClass(opt.listClass);
list.append(this.placeEl);
prev.append(list);
this.setParent(prev);
} else {
// else append to next level up
list = prev.children(opt.listNodeName).last();
list.append(this.placeEl);
}
}
}
// decrease horizontal level
if (mouse.distX < 0) {
// we can't decrease a level if an item preceeds the current one
next = this.placeEl.next(opt.itemNodeName);
if (!next.length) {
parent = this.placeEl.parent();
this.placeEl.closest(opt.itemNodeName).after(this.placeEl);
if (!parent.children().length) {
this.unsetParent(parent.parent());
}
}
}
}
var isEmpty = false;
// find list item under cursor
if (!hasPointerEvents) {
this.dragEl[0].style.visibility = 'hidden';
}
this.pointEl = $(document.elementFromPoint(e.pageX - document.body.scrollLeft, e.pageY - (window.pageYOffset || document.documentElement.scrollTop)));
if (!hasPointerEvents) {
this.dragEl[0].style.visibility = 'visible';
}
if (this.pointEl.hasClass(opt.handleClass)) {
this.pointEl = this.pointEl.parent(opt.itemNodeName);
}
if (this.pointEl.hasClass(opt.emptyClass)) {
isEmpty = true;
}
else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) {
return;
}
// find parent list of item under cursor
var pointElRoot = this.pointEl.closest('.' + opt.rootClass),
isNewRoot = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id');
/**
* move vertical
*/
if (!mouse.dirAx || isNewRoot || isEmpty) {
// check if groups match if dragging over new root
if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) {
return;
}
// check depth limit
depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length;
if (depth > opt.maxDepth) {
return;
}
var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);
parent = this.placeEl.parent();
// if empty create new list to replace empty placeholder
if (isEmpty) {
list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass);
list.append(this.placeEl);
this.pointEl.replaceWith(list);
}
else if (before) {
this.pointEl.before(this.placeEl);
}
else {
this.pointEl.after(this.placeEl);
}
if (!parent.children().length) {
this.unsetParent(parent.parent());
}
if (!this.dragRootEl.find(opt.itemNodeName).length) {
this.dragRootEl.append('<div class="' + opt.emptyClass + '"/>');
}
// parent root list has changed
if (isNewRoot) {
this.dragRootEl = pointElRoot;
this.hasNewRoot = this.el[0] !== this.dragRootEl[0];
}
}
}
};
$.fn.nestable = function(params)
{
var lists = this,
retval = this;
lists.each(function()
{
var plugin = $(this).data("nestable");
if (!plugin) {
$(this).data("nestable", new Plugin(this, params));
$(this).data("nestable-id", new Date().getTime());
} else {
if (typeof params === 'string' && typeof plugin[params] === 'function') {
retval = plugin[params]();
}
}
});
return retval || lists;
};
})(window.jQuery || window.Zepto, window, document);
.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-empty,
.dd-placeholder {
display: block;
position: relative;
margin: 0;
padding: 0;
min-height: 20px;
font-size: 13px;
line-height: 20px;
}
.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 > 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 > button:before {
content: '+';
display: block;
position: absolute;
width: 100%;
text-align: center;
text-indent: 0;
}
.dd-item > button[data-action="collapse"]:before {
content: '-';
}
.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\Composers;
use Illuminate\Contracts\View\View;
use Modules\Core\Composers\BaseSidebarViewComposer;
class SidebarViewComposer extends BaseSidebarViewComposer
{
public function compose(View $view)
{
$view->items->put('menus', [
'weight' => 7,
'request' => "*/$view->prefix/menu*",
'route' => 'dashboard.menu.index',
'icon-class' => 'fa fa-bars',
'title' => 'Menus',
'permission' => $this->auth->hasAccess('menus.index')
]);
}
}
<?php
return [
'menus' => [
'index',
'create',
'store',
'edit',
'update',
'destroy',
],
'menuitem' => [
'index',
'create',
'store',
'edit',
'update',
'destroy',
]
];
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMenusTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('menus', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->tinyInteger('primary')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('menus');
}
}
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMenuTranslationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('menu_translations', function(Blueprint $table) {
$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('menus')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('menu_translations');
}
}
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMenuitemsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('menuitems', function(Blueprint $table) {
$table->increments('id');
$table->integer('menu_id')->unsigned();
$table->foreign('menu_id')->references('id')->on('menus')->onDelete('cascade');
$table->integer('page_id')->unsigned()->nullable();
$table->integer('position')->unsigned()->default(0);
$table->string('target', 10)->nullable();
$table->string('module_name');
/* 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('menuitems');
}
}
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMenuitemTranslationTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('menuitem_translations', function(Blueprint $table) {
$table->increments('id');
$table->integer('menuitem_id')->unsigned();
$table->string('locale')->index();
$table->tinyInteger('status')->default(0);
$table->string('title');
$table->string('url');
$table->string('uri');
$table->unique(['menuitem_id', 'locale']);
$table->foreign('menuitem_id')->references('id')->on('menuitems')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('menuitem_translations');
}
}
<?php namespace Modules\Menu\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
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'];
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'];
}
<?php namespace Modules\Menu\Entities;
use Baum\Node;
use Modules\Core\Internationalisation\Translatable;
class Menuitem extends Node
{
use Translatable;
public $translatedAttributes = ['title', 'uri', 'url', 'status'];
protected $fillable = [
'menu_id',
'page_id',
'position',
'target',
'module_name',
'title',
'uri',
'url',
'status'
];
public function menu()
{
return $this->belongsTo('Modules\Menu\Entities\Menu');
}
}
<?php namespace Modules\Menu\Entities;
use Illuminate\Database\Eloquent\Model;
class MenuitemTranslation extends Model
{
public $fillable = ['title', 'uri', 'url', 'status'];
}
<?php namespace Modules\Menu\Http\Controllers\Admin;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\View;
use Laracasts\Flash\Flash;
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 Redirector
*/
private $redirector;
/**
* @var MenuItemRepository
*/
private $menuItem;
/**
* @var MenuRenderer
*/
private $menuRenderer;
public function __construct(
MenuRepository $menu,
MenuItemRepository $menuItem,
Redirector $redirector,
MenuRenderer $menuRenderer
) {
parent::__construct();
$this->menu = $menu;
$this->redirector = $redirector;
$this->menuItem = $menuItem;
$this->menuRenderer = $menuRenderer;
}
public function index()
{
$menus = $this->menu->all();
return View::make('menu::admin.menus.index', compact('menus'));
}
public function create()
{
return View::make('menu::admin.menus.create');
}
public function store(CreateMenuRequest $request)
{
$this->menu->create($request->all());
Flash::success('Menu created!');
return $this->redirector->route('dashboard.menu.index');
}
public function edit(Menu $menu)
{
$menuItems = $this->menuItem->rootsForMenu($menu->id);
$menuStructure = $this->menuRenderer->renderForMenu($menu->id, $menuItems);
return View::make('menu::admin.menus.edit', compact('menu', 'menuStructure'));
}
public function update(Menu $menu, UpdateMenuRequest $request)
{
$this->menu->update($menu, $request->all());
Flash::success('Menu updated!');
return $this->redirector->route('dashboard.menu.index');
}
public function destroy(Menu $menu)
{
$this->menu->destroy($menu);
Flash::success('Menu destroyed');
return $this->redirector->route('dashboard.menu.index');
}
}
<?php namespace Modules\Menu\Http\Controllers\Admin;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Routing\Redirector;
use Laracasts\Flash\Flash;
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;
class MenuItemController
{
/**
* @var MenuItemRepository
*/
private $menuItem;
/**
* @var Redirector
*/
private $redirector;
public function __construct(MenuItemRepository $menuItem, Redirector $redirector)
{
$this->menuItem = $menuItem;
$this->redirector = $redirector;
}
public function create(Menu $menu)
{
return view('menu::admin.menuitems.create', compact('menu'));
}
public function store(Menu $menu, CreateMenuItemRequest $request)
{
$this->menuItem->create($this->addMenuId($menu, $request));
Flash::success('Menu item created!');
return $this->redirector->route('dashboard.menu.edit', [$menu->id]);
}
public function edit(Menu $menu, Menuitem $menuItem)
{
return view('menu::admin.menuitems.edit', compact('menu', 'menuItem'));
}
public function update(Menu $menu, Menuitem $menuItem, UpdateMenuItemRequest $request)
{
$this->menuItem->update($menuItem, $this->addMenuId($menu, $request));
Flash::success('Menu item updated!');
return $this->redirector->route('dashboard.menu.edit', [$menu->id]);
}
/**
* @param Menu $menu
* @param \Illuminate\Foundation\Http\FormRequest $request
* @return array
*/
private function addMenuId(Menu $menu, FormRequest $request)
{
return array_merge($request->all(), ['menu_id' => $menu->id]);
}
}
<?php namespace Modules\Menu\Http\Controllers\Api;
use Modules\Core\Http\Controllers\Admin\AdminBaseController;
class MenuController extends AdminBaseController
{
}
<?php namespace Modules\Menu\Http\Controllers\Api;
use Illuminate\Http\Request;
use Modules\Menu\Services\MenuService;
class MenuItemController
{
/**
* @var MenuService
*/
private $menuService;
public function __construct(MenuService $menuService)
{
$this->menuService = $menuService;
}
/**
* Update all menu items
* @param Request $request
*/
public function update(Request $request)
{
foreach ($request->all() as $position => $item) {
$this->menuService->handle($item, $position);
}
}
}
<?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: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()
{
return [
'name' => 'required',
'primary' => 'unique: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
use Illuminate\Routing\Router;
$router->model('menus', 'Modules\Menu\Entities\Menu');
$router->model('menuitem', 'Modules\Menu\Entities\Menuitem');
$router->group(['prefix' => LaravelLocalization::setLocale(), 'before' => 'LaravelLocalizationRedirectFilter|auth.admin|permissions'], function(Router $router)
{
$router->group(['prefix' => Config::get('core::core.admin-prefix'), 'namespace' => 'Modules\Menu\Http\Controllers'], function(Router $router)
{
$router->resource('menus', 'Admin\MenuController', ['except' => ['show'], 'names' => [
'index' => 'dashboard.menu.index',
'create' => 'dashboard.menu.create',
'store' => 'dashboard.menu.store',
'edit' => 'dashboard.menu.edit',
'update' => 'dashboard.menu.update',
'destroy' => 'dashboard.menu.destroy',
]]);
$router->resource('menus.menuitem', 'Admin\MenuItemController', ['except' => ['show'], 'names' => [
'index' => 'dashboard.menuitem.index',
'create' => 'dashboard.menuitem.create',
'store' => 'dashboard.menuitem.store',
'edit' => 'dashboard.menuitem.edit',
'update' => 'dashboard.menuitem.update',
'destroy' => 'dashboard.menuitem.destroy',
]]);
});
});
$router->group(['prefix' => 'api', 'namespace' => 'Modules\Menu\Http\Controllers'], function (Router $router) {
$router->resource('media', 'Api\MenuController', ['only' => ['store']]);
$router->post('menuitem/update', 'Api\MenuItemController@update');
});
<?php namespace Modules\Menu\Providers;
use Illuminate\Support\ServiceProvider;
use Modules\Menu\Entities\Menu;
use Modules\Menu\Repositories\Eloquent\EloquentMenuRepository;
class MenuServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->registerBindings();
}
/**
* 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() {
return new EloquentMenuRepository(new Menu);
}
);
}
}
<?php namespace Modules\Menu\Providers;
use Illuminate\Routing\Router;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
/**
* The root namespace to assume when generating URLs to actions.
*
* @var string
*/
protected $rootUrlNamespace = 'Modules\Menu\Http\Controllers';
/**
* The controllers to scan for route annotations.
*
* @var array
*/
protected $scan = [
'Modules\Menu\Http\Controllers',
];
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*
* @param Router $router
* @return void
*/
public function before(Router $router)
{
//
}
/**
* Define the routes for the application.
*
* @return void
*/
public function map(Router $router)
{
require __DIR__ . '/../Http/routes.php';
}
}
<?php namespace Modules\Menu\Repositories\Eloquent;
use Illuminate\Support\Facades\DB;
use Modules\Core\Repositories\Eloquent\EloquentBaseRepository;
use Modules\Menu\Repositories\MenuItemRepository;
class EloquentMenuItemRepository extends EloquentBaseRepository implements MenuItemRepository
{
public function create($data)
{
return $this->model->create($data);
}
public function update($menuItem, $data)
{
$menuItem->update($data);
return $menuItem;
}
public function rootsForMenu($menuId)
{
return $this->model->roots()->where('menu_id', $menuId)->orderBy('position')->get();
}
/**
* Get Items to build routes
*
* @return Array
*/
public function getForRoutes()
{
$menuitems = DB::table('menus')
->select(
'primary',
'menuitems.id',
'menuitems.parent_id',
'menuitems.module_name',
'menuitem_translations.uri',
'menuitem_translations.locale'
)
->join('menuitems', 'menus.id', '=', 'menuitems.menu_id')
->join('menuitem_translations', 'menuitems.id', '=', '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->locale . '/' . $menuitem->uri;
}
return $menuitemsArray;
}
}
<?php namespace Modules\Menu\Repositories\Eloquent;
use Modules\Core\Repositories\Eloquent\EloquentBaseRepository;
use Modules\Menu\Repositories\MenuRepository;
class EloquentMenuRepository extends EloquentBaseRepository implements MenuRepository
{
public function create($data)
{
return $this->model->create($data);
}
public function update($menu, $data)
{
$menu->update($data);
return $menu;
}
}
<?php namespace Modules\Menu\Repositories;
use Modules\Core\Repositories\BaseRepository;
interface MenuItemRepository extends BaseRepository
{
/**
* Get all root elements
* @return mixed
*/
public function rootsForMenu($menuId);
/**
* Get the menu items ready for routes
* @return mixed
*/
public function getForRoutes();
}
<?php namespace Modules\Menu\Repositories;
use Modules\Core\Repositories\BaseRepository;
interface MenuRepository extends BaseRepository
{
}
<?php
return [
'form' => [
'page' => 'Page',
'module' => 'Module',
'target' => 'Target',
'same tab' => 'Same tab',
'new tab' => 'New tab',
]
];
<?php
return [
'titles' => [
'menu' => 'Menu management',
'create menu' => 'Create a menu',
'edit menu' => 'Edit a menu',
'create menu item' => 'Create a menu item',
'edit menu item' => 'Edit a menu item',
],
'breadcrumb' => [
'menu' => 'Menu management',
'create menu' => 'Create a menu',
'edit menu' => 'Edit a menu',
'create menu item' => 'Create a menu item',
'edit menu item' => 'Edit a menu item',
],
'button' => [
'create menu item' => 'Create a menu item',
'create menu' => 'Create a menu'
],
'table' => [
'name' => 'Name',
'title' => 'Title',
],
'form' => [
'title' => 'Title',
'name' => 'Name',
'status' => 'Online',
'uri' => 'URI',
'primary' => 'Primary menu (used for front-end routing)',
]
];
<?php
return [
'only one primary menu' => 'Only one menu can be primary at a time',
'name is required' => 'The name is required',
];
<?php
return [
'form' => [
'page' => 'Page',
'module' => 'Module',
'target' => 'Target',
'same tab' => 'Même tab',
'new tab' => 'Nouveau tab',
]
];
<?php
return [
'titles' => [
'menu' => 'Gestionnaire de menu',
'create menu' => 'Créer un menu',
'edit menu' => 'Editer un menu',
'create menu item' => 'Créer un élément du menu',
'edit menu item' => 'Editer un élément du menu',
],
'breadcrumb' => [
'menu' => 'Gestionnaire de menu',
'create menu' => 'Créer un menu',
'edit menu' => 'Editer un menu',
'create menu item' => 'Créer un élément du menu',
'edit menu item' => 'Editer un élément du menu',
],
'button' => [
'create menu item' => 'Créer un élément de menu',
'create menu' => 'Créer un menu'
],
'table' => [
'name' => 'Nom',
'title' => 'Titre',
],
'form' => [
'title' => 'Titre',
'name' => 'Nom',
'status' => 'En ligne',
'uri' => 'URI',
'primary' => 'Menu principal (utiliser pour le routage front-end)',
]
];
<?php
return [
'only one primary menu' => 'Il ne peut avoir qu\'un seul menu primaire',
'name is required' => 'Le nom est réquis',
];
@extends('core::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('dashboard.menu.index') }}">{{ trans('menu::menu.breadcrumb.menu') }}</a></li>
<li>{{ trans('menu::menu.breadcrumb.create menu item') }}</li>
</ol>
@stop
@section('styles')
<link href="{!! Module::asset('core:css/vendor/iCheck/flat/blue.css') !!}" rel="stylesheet" type="text/css" />
@stop
@section('content')
{!! Form::open(['route' => ['dashboard.menuitem.store', $menu->id], 'method' => 'post']) !!}
<div class="row">
<div class="col-md-12">
<div class="box box-info">
<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-info">
<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>
<a class="btn btn-danger pull-right btn-flat" href="{{ URL::route('dashboard.menu.edit', [$menu->id])}}"><i class="fa fa-times"></i> {{ trans('core::core.button.cancel') }}</a>
</div>
</div>
</div>
{!! Form::close() !!}
@stop
@section('scripts')
<script>
$( document ).ready(function() {
$('input[type="checkbox"].flat-blue, input[type="radio"].flat-blue').iCheck({
checkboxClass: 'icheckbox_flat-blue',
radioClass: 'iradio_flat-blue'
});
});
</script>
@stop
@extends('core::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('dashboard.menu.index') }}">{{ trans('menu::menu.breadcrumb.menu') }}</a></li>
<li>{{ trans('menu::menu.breadcrumb.edit menu item') }}</li>
</ol>
@stop
@section('styles')
<link href="{!! Module::asset('core:css/vendor/iCheck/flat/blue.css') !!}" rel="stylesheet" type="text/css" />
@stop
@section('content')
{!! Form::open(['route' => ['dashboard.menuitem.update', $menu->id, $menuItem->id], 'method' => 'put']) !!}
<div class="row">
<div class="col-md-12">
<div class="box box-info">
<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-info">
<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>
<a class="btn btn-danger pull-right btn-flat" href="{{ URL::route('dashboard.menu.edit', [$menu->id])}}"><i class="fa fa-times"></i> {{ trans('core::core.button.cancel') }}</a>
</div>
</div>
</div>
{!! Form::close() !!}
@stop
@section('scripts')
<script>
$( document ).ready(function() {
$('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>
@stop
<div class="form-group">
<label for="page">{{ trans('menu::menu-items.form.page') }}</label>
<select class="form-control" name="page_id" id="page">
<option value=""></option>
</select>
</div>
<div class="form-group">
<label for="module">{{ trans('menu::menu-items.form.module') }}</label>
<select class="form-control" name="module" id="module">
<option value=""></option>
<?php foreach(Module::enabled() as $module): ?>
<option value="{{ strtolower($module) }}">{{ $module }}</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]", Input::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="form-group">
{!! 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]", Input::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="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">
<label for="page">{{ trans('menu::menu-items.form.page') }}</label>
<select class="form-control" name="page_id" id="page">
<option value=""></option>
</select>
</div>
<div class="form-group">
<label for="module">{{ trans('menu::menu-items.form.module') }}</label>
<select class="form-control" name="module" id="module">
<option value=""></option>
<?php foreach(Module::enabled() as $module): ?>
<option value="{{ strtolower($module) }}" {{ $menuItem->module_name == strtolower($module) ? 'selected' : '' }}>{{ $module }}</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" {{ $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')) !!}
{!! Form::text("{$lang}[title]", Input::old("{$lang}[title]", $menuItem->translate($lang)->title), ['class' => 'form-control', 'placeholder' => trans('menu::menu.form.title')]) !!}
{!! $errors->first("{$lang}[title]", '<span class="help-block">:message</span>') !!}
</div>
<div class="form-group">
{!! 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]", Input::old("{$lang}[uri]", $menuItem->translate($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="checkbox">
<label for="{{$lang}}[status]">
<input id="{{$lang}}[status]"
name="{{$lang}}[status]"
type="checkbox"
class="flat-blue"
{{ (bool)$menuItem->translate($lang)->status ? 'checked' : '' }}
value="1" />
{{ trans('menu::menu.form.status') }}
</label>
</div>
@extends('core::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('dashboard.menu.index') }}">{{ trans('menu::menu.breadcrumb.menu') }}</a></li>
<li>{{ trans('menu::menu.breadcrumb.create menu') }}</li>
</ol>
@stop
@section('styles')
<link href="{!! Module::asset('core:css/vendor/iCheck/flat/blue.css') !!}" rel="stylesheet" type="text/css" />
@stop
@section('content')
{!! Form::open(['route' => ['dashboard.menu.store'], 'method' => 'post']) !!}
<div class="row">
<div class="col-md-12">
<div class="box box-info">
<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-info">
<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>
<a class="btn btn-danger pull-right btn-flat" href="{{ URL::route('dashboard.menu.index')}}"><i class="fa fa-times"></i> {{ trans('core::core.button.cancel') }}</a>
</div>
</div>
</div>
{!! Form::close() !!}
@stop
@section('scripts')
<script>
$( document ).ready(function() {
$('input[type="checkbox"].flat-blue, input[type="radio"].flat-blue').iCheck({
checkboxClass: 'icheckbox_flat-blue',
radioClass: 'iradio_flat-blue'
});
});
</script>
@stop
@extends('core::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('dashboard.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('core:css/vendor/iCheck/flat/blue.css') !!}" rel="stylesheet" type="text/css" />
<link href="{!! Module::asset('menu:css/nestable.css') !!}" rel="stylesheet" type="text/css" />
@stop
@section('content')
{!! Form::open(['route' => ['dashboard.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-info" style="overflow: hidden;">
<div class="box-body">
{!! $menuStructure !!}
</div>
</div>
</div>
<div class="col-md-6">
<div class="box box-info">
<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-info">
<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>
<a class="btn btn-danger pull-right btn-flat" href="{{ URL::route('dashboard.menu.index')}}"><i class="fa fa-times"></i> {{ trans('core::core.button.cancel') }}</a>
</div>
</div>
</div>
{!! Form::close() !!}
@stop
@section('scripts')
<script>
$( document ).ready(function() {
$('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: '/api/menuitem/update',
data: JSON.stringify(data),
contentType: 'application/json;',
dataType: 'json',
success: function(data) {
},
error:function (xhr, ajaxOptions, thrownError){
}
});
});
});
</script>
@stop
@extends('core::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('dashboard.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-info">
<div class="box-header">
</div>
<!-- /.box-header -->
<div class="box-body table-responsive">
<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>{{ trans('core::core.table.actions') }}</th>
</tr>
</thead>
<tbody>
<?php if (isset($menus)): ?>
<?php foreach($menus as $menu): ?>
<tr>
<td>
<a href="{{ URL::route('dashboard.menu.edit', [$menu->id]) }}">
{{ $menu->name }}
</a>
</td>
<td>
<a href="{{ URL::route('dashboard.menu.edit', [$menu->id]) }}">
{{ $menu->title }}
</a>
</td>
<td>
<div class="btn-group">
<a href="{{ URL::route('dashboard.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" 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-default" data-dismiss="modal">{{ trans('core::core.button.cancel') }}</button>
{!! Form::open(['route' => ['dashboard.menu.destroy', $menu->id], 'method' => 'delete', 'class' => 'pull-left']) !!}
<button type="submit" class="btn btn-danger 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('scripts')
<?php $locale = App::getLocale(); ?>
<script type="text/javascript">
$(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") ?>'
},
"columns": [
null,
null,
{ "sortable": false }
]
});
});
</script>
@stop
<div class='form-group{{ $errors->has('name') ? ' has-error' : '' }}'>
{!! Form::label('name', trans('menu::menu.form.name')) !!}
{!! Form::text('name', Input::old('name'), ['class' => 'form-control', 'placeholder' => trans('menu::menu.form.name')]) !!}
{!! $errors->first('Name', '<span class="help-block">:message</span>') !!}
</div>
<div class="checkbox">
<label for="primary">
<input id="primary"
name="primary"
type="checkbox"
class="flat-blue"
value="1" />
{{ trans('menu::menu.form.primary') }}
</label>
</div>
<div class='form-group{{ $errors->has("{$lang}[title]") ? ' has-error' : '' }}'>
{!! Form::label("{$lang}[title]", trans('menu::menu.form.title')) !!}
{!! Form::text("{$lang}[title]", Input::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"
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', Input::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="checkbox {{ $errors->has('primary') ? ' has-error' : '' }}">
<label for="primary">
<input id="primary"
name="primary"
type="checkbox"
class="flat-blue"
{{ ((bool)$menu->primary) ? 'checked' : '' }}
value="1" />
{{ trans('menu::menu.form.primary') }}
{!! $errors->first('primary', '<span class="help-block">:message</span>') !!}
</label>
</div>
<div class='form-group{{ $errors->has("{$lang}[title]") ? ' has-error' : '' }}'>
{!! Form::label("{$lang}[title]", trans('menu::menu.form.title')) !!}
{!! Form::text("{$lang}[title]", Input::old("{$lang}[title]", $menu->translate($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"
{{ ((bool)$menu->translate($lang)->status) ? 'checked' : '' }}
value="1" />
{{ trans('menu::menu.form.status') }}
</label>
</div>
<?php namespace Modules\Menu\Services;
use Baum\Node;
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}\">";
$this->menu .= '<a class="btn btn-sm btn-info"
style="float:left; margin-right: 15px;"
href="' . URL::route('dashboard.menuitem.edit', [$this->menuId, $item->id]) . '">
Edit</a>';
$this->menu .= "<div class=\"dd-handle\">{$item->title}</div>";
if ($this->hasChildren($item)) {
$this->generateHtmlFor($item->children()->get());
}
$this->menu .= '</li>';
}
$this->menu .= '</ol>';
}
/**
* @param $item
* @return bool
*/
private function hasChildren(Node $item)
{
return !$item->children()->get()->isEmpty();
}
}
<?php namespace Modules\Menu\Services;
use Modules\Menu\Repositories\MenuItemRepository;
class MenuService
{
/**
* Current Menu Item being looped over
* @var
*/
protected $menuItem;
/**
* @var MenuItemRepository
*/
private $menuItemRepository;
/**
* @param MenuItemRepository $menuItem
*/
public function __construct(MenuItemRepository $menuItem)
{
$this->menuItemRepository = $menuItem;
}
/**
* Perform needed operations on given menu item and set its position
* @param $item
* @param int $position
*/
public function handle($item, $position)
{
$this->menuItem = $this->menuItemRepository->find($item['id']);
$this->savePosition($this->menuItem, $position);
$this->menuItem->makeRoot();
if ($this->hasChildren($item)) {
$this->setChildrenRecursively($item, $this->menuItem);
}
}
/**
* Sets the children of the given item
* @param $item
* @param $parent
*/
private function setChildrenRecursively($item, $parent)
{
foreach ($item['children'] as $childPosition => $childItem) {
$childMenuItem = $this->menuItemRepository->find($childItem['id']);
$this->savePosition($childMenuItem, $childPosition);
$childMenuItem->makeChildOf($parent);
if ($this->hasChildren($childItem)) $this->setChildrenRecursively($childItem, $childMenuItem);
}
}
/**
* Check if the item has children
* @param $item
* @return bool
*/
private function hasChildren($item)
{
return isset($item['children']);
}
/**
* Save the position of the given item
* @param $item
* @param $position
*/
private function savePosition($item, $position)
{
$item->position = $position;
$item->save();
}
}
<?php
View::composer('core::partials.sidebar-nav', 'Modules\Menu\Composers\SidebarViewComposer');
{
"name": "Menu",
"alias": "menu",
"description": "",
"keywords": [
],
"active": 1,
"providers": [
"Modules\\Menu\\Providers\\RouteServiceProvider",
"Modules\\Menu\\Providers\\MenuServiceProvider"
]
}
# Menu Module
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/f6ca068c-662b-4606-9bee-262abc858f02/mini.png)](https://insight.sensiolabs.com/projects/f6ca068c-662b-4606-9bee-262abc858f02)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/AsgardCms/Menu/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/AsgardCms/Menu/?branch=master)
[![Code Climate](https://codeclimate.com/github/AsgardCms/Menu/badges/gpa.svg)](https://codeclimate.com/github/AsgardCms/Menu)
<?php
/*
|--------------------------------------------------------------------------
| Register The Module Namespaces
|--------------------------------------------------------------------------
|
| Here is you can register the namespace for this module.
| You may to edit this namespace if you want.
|
*/
View::addNamespace('menu', __DIR__ . '/Resources/views/');
Lang::addNamespace('menu', __DIR__ . '/Resources/lang/');
Config::addNamespace('menu', __DIR__ . '/Config/');
require __DIR__ . '/composers.php';
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