yf_menus_editor admin moduleSystem-wide menus editor.
| File | plugins/sys/admin_modules/yf_menus_editor.class.php |
|---|---|
| Use | admin URL ?object=menus_editor |
A site sets them in its config with $PROJECT_CONF['menus_editor']['NAME'] = ...;
| Property | Default | Description |
|---|---|---|
$ICONS_PATHstring |
'uploads/icons/' |
Path to icons |
$_groups |
null |
|
$_item_types |
null |
|
$_menu_items |
null |
|
$_menu_types |
null |
|
$_menus_infos |
null |
|
$_servers |
null |
|
$_sites |
null |
public function activate_item()
public function activate_item()
{
if ( ! empty($_GET['id'])) {
$item_info = db()->query_fetch('SELECT * FROM ' . db('menu_items') . ' WHERE id=' . (int) (($_GET['id'] ?? null)));
}
if ( ! empty($item_info)) {
db()->update('menu_items', ['active' => (int) ! $item_info['active']], 'id=' . (int) ($item_info['id']));
common()->admin_wall_add(['menu item: ' . $item_info['name'] . ' ' . ($item_info['active'] ? 'inactivated' : 'activated'), $item_info['id']]);
}
module('menus_editor')->_purge_caches();
if (is_ajax()) {
no_graphics(true);
echo $item_info['active'] ? 0 : 1;
} else {
return js_redirect('./?object=' . ($_GET['object'] ?? null) . '&action=show_items&id=' . $item_info['menu_id']);
}
}
public function active()
Change menu activity.
public function active()
{
if ( ! empty($_GET['id'])) {
$menu_info = db()->query_fetch('SELECT * FROM ' . db('menus') . ' WHERE id=' . (int) (($_GET['id'] ?? null)));
}
if ( ! empty($menu_info)) {
db()->UPDATE('menus', ['active' => (int) ! $menu_info['active']], 'id=' . (int) ($menu_info['id']));
common()->admin_wall_add(['menu: ' . $menu_info['name'] . ' ' . ($menu_info['active'] ? 'inactivated' : 'activated'), $menu_info['id']]);
module('menus_editor')->_purge_caches();
}
if (is_ajax()) {
no_graphics(true);
echo $menu_info['active'] ? 0 : 1;
} else {
return js_redirect(url('/@object'));
}
}
public function add()
public function add()
{
$a = $_POST;
$a['redirect_link'] = url('/@object');
return form($a, ['autocomplete' => 'off'])
->validate([
'name' => 'trim|required|is_unique[menus.name]',
'type' => 'trim|required',
])
->db_insert_if_ok('menus', ['type', 'name', 'desc', 'stpl_name', 'method_name', 'custom_fields', 'active'], [])
->on_after_update(function () {
common()->admin_wall_add(['menu added: ' . ($_POST['name'] ?? null) . '', db()->insert_id()]);
module('menus_editor')->_purge_caches();
})
->radio_box('type', ['user' => 'User', 'admin' => 'Admin'])
->text('name')
->text('desc', 'Description')
->text('stpl_name')
->text('method_name')
->text('custom_fields')
->active_box()
->save_and_back();
}
public function add_item()
public function add_item()
{
$menu_info = db()->get('SELECT * FROM ' . db('menus') . ' WHERE id=' . (int) (($_GET['id'] ?? null)));
if (empty($menu_info['id'])) {
return _e('No such menu!');
}
$_GET['id'] = (int) ($menu_info['id']);
$multi_selects = ['user_groups', 'site_ids', 'server_ids'];
if (main()->is_post()) {
$tmp = [];
foreach (explode(',', $menu_info['custom_fields']) as $field_name) {
if ($field_name && ($_POST['custom'][$field_name] ?? null)) {
$tmp[$field_name] = $field_name . '=' . ($_POST['custom'][$field_name] ?? null);
}
}
$_POST['other_info'] = implode(';' . PHP_EOL, $tmp);
foreach ($multi_selects as $k) {
$_POST[$k] = $this->_multi_html_to_db(($_POST[$k] ?? null));
}
}
$a = $_POST;
$a['redirect_link'] = './?object=' . ($_GET['object'] ?? null) . '&action=show_items&id=' . $menu_info['id'];
return form($a)
->validate([
'name' => 'trim|required',
])
->db_insert_if_ok('menu_items', ['type_id', 'parent_id', 'name', 'location', 'icon', 'user_groups', 'site_ids', 'server_ids', 'active', 'other_info'], ['menu_id' => $menu_info['id']])
->on_after_update(function () {
common()->admin_wall_add(['menu item added: ' . ($_POST['name'] ?? null) . '', db()->insert_id()]);
module('menus_editor')->_purge_caches();
})
->select_box('type_id', $this->_item_types)
->select_box('parent_id', $this->_get_parents_for_select($menu_info['id']), ['desc' => 'Parent item'])
->text('name')
->location_select_box('location')
->multi_select_box('user_groups', $this->_groups[$menu_info['type']], ['edit_link' => './?object=' . $menu_info['type'] . '_groups', 'desc' => 'Groups'])
->multi_select_box('site_ids', $this->_sites, ['edit_link' => './?object=manage_sites', 'desc' => 'Sites'])
->multi_select_box('server_ids', $this->_servers, ['edit_link' => './?object=manage_servers', 'desc' => 'Servers'])
->icon_select_box('icon')
->active_box()
->custom_fields('other_info', $menu_info['custom_fields'])
->save_and_back();
}
public function clone_item()
Clone menu item.
public function clone_item()
{
$_GET['id'] = (int) (($_GET['id'] ?? null));
if (empty($_GET['id'])) {
return _e('No id!');
}
$item_info = db()->query_fetch('SELECT * FROM ' . db('menu_items') . ' WHERE id=' . (int) (($_GET['id'] ?? null)));
if (empty($item_info['id'])) {
return _e('No such menu item!');
}
$menu_info = db()->query_fetch('SELECT * FROM ' . db('menus') . ' WHERE id=' . (int) ($item_info['menu_id']));
if (empty($menu_info['id'])) {
return _e('No such menu!');
}
$sql = $item_info;
unset($sql['id']);
db()->insert_safe('menu_items', $sql);
common()->admin_wall_add(['menu item cloned: ' . $item_info['name'] . '', $item_info['id']]);
module('menus_editor')->_purge_caches();
return js_redirect('./?object=' . ($_GET['object'] ?? null) . '&action=show_items&id=' . $menu_info['id']);
}
public function delete()
Delete menu and all sub items.
public function delete()
{
$_GET['id'] = (int) (($_GET['id'] ?? null));
if ( ! empty($_GET['id'])) {
$menu_info = db()->query_fetch('SELECT * FROM ' . db('menus') . ' WHERE id=' . (int) (($_GET['id'] ?? null)));
}
if ( ! empty($menu_info['id'])) {
db()->query('DELETE FROM ' . db('menus') . ' WHERE id=' . (int) (($_GET['id'] ?? null)) . ' LIMIT 1');
db()->query('DELETE FROM ' . db('menu_items') . ' WHERE menu_id=' . (int) (($_GET['id'] ?? null)));
common()->admin_wall_add(['menu deleted: ' . $menu_info['name'] . '', $menu_info['id']]);
module('menus_editor')->_purge_caches();
}
if (is_ajax()) {
no_graphics(true);
echo $_GET['id'] ?? null;
} else {
return js_redirect(url('/@object'));
}
}
public function delete_item()
public function delete_item()
{
$_GET['id'] = (int) (($_GET['id'] ?? null));
if ( ! empty($_GET['id'])) {
$item_info = db()->query_fetch('SELECT * FROM ' . db('menu_items') . ' WHERE id=' . (int) (($_GET['id'] ?? null)));
}
if ( ! empty($item_info)) {
db()->query('DELETE FROM ' . db('menu_items') . ' WHERE id=' . (int) (($_GET['id'] ?? null)));
db()->update('menu_items', ['parent_id' => 0], 'parent_id=' . (int) (($_GET['id'] ?? null)));
common()->admin_wall_add(['menu item deleted: ' . $item_info['name'] . '', $item_info['id']]);
}
module('menus_editor')->_purge_caches();
if (is_ajax()) {
no_graphics(true);
echo $_GET['id'] ?? null;
} else {
return js_redirect('./?object=' . ($_GET['object'] ?? null) . '&action=show_items&id=' . $item_info['menu_id']);
}
}
public function drag_items()
public function drag_items()
{
$cur_items = null;
if (empty($_GET['id'])) {
return _e('No id!');
}
$menu_info = db()->query_fetch('SELECT * FROM ' . db('menus') . ' WHERE id=' . (int) (($_GET['id'] ?? null)) . ' OR name="' . db()->es(($_GET['id'] ?? null)) . '"');
if (empty($menu_info)) {
return _e('No such menu!');
}
$items = $this->_show_menu([
'force_stpl_name' => ($_GET['object'] ?? null) . '/drag',
'name' => $menu_info['name'],
'return_array' => 1,
]);
if (main()->is_post()) {
$cur_items = $this->_auto_update_items_orders($menu_info['id']);
$batch = [];
foreach ((array) json_decode((string) ($_POST['items'] ?? null), $assoc = true) as $order_id => $info) {
$item_id = (int) $info['item_id'];
if ( ! $item_id || ! isset($items[$item_id])) {
continue;
}
$parent_id = (int) $info['parent_id'];
$new_data = [
'id' => $item_id,
'order' => (int) $order_id,
'parent_id' => (int) $parent_id,
];
$old_info = $cur_items[$item_id];
$old_data = [
'id' => $item_id,
'order' => (int) ($old_info['order']),
'parent_id' => (int) ($old_info['parent_id']),
];
if ($new_data != $old_data) {
$batch[$item_id] = $new_data;
}
}
if ($batch) {
db()->update_batch('menu_items', db()->es_for_builder($batch));
common()->admin_wall_add(['menu items dragged: ' . $menu_info['name'] . '', $menu_info['id']]);
module('menus_editor')->_purge_caches();
}
if (is_ajax()) {
no_graphics(true);
} else {
js_redirect(url('/@object/@action/@id/@page'));
}
return false;
}
if (isset($items[''])) {
unset($items['']);
}
$tpl_items = [];
foreach ((array) $items as $id => $item) {
if ( ! $id) {
continue;
}
$item['edit_link'] = './?object=' . ($_GET['object'] ?? null) . '&action=edit_item&id=' . $id;
$item['delete_link'] = './?object=' . ($_GET['object'] ?? null) . '&action=delete_item&id=' . $id;
$item['active_link'] = './?object=' . ($_GET['object'] ?? null) . '&action=activate_item&id=' . $id;
$item['clone_link'] = './?object=' . ($_GET['object'] ?? null) . '&action=clone_item&id=' . $id;
$tpl_items[$id] = tpl()->parse('@object/drag_item', $item);
}
$replace = [
'items' => implode(PHP_EOL, (array) $tpl_items),
'form_action' => './?object=' . ($_GET['object'] ?? null) . '&action=' . ($_GET['action'] ?? null) . '&id=' . ($_GET['id'] ?? null),
'add_link' => './?object=' . ($_GET['object'] ?? null) . '&action=add_item&id=' . ($_GET['id'] ?? null),
'back_link' => './?object=' . ($_GET['object'] ?? null) . '&action=show_items&id=' . ($_GET['id'] ?? null),
];
return tpl()->parse('@object/drag_main', $replace);
}
public function edit()
public function edit()
{
$id = (int) (($_GET['id'] ?? null));
if ( ! $id) {
return _e('No id');
}
$a = db()->from('menus')->whereid($id)->get();
$a['redirect_link'] = url('/@object');
return form($a, ['autocomplete' => 'off'])
->validate([
'name' => 'trim|required',
])
->db_update_if_ok('menus', ['name', 'desc', 'stpl_name', 'method_name', 'custom_fields', 'active'], 'id=' . $id)
->on_after_update(function () use ($id) {
common()->admin_wall_add(['menu updated: ' . $_POST['name'] . '', $id]);
module('menus_editor')->_purge_caches();
})
->info('type')
->text('name')
->text('desc', 'Description')
->text('stpl_name')
->text('method_name')
->text('custom_fields')
->active_box()
->save_and_back();
}
public function edit_item()
public function edit_item()
{
$item_info = db()->query_fetch('SELECT * FROM ' . db('menu_items') . ' WHERE id=' . (int) (($_GET['id'] ?? null)));
if (empty($item_info['id'])) {
return _e('No such menu item!');
}
$menu_info = db()->get('SELECT * FROM ' . db('menus') . ' WHERE id=' . (int) ($item_info['menu_id']));
if (empty($menu_info['id'])) {
return _e('No such menu!');
}
$_GET['id'] = (int) ($item_info['id']);
$multi_selects = ['user_groups', 'site_ids', 'server_ids'];
if (main()->is_post()) {
$tmp = [];
foreach (explode(',', $menu_info['custom_fields']) as $field_name) {
if ($field_name && ($_POST['custom'][$field_name] ?? null)) {
$tmp[$field_name] = $field_name . '=' . ($_POST['custom'][$field_name] ?? null);
}
}
$_POST['other_info'] = implode(';' . PHP_EOL, $tmp);
foreach ($multi_selects as $k) {
$_POST[$k] = $this->_multi_html_to_db(($_POST[$k] ?? null));
}
} else {
foreach ($multi_selects as $k) {
$item_info[$k] = $this->_multi_db_to_html($item_info[$k]);
}
}
$a = $item_info;
$a['redirect_link'] = './?object=' . ($_GET['object'] ?? null) . '&action=show_items&id=' . $menu_info['id'];
return form($a)
->validate([
'name' => 'trim|required',
])
->db_update_if_ok('menu_items', [
'type_id', 'parent_id', 'name', 'location', 'icon', 'user_groups', 'site_ids', 'server_ids', 'active', 'other_info',
], 'id=' . $item_info['id'])
->on_after_update(function () use ($item_info) {
common()->admin_wall_add(['menu item updated: ' . ($_POST['name'] ?? null) . '', $item_info['id']]);
module('menus_editor')->_purge_caches();
})
->select_box('type_id', $this->_item_types)
->select_box('parent_id', $this->_get_parents_for_select($menu_info['id'], $item_info['id']), ['desc' => 'Parent item'])
->text('name')
->location_select_box('location')
->multi_select_box('user_groups', $this->_groups[$menu_info['type']], ['edit_link' => './?object=' . $menu_info['type'] . '_groups', 'desc' => 'Groups'])
->multi_select_box('site_ids', $this->_sites, ['edit_link' => './?object=manage_sites', 'desc' => 'Sites'])
->multi_select_box('server_ids', $this->_servers, ['edit_link' => './?object=manage_servers', 'desc' => 'Servers'])
->icon_select_box('icon')
->active_box()
->custom_fields('other_info', $menu_info['custom_fields'])
->save_and_back();
}
public function export()
Export menu items.
public function export()
{
$_GET['id'] = (int) (($_GET['id'] ?? null));
$menu_info = db()->query_fetch('SELECT * FROM ' . db('menus') . ' WHERE id=' . (int) (($_GET['id'] ?? null)));
$params = [
'single_table' => '',
'tables' => [db('menus'), db('menu_items')],
'full_inserts' => 1,
'ext_inserts' => 1,
'export_type' => 'insert',
'silent_mode' => true,
];
if ($menu_info['id']) {
$params['where'] = [
db('menus') => 'id=' . (int) ($menu_info['id']),
db('menu_items') => 'menu_id=' . (int) ($menu_info['id']),
];
}
$EXPORTED_SQL = module('db_manager')->export($params);
$replace = [
'sql_text' => _prepare_html($EXPORTED_SQL, 0),
'back_link' => url('/@object'),
];
return tpl()->parse('db_manager/export_text_result', $replace);
}
public function show()
Display menus.
public function show()
{
$num_items = db()->get_2d('SELECT m.id, COUNT(i.id) AS num FROM ' . db('menus') . ' AS m LEFT JOIN ' . db('menu_items') . ' AS i ON m.id = i.menu_id GROUP BY m.id');
$data = db()->from('menus')->order_by('name ASC, type DESC')->get_all();
return table($data, [
'pager_records_on_page' => 1000,
'group_by' => 'name',
'hide_empty' => 1,
])
->link('name', url('/@object/show_items/%d'))
->text('id', 'Num Items', ['data' => $num_items])
->text('type')
->text('stpl_name')
->text('method_name')
->text('custom_fields')
->btn('Drag items', url('/@object/drag_items/%id'), ['icon' => 'icon-move fa fa-arrows', 'btn_no_text' => 1])
->btn('View Items', url('/@object/show_items/%d'), ['btn_no_text' => 1])
->btn_clone('', url('/@object/clone_menu/%d'), ['btn_no_text' => 1])
->btn_edit(['no_ajax' => 1, 'btn_no_text' => 1])
->btn_delete(['btn_no_text' => 1])
->btn_active()
->footer_add(['no_ajax' => 1]);
}
public function show_items()
Display menu items for the given.
public function show_items()
{
$menu_info = db()->query_fetch('SELECT * FROM ' . db('menus') . ' WHERE id=' . (int) (($_GET['id'] ?? null)) . ' OR name="' . db()->es(($_GET['id'] ?? null)) . '"');
if (empty($menu_info)) {
return _e('No such menu!');
}
$_GET['id'] = (int) ($menu_info['id']);
$menu_items = $this->_auto_update_items_orders($menu_info['id']);
if (main()->is_post()) {
$batch = [];
foreach ((array) $menu_items as $a) {
if ( ! isset($_POST['name'][$a['id']])) {
continue;
}
$batch[$a['id']] = [
'id' => $a['id'],
'name' => ($_POST['name'][$a['id']] ?? null),
'location' => ($_POST['location'][$a['id']] ?? null),
];
}
if ($batch) {
db()->update_batch('menu_items', db()->es_for_builder($batch));
common()->admin_wall_add(['menu items updated: ' . $menu_info['name'] . '', $menu_info['id']]);
module('menus_editor')->_purge_caches();
}
return js_redirect('./?object=' . ($_GET['object'] ?? null) . '&action=show_items&id=' . ($_GET['id'] ?? null));
}
$groups = $this->_groups[$menu_info['type']];
return table($menu_items, ['pager_records_on_page' => 10000, 'condensed' => 1, 'hide_empty' => 1])
->form()
->icon('icon')
->input_padded('name', ['width' => '300'])
->input('location', ['width' => '300'])
->text('type_id', 'Item type', ['data' => $this->_item_types, 'nowrap' => 1])
->data('user_groups', $groups, ['desc' => 'Groups'])
->data('sites', $this->_sites)
->data('servers', $this->_servers)
->text('other_info')
->btn_edit('', url('/@object/edit_item/%d'), ['no_ajax' => 1, 'btn_no_text' => 1])
->btn_delete('', url('/@object/delete_item/%d'), ['btn_no_text' => 1])
->btn_clone('', url('/@object/clone_item/%d'), ['btn_no_text' => 1])
->btn_active('', url('/@object/activate_item/%d'))
->footer_add('Add item', url('/@object/add_item/@id'), ['copy_to_header' => 1, 'no_ajax' => 1])
->footer_link('Drag items', url('/@object/drag_items/@id'), ['icon' => 'icon-move fa fa-arrows', 'copy_to_header' => 1, 'no_ajax' => 1])
->footer_submit();
}
_ hooks and helpers; not routable as page actionspublic function _auto_update_items_orders($menu_id)
public function _auto_update_items_orders($menu_id)
{
if ( ! $menu_id) {
return false;
}
$menu_items = $this->_recursive_get_menu_items($menu_id);
$new_order = 1;
$batch = [];
foreach ((array) $menu_items as $item_id => $info) {
if ( ! $info) {
continue;
}
if ($info['order'] != $new_order) {
$batch[$item_id] = [
'id' => $item_id,
'order' => $new_order,
];
$menu_items[$item_id]['order'] = $new_order;
}
$new_order++;
}
if ($batch) {
db()->update_batch('menu_items', $batch);
}
return $menu_items;
}
public function _count_levels(&$children, $start_id = 0, $level = 0)
public function _count_levels(&$children, $start_id = 0, $level = 0)
{
$ids = [];
foreach ((array) $children[$start_id] as $id => $_tmp) {
$ids[$id] = $level;
if (isset($children[$id])) {
foreach ((array) $this->_count_levels($children, $id, $level + 1) as $_id => $_level) {
$ids[$_id] = $_level;
}
}
}
return $ids;
}
public function _get_parents_for_select($menu_id, $skip_id = null)
public function _get_parents_for_select($menu_id, $skip_id = null)
{
$data = [0 => '-- TOP --'];
foreach ((array) $this->_recursive_get_menu_items($menu_id/*, $skip_id*/) as $cur_item_id => $cur_item_info) {
if (empty($cur_item_id)) {
continue;
}
if ($skip_id && $cur_item_id == $skip_id) {
continue;
}
$data[$cur_item_id] = str_repeat(' ', $cur_item_info['level']) . ' └ ' . $cur_item_info['name'];
}
return $data;
}
public function _get_stpls($type = 'user')
Get array of templates for the given init type.
public function _get_stpls($type = 'user')
{
return module('template_editor')->_get_stpls_for_type($type);
}
public function _init()
public function _init()
{
$array_all = ['' => '-- ALL --'];
$this->_groups = [
'user' => $array_all + (array) db()->get_2d('SELECT id,name FROM ' . db('user_groups') . ' WHERE active="1"'),
'admin' => $array_all + (array) db()->get_2d('SELECT id,name FROM ' . db('admin_groups') . ' WHERE active="1"'),
];
$this->_sites = $array_all + (array) db()->get_2d('SELECT id,name FROM ' . db('sites') . ' WHERE active="1"');
$this->_servers = $array_all + (array) db()->get_2d('SELECT id,name FROM ' . db('core_servers') . ' WHERE active="1"');
$this->_menu_types = [
'user' => 'user',
'admin' => 'admin',
];
$this->_item_types = [
1 => 'Internal link',
2 => 'External link',
3 => 'Spacer',
4 => 'Divider',
];
}
public function _multi_db_to_html($input = '')
public function _multi_db_to_html($input = '')
{
if ( ! is_array($input)) {
$input = explode(',', str_replace([' ', "\t", "\r", "\n", ',,'], '', $input));
}
$output = [];
foreach ((array) $input as $v) {
if ($v) {
$output[$v] = $v;
}
}
return (array) $output;
}
public function _multi_html_to_db($input = [])
public function _multi_html_to_db($input = [])
{
if (is_array($input)) {
$input = ',' . implode(',' . PHP_EOL, $input) . ',';
}
return (string) str_replace([' ', "\t", "\r", ',,'], '', $input);
}
public function _on_before_redirect()
Execute this before redirect.
public function _on_before_redirect()
{
if (defined('ADMIN_FRAMESET_MODE')) {
$_SESSION['_menu_js_refresh_frameset'] = true;
}
}
public function _purge_caches()
public function _purge_caches()
{
cache_del(['menus', 'menu_items']);
}
public function _recursive_sort_items($items = [], $skip_item_id = 0, $parent_id = 0, $level = 0)
Get and sort items ordered array (recursively).
public function _recursive_sort_items($items = [], $skip_item_id = 0, $parent_id = 0, $level = 0)
{
$children = [];
foreach ((array) $items as $id => $info) {
$parent_id = $info['parent_id'];
if ($skip_item_id == $id) {
continue;
}
$children[$parent_id][$id] = $id;
}
$ids = $this->_count_levels($children, 0);
$new_items = [];
foreach ((array) $ids as $id => $level) {
$new_items[$id] = $items[$id] + ['level' => $level];
}
return $new_items;
}