“中国要复兴、富强,必须在开源软件领域起到主导作用,为了国家安全和人类发展,责无旁贷,我们须为此而奋斗”——By:云客
系统管理工具栏toolbar是指用户登录后页面顶部的黑色条区域,后简称工具栏,里面列出了系统最重要的一些链接,默认有:管理、快捷方式、用户等,点击这些链接后会展开显示对应的子链接。注意这里并没有将工具栏里面的链接称为菜单,在drupal中菜单有明确的定义(请见本系列后续的菜单系统介绍),默认情况下工具栏用到了菜单,但她本身不是菜单,先明确这一点以便于理解一些概念。
工具栏由两部分构成:
tab:工具栏里面的链接,可以通过附加类属性的方式追加显示图标
tray:这是可选的,代表点击tab中的链接后展开显示的对应区域(tab的托盘),里面可以放置子链接或其它内容;如果省略tray,那么点击tab后将直接打开其链接,否则点击事件被拦截并展开tray显示。
模块可以向工具栏中添加tab和其对应的tray
添加工具栏:
整个管理工具栏是通过钩子“page_top”添加的,该钩子在HTML渲染器的以下方法中派发:
\Drupal\Core\Render\MainContent\HtmlRenderer::buildPageTopAndBottom
运行时机在块系统运行后,她向#type为html的渲染数组添加子元素page_top,工具栏就位于该子元素中(html渲染数组默认有三个子元素page_top 、page、page_bottom),工具栏由核心模块toolbar添加,钩子函数如下:
function toolbar_page_top(array &$page_top) {
$page_top['toolbar'] = [
'#type' => 'toolbar',
'#access' => \Drupal::currentUser()->hasPermission('access toolbar'),
'#cache' => [
'keys' => ['toolbar'],
'contexts' => ['user.permissions'],
],
];
}
由该钩子可见,工具栏并不是依据用户是否登录来判断显示的,只要具备'access toolbar'权限即显示。
添加工具栏链接:
从上面的钩子可知道管理工具栏由toolbar元素类型的渲染数组产生,所有工作被封装到该元素类型中,该元素类型类如下:
\Drupal\toolbar\Element\Toolbar
在其中将派发toolbar钩子及其修改钩子来构建工具栏,如果模块需向工具栏添加链接可以实现该钩子,这里假设模块名为yunke,添加菜单的钩子示例如下:
function yunke_toolbar()
{
$items['yunke'] = [
'#type' => 'toolbar_item',
'tab' => [
'#type' => 'link',
'#title' => "云客",
'#url' => \Drupal\Core\Url::fromRoute('<front>'),
'#attributes' => [
'title' => "工具栏顶级链接",
'class' => ['toolbar-icon'], //如需图标,则在此处补充具体的图标类
],
],
'#weight' => 10000, //菜单排序 越大越靠后
];
// tab如不需要托盘区域,那么在此处直接返回$items即可,不需要以下内容,
//此时tab链接为以上'#url'指定的地址,以下构建tray托盘区域
$items['yunke']['tray'] = [
'#heading' => '托盘区域', //一个隐藏的内容,用于辅助视障设备
];
//列出以下子链接,注意以下将采用links主题钩子,因此不是以属性方式传递
$links = [
'index' => [
'title' => "链接一",
'url' => \Drupal\Core\Url::fromRoute('<front>'),
],
'info' => [
'title' => "链接二",
'url' => \Drupal\Core\Url::fromRoute('<front>'),
],
'resources' => [
'title' => "链接三",
'url' => \Drupal\Core\Url::fromRoute('<front>'),
],
];
$items['yunke']['tray']['group1']=[
'#theme' => 'links__toolbar_yunke', //默认将采用links主题钩子
'#links' => $links,
'#attributes' => [
'class' => ['toolbar-menu'],
],
];
$items['yunke']['tray']['group2']=[ //可以根据需要给菜单分组
'#type' => 'link',
'#title' => '子菜单组',
'#url' => \Drupal\Core\Url::fromRoute('<front>'),
];
//还可以在$items['yunke']['tray']中追加任意需要显示的内容
return $items;
}
以上列子yunke模块只添加了一个tab,如需多个只需在$items中添加多个子元素即可
调整工具栏:
模块可以实现toolbar修改钩子来调整工具栏,如排序、删除其他模块的工具栏设置等等,这里以排序做一个演示,假设模块名为yunke:
function yunke_toolbar_alter(&$items)
{
$items['administration']['#weight']=999;
}
这将使“管理”链接排到“用户”后面,这里需要注意:如果你想通过该钩子去调整其他模块提供的工具栏元素,还需要视情况而定,在使用了延迟构建#lazy_builder或渲染前处理器#pre_render时,有些链接可能尚未产生,通常这种情况下需要用到对应模块自己派发的修改钩子,比如这里你并不能调整管理菜单的内容,而需要通过菜单系统来调整。
删除整个工具栏:
添加整个工具栏是在钩子“page_top”中,那么删除她也可以实现该钩子,这里介绍一种方法,假设模块名为“yunke_help”,如下:
function yunke_help_page_top(array &$page_top)
{
$route_name = \Drupal::routeMatch()->getRouteName();
if ($route_name != "yunke_help.index") {
return;
}
if (isset($page_top["#pre_render"]) && is_array($page_top["#pre_render"])) {
$page_top["#pre_render"][] = "delete_toolbar";
} else {
$page_top["#pre_render"] = ["delete_toolbar"];
}
}
function delete_toolbar($e)
{
unset($e['toolbar']);
return $e;
}
以上方法用到了#pre_render,你可能会想到为什么不在page_top钩子中直接unset变量$page_top['toolbar'],这是因为我们无法保证各模块钩子的执行顺序,执行时可能还未产生,且系统也没有派发对应的page_top修改钩子。
我们来看一看如下的toolbar修改钩子是否能实现删除功能:
function yunke_toolbar_alter(&$items)
{
$items['#access']=false;
}
这是不行的,因为该钩子在工具栏渲染数组的#pre_render中运行,此时权限检查已经执行过了。但如果改为以下逻辑将可以实现删除:
function yunke_toolbar_alter(&$items)
{
$items = [];
$items['#cache'] = []; //禁用工具栏渲染数组缓存
}
该方法虽然能够实现删除,但不如以上采用#pre_render的方法优雅,且浪费性能。
系统管理菜单:
工具栏中的管理链接是真正的菜单,内容由菜单系统提供,工具栏tab由toolbar模块在toolbar_toolbar()钩子中添加,关于该内容请阅读本系列菜单主题。
反馈互动