Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176

This commit is contained in:
Pantheon Automation 2015-08-17 17:00:26 -07:00 committed by Greg Anderson
commit 9921556621
13277 changed files with 1459781 additions and 0 deletions

View file

@ -0,0 +1,2 @@
id: default
label: Default

View file

@ -0,0 +1,21 @@
# Schema for the configuration files of the Shortcut module.
shortcut.set.*:
type: config_entity
label: 'Shortcut settings'
mapping:
id:
type: string
label: 'ID'
label:
type: label
label: 'Label'
# Schema for theme settings.
theme_settings.third_party.shortcut:
type: mapping
label: 'Shortcut settings'
mapping:
module_link:
type: boolean
label: 'Add shortcut link'

View file

@ -0,0 +1,40 @@
/**
* @file
* Styling for the shortcut module icons.
*/
/**
* Toolbar tab icon.
*/
.toolbar-bar .toolbar-icon-shortcut:before {
background-image: url(../../../misc/icons/bebebe/star.svg);
}
.toolbar-bar .toolbar-icon-shortcut:active:before,
.toolbar-bar .toolbar-icon-shortcut.is-active:before {
background-image: url(../../../misc/icons/ffffff/star.svg);
}
/**
* Add/remove links.
*/
.shortcut-action__icon {
background: transparent url(../images/favstar.svg) no-repeat left top;
width: 20px;
height: 20px;
display: inline-block;
vertical-align: -2px;
}
[dir="rtl"] .shortcut-action__icon {
background-image: url(../images/favstar-rtl.svg);
}
.shortcut-action--add:hover .shortcut-action__icon,
.shortcut-action--add:focus .shortcut-action__icon {
background-position: -20px top;
}
.shortcut-action--remove .shortcut-action__icon {
background-position: -40px top;
}
.shortcut-action--remove:focus .shortcut-action__icon,
.shortcut-action--remove:hover .shortcut-action__icon {
background-position: -60px top;
}

View file

@ -0,0 +1,62 @@
/**
* @file
* Styling for the shortcut module.
*/
/**
* Toolbar.
*/
.toolbar .toolbar-tray-vertical .edit-shortcuts {
text-align: right; /* LTR */
padding: 1em;
}
[dir="rtl"] .toolbar .toolbar-tray-vertical .edit-shortcuts {
text-align: left;
}
.toolbar .toolbar-tray-horizontal .edit-shortcuts {
float: right; /* LTR */
}
[dir="rtl"] .toolbar .toolbar-tray-horizontal .edit-shortcuts {
float: left;
}
/**
* Add/remove links.
*/
.shortcut-action {
display: inline-block;
margin-left: 0.3em; /* LTR */
}
[dir="rtl"] .shortcut-action {
margin-left: 0;
margin-right: 0.3em;
}
.shortcut-action__message {
background: #000000;
background: rgba(0, 0, 0, 0.5);
border-radius: 5px;
padding: 0 5px;
color: #ffffff;
display: inline-block;
margin-left: 0.3em; /* LTR */
opacity: 0;
-ms-transform: translateY(-12px);
-webkit-transform: translateY(-12px);
transform: translateY(-12px);
-webkit-transition: all 200ms ease-out;
transition: all 200ms ease-out;
-ms-backface-visibility: hidden;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
[dir="rtl"] .shortcut-action__message {
margin-left: 0;
margin-right: 0.3em;
}
.shortcut-action:hover .shortcut-action__message,
.shortcut-action:focus .shortcut-action__message {
opacity: 1;
-ms-transform: translateY(-2px);
-webkit-transform: translateY(-2px);
transform: translateY(-2px);
}

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="80" height="20" xmlns="http://www.w3.org/2000/svg">
<g>
<g id="svg_1">
<path id="svg_2" d="m70.39463,2.259c-0.149605,-0.385 -0.395561,-0.385 -0.545135,0l-1.966644,5.049c-0.149582,0.385 -0.611374,0.699 -1.024971,0.699l-1.756836,0.169c-0.413605,0 -0.494911,0.219001 -0.180702,0.487l1.315094,0.981c0.31424,0.268 0.498962,0.816 0.408607,1.22l-1.477745,6.562c-0.090355,0.403 0.118454,0.546999 0.464806,0.319l3.862999,-2.539001c0.346359,-0.228001 0.910545,-0.228001 1.256889,0l3.863014,2.539001c0.346344,0.227999 0.555161,0.084 0.464806,-0.319l-1.051086,-4.707001c-0.090355,-0.403 0.094368,-0.952 0.408577,-1.22l3.531731,-3.006c0.314209,-0.268 0.232903,-0.487 -0.180702,-0.487l-4.398079,0c-0.413612,0 -0.875404,-0.314 -1.024986,-0.698999l-1.969635,-5.048z" fill="#FEF6A8"/>
<polygon id="svg_3" points="62.545124552483685,5 65.1010474534105,7.546999931335449 67.6569842932513,5 69.07749901699208,6.414999961853027 66.52157611606526,8.961000442504883 69.07448821157232,11.505000114440918 67.65596675253073,12.918999671936035 65.10203711630311,10.375 62.54711781718288,12.918999671936035 61.12760669524869,11.505000114440918 63.682525994368916,8.960000038146973 61.12559949163551,6.414000034332275 " fill="#807640"/>
<path id="svg_4" d="m64.930397,13.897s0.133507,-0.516001 0.653519,-0.349c0.518013,0.165999 0.303192,0.674 0.303192,0.674l-0.721832,3.204c-0.061234,0.272999 0.01609,0.427999 0.178696,0.427999c0.078323,0 0.174698,-0.035 0.28614,-0.107l3.863007,-2.539c0.174667,-0.115 0.401566,-0.172 0.62944,-0.172s0.45475,0.057 0.627426,0.172l3.863022,2.539c0.111427,0.072001 0.209808,0.107 0.28611,0.107c0.164627,0 0.240929,-0.153999 0.178696,-0.427999l-1.051094,-4.707001c-0.090347,-0.401999 0.094368,-0.951 0.409584,-1.219l3.531731,-3.007c0.315224,-0.269 0.232903,-0.487 -0.180702,-0.487l-4.400085,0c-0.413628,0 -0.874405,-0.314 -1.024986,-0.698999l-1.966637,-5.049c-0.075302,-0.192 -0.174683,-0.287 -0.273064,-0.287s-0.197769,0.096 -0.272064,0.288l-0.462784,1.158s-0.256004,0.409 -0.637497,0.219c-0.441711,-0.221 -0.227875,-0.66 -0.227875,-0.66l0.390511,-1.078c0.337311,-0.861 1.01194,-0.927 1.209709,-0.927s0.872391,0.066 1.20768,0.926l1.968658,5.049l0.109421,0.062l4.378006,-0.001c0.937645,0 1.234795,0.609 1.306076,0.796c0.06826,0.187 0.23893,0.844 -0.472839,1.451l-3.533722,3.007c-0.037148,0.041 -0.086334,0.186 -0.078308,0.25l1.049088,4.698999c0.133507,0.596001 -0.051216,0.984001 -0.229912,1.205002c-0.225868,0.278999 -0.563187,0.438999 -0.929596,0.438999c-0.280106,0 -0.563194,-0.092001 -0.839264,-0.273001l-3.863014,-2.539l0,0.002001l-0.074287,-0.008001l-0.088341,0.014l-3.85096,2.531c-0.274071,0.182001 -0.559174,0.273001 -0.839264,0.273001c-0.364418,0 -0.703735,-0.16 -0.927589,-0.438999c-0.178711,-0.221001 -0.362411,-0.609001 -0.230904,-1.205002l0.74691,-3.312999z" fill="#80722D"/>
</g>
<g id="svg_5">
<path id="svg_6" d="m54.758999,18.355c-0.18,0 -0.368999,-0.063999 -0.560997,-0.191l-3.848003,-2.539c-0.083,-0.055 -0.215,-0.088 -0.350998,-0.088s-0.268002,0.033 -0.351002,0.088l-3.848,2.539c-0.190998,0.127001 -0.381001,0.191 -0.561001,0.191c-0.211998,0 -0.406998,-0.091999 -0.535,-0.251999c-0.108997,-0.136002 -0.219997,-0.383001 -0.130997,-0.784l1.046997,-4.708c0.049999,-0.222 -0.070999,-0.584 -0.243,-0.731l-3.516998,-3.006001c-0.451,-0.386 -0.378002,-0.756 -0.326,-0.896999c0.053001,-0.141 0.237,-0.47 0.831001,-0.47l4.380997,0c0.206001,0 0.480003,-0.188 0.556,-0.38l1.961002,-5.049c0.213001,-0.549 0.586998,-0.608 0.737,-0.608s0.523998,0.059 0.737,0.607l1.959,5.049c0.075001,0.191 0.350002,0.38 0.556,0.38l4.382999,0c0.594002,0 0.778,0.329 0.831001,0.47c0.052002,0.142 0.125,0.512 -0.326,0.897l-3.517998,3.006c-0.173,0.147 -0.294003,0.51 -0.244003,0.731l1.047001,4.708001c0.089001,0.400999 -0.021,0.646999 -0.131001,0.783998c-0.127998,0.162001 -0.322998,0.253 -0.535,0.253z" fill="#FEF6A8"/>
<path id="svg_7" d="m50,1.97c0.098999,0 0.195999,0.096 0.271,0.289l1.959,5.049c0.149002,0.385 0.609001,0.699 1.021,0.699l4.382999,0c0.412003,0 0.493,0.219 0.18,0.487l-3.516998,3.006c-0.313,0.268 -0.497002,0.816 -0.407001,1.22l1.047001,4.707999c0.061001,0.273001 -0.014999,0.428001 -0.178001,0.428001c-0.077,0 -0.174,-0.035002 -0.285,-0.108002l-3.848,-2.539c-0.173,-0.113 -0.398998,-0.171 -0.625999,-0.171s-0.452999,0.058 -0.625999,0.171l-3.848,2.539c-0.111,0.073 -0.208,0.108002 -0.285,0.108002c-0.163002,0 -0.238003,-0.154001 -0.178001,-0.428001l1.047001,-4.707999c0.09,-0.403 -0.094002,-0.952001 -0.407001,-1.22l-3.517998,-3.006c-0.313,-0.268001 -0.232002,-0.487 0.18,-0.487l4.380997,0c0.412003,0 0.872002,-0.314 1.021,-0.699l1.961002,-5.049c0.076,-0.193 0.173,-0.289 0.271999,-0.289m0,-1c-0.198002,0 -0.869999,0.067 -1.203999,0.927l-1.961002,5.049l-0.108997,0.062l-4.360001,-0.001c-0.934002,0 -1.231003,0.61 -1.299999,0.797c-0.069,0.187 -0.239002,0.844 0.470997,1.451l3.518002,3.005c0.039001,0.043 0.085999,0.186 0.078999,0.25l-1.045998,4.701c-0.132,0.594999 0.051998,0.983 0.229,1.205c0.223999,0.278999 0.560997,0.438999 0.924999,0.438999c0.279999,0 0.562,-0.093 0.835999,-0.272999l3.848,-2.539001l0.000999,0.001001l0.074001,-0.007l0.089001,0.014l3.834,2.531c0.274002,0.181 0.556999,0.272999 0.835999,0.272999c0.364002,0 0.701,-0.16 0.925003,-0.438999c0.177998,-0.222 0.361,-0.610001 0.229,-1.206001l-1.046001,-4.706999c-0.006001,-0.058001 0.041,-0.2 0.085999,-0.248l3.512001,-3c0.709999,-0.607 0.540001,-1.264 0.471001,-1.451c-0.068001,-0.187 -0.366001,-0.797 -1.299999,-0.797l-4.382999,0l-0.098003,-0.08l-1.949997,-5.03c-0.336002,-0.86 -1.007999,-0.927 -1.206001,-0.927z" fill="#80722D"/>
</g>
<g id="svg_8">
<path id="svg_9" d="m24.614159,14.96s0.134195,-0.516 0.656843,-0.349c0.520624,0.166 0.304714,0.674 0.304714,0.674l-0.481285,2.142c-0.06155,0.273001 0.016146,0.427999 0.179598,0.427999c0.078697,0 0.175564,-0.035 0.287556,-0.107l3.882521,-2.539c0.175562,-0.115 0.403591,-0.172 0.632626,-0.172s0.457062,0.057 0.630606,0.172l3.88253,2.539c0.111988,0.072001 0.210865,0.107 0.287552,0.107c0.165478,0 0.242153,-0.153999 0.179596,-0.427999l-1.056396,-4.707c-0.090809,-0.402 0.094849,-0.951 0.411667,-1.219l3.549557,-3.007c0.316818,-0.269 0.234085,-0.487 -0.181614,-0.487l-4.422318,0c-0.415699,0 -0.878819,-0.314 -1.030159,-0.699l-1.976582,-5.049c-0.075674,-0.193 -0.17556,-0.288 -0.274439,-0.288s-0.198767,0.096 -0.27343,0.288l-1.389357,3.538s-0.257284,0.409 -0.640696,0.219c-0.443943,-0.221 -0.229034,-0.66 -0.229034,-0.66l0.612446,-1.658l0.703257,-1.801c0.340021,-0.86 1.018047,-0.926 1.216814,-0.926s0.876799,0.066 1.213793,0.926l1.9786,5.049l0.10997,0.062l4.400127,-0.001c0.942379,0 1.241035,0.609 1.312668,0.796c0.068615,0.187 0.240135,0.844 -0.475224,1.451l-3.551575,3.007c-0.037338,0.041 -0.086777,0.186 -0.078701,0.25l1.054375,4.698999c0.134193,0.596001 -0.051464,0.984001 -0.231056,1.205002c-0.22702,0.278999 -0.566032,0.438999 -0.934299,0.438999c-0.281509,0 -0.566044,-0.092001 -0.843502,-0.273001l-3.882523,-2.539l0,0.002001l-0.074667,-0.008001l-0.088793,0.014l-3.870409,2.531c-0.275454,0.182001 -0.562002,0.273001 -0.843506,0.273001c-0.366257,0 -0.707291,-0.16 -0.932289,-0.438999c-0.179598,-0.221001 -0.364237,-0.609001 -0.232063,-1.205002l0.506502,-2.249999z" fill="#5A563B" opacity="0.7"/>
<polygon id="svg_10" points="20.99598980255405,7.960000038146973 24.022905373148888,7.960000038146973 24.022905373148888,4.960000038146973 26.040840745155492,4.960000038146973 26.040840745155492,7.960000038146973 29.06775631575033,7.960000038146973 29.06775631575033,9.958999633789062 26.040840745155492,9.958999633789062 26.040840745155492,12.960000038146973 24.022905373148888,12.960000038146973 24.022905373148888,9.958999633789062 20.99598980255405,9.958999633789062 " fill="#807640"/>
</g>
<path id="svg_11" d="m10,1.97c0.098,0 0.197,0.096 0.271,0.289l1.959,5.049c0.149,0.385 0.609,0.699 1.021001,0.699l4.383,0c0.411999,0 0.493,0.219 0.179998,0.487l-3.516999,3.006c-0.313,0.268 -0.497,0.816 -0.407,1.22l1.047,4.707c0.061,0.273001 -0.015,0.427999 -0.178,0.427999c-0.077,0 -0.174,-0.035 -0.285,-0.108l-3.848,-2.539c-0.172,-0.114 -0.399,-0.171 -0.626,-0.171s-0.454,0.057 -0.626,0.171l-3.848,2.539c-0.111,0.073 -0.208,0.108 -0.285,0.108c-0.163,0 -0.239,-0.153999 -0.178,-0.427999l1.047,-4.707c0.09,-0.403 -0.094,-0.951 -0.407,-1.22l-3.518,-3.006c-0.313,-0.268001 -0.233,-0.487 0.18,-0.487l4.382,0c0.413,0 0.872,-0.314 1.021,-0.699l1.96,-5.049c0.075001,-0.193 0.173,-0.289 0.272,-0.289m0,-1c-0.198,0 -0.87,0.067 -1.204,0.927l-1.96,5.049l-0.109,0.062l-4.362,-0.001c-0.934,0 -1.231,0.61 -1.3,0.796c-0.069,0.187 -0.239,0.844 0.47,1.451l3.519,3.007c0.038,0.041 0.086,0.185 0.079,0.249001l-1.046,4.699999c-0.133,0.595001 0.051,0.983002 0.229,1.205002c0.224,0.278999 0.561,0.438999 0.925,0.438999c0.28,0 0.562,-0.093 0.836,-0.274l3.848,-2.538l0,0.000999l0.075,-0.007l0.088,0.014l3.834,2.531c0.274,0.181 0.557,0.273001 0.836,0.273001c0.363999,0 0.700999,-0.16 0.924999,-0.438999c0.178,-0.222 0.361001,-0.610001 0.229,-1.206001l-1.047,-4.705999c-0.006,-0.058001 0.041,-0.2 0.086,-0.248l3.511999,-3c0.710001,-0.607 0.540001,-1.264 0.471001,-1.451c-0.068001,-0.187 -0.365999,-0.797 -1.299999,-0.797l-4.383,0l-0.098001,-0.08l-1.95,-5.03c-0.333,-0.86 -1.005,-0.927 -1.203,-0.927z" fill="#5A563B" opacity="0.7"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

@ -0,0 +1,44 @@
<?php
/**
* @file
* Hooks provided by the Shortcut module.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Return the name of a default shortcut set for the provided user account.
*
* This hook allows modules to define default shortcut sets for a particular
* user that differ from the site-wide default (for example, a module may want
* to define default shortcuts on a per-role basis).
*
* The default shortcut set is used only when the user does not have any other
* shortcut set explicitly assigned to them.
*
* Note that only one default shortcut set can exist per user, so when multiple
* modules implement this hook, the last (i.e., highest weighted) module which
* returns a valid shortcut set name will prevail.
*
* @param $account
* The user account whose default shortcut set is being requested.
* @return
* The name of the shortcut set that this module recommends for that user, if
* there is one.
*/
function hook_shortcut_default_set($account) {
// Use a special set of default shortcuts for administrators only.
$roles = \Drupal::entityManager()->getStorage('user_role')->loadByProperties(['is_admin' => TRUE]);
$user_admin_roles = array_intersect(array_keys($roles), $account->getRoles());
if ($user_admin_roles) {
return 'admin-shortcuts';
}
}
/**
* @} End of "addtogroup hooks".
*/

View file

@ -0,0 +1,9 @@
name: Shortcut
type: module
description: 'Allows users to manage customizable lists of shortcut links.'
package: Core
version: VERSION
core: 8.x
configure: entity.shortcut_set.collection
dependencies:
- link

View file

@ -0,0 +1,69 @@
<?php
/**
* @file
* Install, update and uninstall functions for the shortcut module.
*/
/**
* Implements hook_schema().
*/
function shortcut_schema() {
$schema['shortcut_set_users'] = array(
'description' => 'Maps users to shortcut sets.',
'fields' => array(
'uid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => 'The {users}.uid for this set.',
),
'set_name' => array(
'type' => 'varchar_ascii',
'length' => 32,
'not null' => TRUE,
'default' => '',
'description' => "The {shortcut_set}.set_name that will be displayed for this user.",
),
),
'primary key' => array('uid'),
'indexes' => array(
'set_name' => array('set_name'),
),
'foreign keys' => array(
'set_user' => array(
'table' => 'users',
'columns' => array('uid' => 'uid'),
),
'set_name' => array(
'table' => 'shortcut_set',
'columns' => array('set_name' => 'set_name'),
),
),
);
return $schema;
}
/**
* Implements hook_install().
*/
function shortcut_install() {
// Theme settings are not configuration entities and cannot depend on modules
// so to set a module-specific setting, we need to set it with logic.
if (\Drupal::service('theme_handler')->themeExists('seven')) {
\Drupal::configFactory()->getEditable('seven.settings')->set('third_party_settings.shortcut.module_link', TRUE)->save(TRUE);
}
}
/**
* Implements hook_uninstall().
*/
function shortcut_uninstall() {
// Theme settings are not configuration entities and cannot depend on modules
// so to unset a module-specific setting, we need to unset it with logic.
if (\Drupal::service('theme_handler')->themeExists('seven')) {
\Drupal::configFactory()->getEditable('seven.settings')->clear('third_party_settings.shortcut.module_link')->save(TRUE);
}
}

View file

@ -0,0 +1,6 @@
drupal.shortcut:
version: VERSION
css:
theme:
css/shortcut.theme.css: {}
css/shortcut.icons.theme.css: {}

View file

@ -0,0 +1,10 @@
shortcut_set_add_local_action:
route_name: shortcut.set_add
title: 'Add shortcut set'
appears_on:
- entity.shortcut_set.collection
shortcut.link_add:
route_name: shortcut.link_add
title: 'Add shortcut'
appears_on:
- entity.shortcut_set.customize_form

View file

@ -0,0 +1,5 @@
entity.shortcut_set.collection:
title: Shortcuts
description: 'Add and modify shortcut sets.'
route_name: entity.shortcut_set.collection
parent: system.admin_config_ui

View file

@ -0,0 +1,19 @@
shortcut.set_switch:
route_name: shortcut.set_switch
base_route: entity.user.canonical
title: 'Shortcuts'
entity.shortcut_set.customize_form:
title: 'List links'
route_name: entity.shortcut_set.customize_form
base_route: entity.shortcut_set.customize_form
entity.shortcut_set.edit_form:
title: 'Edit set name'
route_name: entity.shortcut_set.edit_form
base_route: entity.shortcut_set.customize_form
weight: 10
entity.shortcut.canonical:
route_name: entity.shortcut.canonical
base_route: entity.shortcut.canonical
title: Edit

View file

@ -0,0 +1,437 @@
<?php
/**
* @file
* Allows users to manage customizable lists of shortcut links.
*/
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\shortcut\Entity\ShortcutSet;
use Drupal\shortcut\ShortcutSetInterface;
/**
* Implements hook_help().
*/
function shortcut_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.shortcut':
$output = '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Shortcut module allows users to create sets of <em>shortcut</em> links to commonly-visited pages of the site. Shortcuts are contained within <em>sets</em>. Each user with <em>Select any shortcut set</em> permission can select a shortcut set created by anyone at the site. For more information, see the <a href="!shortcut">online documentation for the Shortcut module</a>.', array('!shortcut' => 'https://www.drupal.org/documentation/modules/shortcut')) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl><dt>' . t('Administering shortcuts') . '</dt>';
$output .= '<dd>' . t('Users with the <em>Administer shortcuts</em> permission can manage shortcut sets and edit the shortcuts within sets from the <a href="!shortcuts">Shortcuts administration page</a>.', array('!shortcuts' => \Drupal::url('entity.shortcut_set.collection'))) . '</dd>';
$output .= '<dt>' . t('Choosing shortcut sets') . '</dt>';
$output .= '<dd>' . t('Users with permission to switch shortcut sets can choose a shortcut set to use from the Shortcuts tab of their user account page.') . '</dd>';
$output .= '<dt>' . t('Adding and removing shortcuts') . '</dt>';
$output .= '<dd>' . t('The Shortcut module creates an add/remove link for each page on your site; the link lets you add or remove the current page from the currently-enabled set of shortcuts (if your theme displays it and you have permission to edit your shortcut set). The core Seven administration theme displays this link next to the page title, as a grey or yellow star. If you click on the grey star, you will add that page to your preferred set of shortcuts. If the page is already part of your shortcut set, the link will be a yellow star, and will allow you to remove the current page from your shortcut set.') . '</dd>';
$output .= '<dt>' . t('Displaying shortcuts') . '</dt>';
$output .= '<dd>' . t('You can display your shortcuts by enabling the <em>Shortcuts</em> block on the <a href="!blocks">Blocks administration page</a>. Certain administrative modules also display your shortcuts; for example, the core <a href="!toolbar-help">Toolbar module</a> provides a corresponding menu item.', array('!blocks' => (\Drupal::moduleHandler()->moduleExists('block')) ? \Drupal::url('block.admin_display') : '#', '!toolbar-help' => (\Drupal::moduleHandler()->moduleExists('toolbar')) ? \Drupal::url('help.page', array('name' => 'toolbar')) : '#')) . '</dd>';
$output .= '</dl>';
return $output;
case 'entity.shortcut_set.collection':
case 'shortcut.set_add':
case 'entity.shortcut_set.edit_form':
$user = \Drupal::currentUser();
if ($user->hasPermission('access shortcuts') && $user->hasPermission('switch shortcut sets')) {
$output = '<p>' . t('Define which shortcut set you are using on the <a href="@shortcut-link">Shortcuts tab</a> of your account page.', array('@shortcut-link' => \Drupal::url('shortcut.set_switch', array('user' => $user->id())))) . '</p>';
return $output;
}
}
}
/**
* Access callback for editing a shortcut set.
*
* @param Drupal\shortcut\ShortcutSetInterface $shortcut_set
* (optional) The shortcut set to be edited. If not set, the current user's
* shortcut set will be used.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
function shortcut_set_edit_access(ShortcutSetInterface $shortcut_set = NULL) {
$account = \Drupal::currentUser();
// Shortcut administrators can edit any set.
if ($account->hasPermission('administer shortcuts')) {
return AccessResult::allowed()->cachePerPermissions();
}
// Sufficiently-privileged users can edit their currently displayed shortcut
// set, but not other sets. They must also be able to access shortcuts.
$may_edit_current_shortcut_set = $account->hasPermission('customize shortcut links') && (!isset($shortcut_set) || $shortcut_set == shortcut_current_displayed_set()) && $account->hasPermission('access shortcuts');
return AccessResult::allowedIf($may_edit_current_shortcut_set)->cachePerPermissions();
}
/**
* Access callback for switching the shortcut set assigned to a user account.
*
* @param object $account
* (optional) The user account whose shortcuts will be switched. If not set,
* permissions will be checked for switching the logged-in user's own
* shortcut set.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
function shortcut_set_switch_access($account = NULL) {
$user = \Drupal::currentUser();
if ($user->hasPermission('administer shortcuts')) {
// Administrators can switch anyone's shortcut set.
return AccessResult::allowed()->cachePerPermissions();
}
if (!$user->hasPermission('access shortcuts')) {
// The user has no permission to use shortcuts.
return AccessResult::neutral()->cachePerPermissions();
}
if (!$user->hasPermission('switch shortcut sets')) {
// The user has no permission to switch anyone's shortcut set.
return AccessResult::neutral()->cachePerPermissions();
}
// Users with the 'switch shortcut sets' permission can switch their own
// shortcuts sets.
if (!isset($account)) {
return AccessResult::allowed()->cachePerPermissions();
}
else if ($user->id() == $account->id()) {
return AccessResult::allowed()->cachePerPermissions()->cachePerUser();
}
// No opinion.
return AccessResult::neutral()->cachePerPermissions();
}
/**
* Assigns a user to a particular shortcut set.
*
* @param $shortcut_set Drupal\shortcut\Entity\Shortcut
* An object representing the shortcut set.
* @param $account
* A user account that will be assigned to use the set.
*
* @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
* Use \Drupal::entityManager()->getStorage('shortcut_set')->assignUser().
*/
function shortcut_set_assign_user($shortcut_set, $account) {
\Drupal::entityManager()
->getStorage('shortcut_set')
->assignUser($shortcut_set, $account);
}
/**
* Unassigns a user from any shortcut set they may have been assigned to.
*
* The user will go back to using whatever default set applies.
*
* @param $account
* A user account that will be removed from the shortcut set assignment.
*
* @return
* TRUE if the user was previously assigned to a shortcut set and has been
* successfully removed from it. FALSE if the user was already not assigned
* to any set.
*
* @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
* Use \Drupal::entityManager()->getStorage('shortcut_set')->unassignUser().
*/
function shortcut_set_unassign_user($account) {
return (bool) \Drupal::entityManager()
->getStorage('shortcut_set')
->unassignUser($account);
}
/**
* Returns the current displayed shortcut set for the provided user account.
*
* @param $account
* (optional) The user account whose shortcuts will be returned. Defaults to
* the currently logged-in user.
*
* @return
* An object representing the shortcut set that should be displayed to the
* current user. If the user does not have an explicit shortcut set defined,
* the default set is returned.
*/
function shortcut_current_displayed_set($account = NULL) {
$shortcut_sets = &drupal_static(__FUNCTION__, array());
$user = \Drupal::currentUser();
if (!isset($account)) {
$account = $user;
}
// Try to return a shortcut set from the static cache.
if (isset($shortcut_sets[$account->id()])) {
return $shortcut_sets[$account->id()];
}
// If none was found, try to find a shortcut set that is explicitly assigned
// to this user.
$shortcut_set_name = \Drupal::entityManager()
->getStorage('shortcut_set')
->getAssignedToUser($account);
if ($shortcut_set_name) {
$shortcut_set = ShortcutSet::load($shortcut_set_name);
}
// Otherwise, use the default set.
else {
$shortcut_set = shortcut_default_set($account);
}
$shortcut_sets[$account->id()] = $shortcut_set;
return $shortcut_set;
}
/**
* Returns the default shortcut set for a given user account.
*
* @param object $account
* (optional) The user account whose default shortcut set will be returned.
* If not provided, the function will return the currently logged-in user's
* default shortcut set.
*
* @return
* An object representing the default shortcut set.
*/
function shortcut_default_set($account = NULL) {
$user = \Drupal::currentUser();
if (!isset($account)) {
$account = $user;
}
// Allow modules to return a default shortcut set name. Since we can only
// have one, we allow the last module which returns a valid result to take
// precedence. If no module returns a valid set, fall back on the site-wide
// default, which is the lowest-numbered shortcut set.
$suggestions = array_reverse(\Drupal::moduleHandler()->invokeAll('shortcut_default_set', array($account)));
$suggestions[] = 'default';
foreach ($suggestions as $name) {
if ($shortcut_set = ShortcutSet::load($name)) {
break;
}
}
return $shortcut_set;
}
/**
* Check to see if a shortcut set with the given title already exists.
*
* @param $title
* Human-readable name of the shortcut set to check.
*
* @return
* TRUE if a shortcut set with that title exists; FALSE otherwise.
*
* @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
*/
function shortcut_set_title_exists($title) {
$sets = ShortcutSet::loadMultiple();
foreach ($sets as $set) {
if ($set->label() == $title) {
return TRUE;
}
}
return FALSE;
}
/**
* Returns an array of shortcut links, suitable for rendering.
*
* @param \Drupal\shortcut\ShortcutSetInterface $shortcut_set
* (optional) An object representing the set whose links will be displayed.
* If not provided, the user's current set will be displayed.
*
* @return \Drupal\shortcut\ShortcutInterface[]
* An array of shortcut links, in the format returned by the menu system.
*/
function shortcut_renderable_links($shortcut_set = NULL) {
$shortcut_links = array();
if (!isset($shortcut_set)) {
$shortcut_set = shortcut_current_displayed_set();
}
$cache_tags = array();
foreach ($shortcut_set->getShortcuts() as $shortcut) {
$shortcut = \Drupal::entityManager()->getTranslationFromContext($shortcut);
$url = $shortcut->getUrl();
if ($url->access()) {
$links[$shortcut->id()] = array(
'type' => 'link',
'title' => $shortcut->label(),
'url' => $shortcut->getUrl(),
);
$cache_tags = Cache::mergeTags($cache_tags, $shortcut->getCacheTags());
}
}
if (!empty($links)) {
$shortcut_links = array(
'#theme' => 'links__toolbar_shortcuts',
'#links' => $links,
'#attributes' => array(
'class' => array('toolbar-menu'),
),
'#cache' => array(
'tags' => $cache_tags,
),
);
}
return $shortcut_links;
}
/**
* Implements hook_preprocess_HOOK() for block templates.
*/
function shortcut_preprocess_block(&$variables) {
if ($variables['configuration']['provider'] == 'shortcut') {
$variables['attributes']['role'] = 'navigation';
}
}
/**
* Implements hook_preprocess_HOOK() for page templates.
*/
function shortcut_preprocess_page(&$variables) {
// Only display the shortcut link if the user has the ability to edit
// shortcuts and if the page's actual content is being shown (for example,
// we do not want to display it on "access denied" or "page not found"
// pages).
if (shortcut_set_edit_access()->isAllowed() && !\Drupal::request()->attributes->has('exception')) {
$link = Url::fromRouteMatch(\Drupal::routeMatch())->getInternalPath();
$route_match = \Drupal::routeMatch();
$query = array(
'link' => $link,
'name' => $variables['title'],
);
$query += \Drupal::destination()->getAsArray();
$shortcut_set = shortcut_current_displayed_set();
// Check if $link is already a shortcut and set $link_mode accordingly.
$shortcuts = \Drupal::entityManager()->getStorage('shortcut')->loadByProperties(array('shortcut_set' => $shortcut_set->id()));
/** @var \Drupal\shortcut\ShortcutInterface $shortcut */
foreach ($shortcuts as $shortcut) {
if (($shortcut_url = $shortcut->getUrl()) && $shortcut_url->isRouted() && $shortcut_url->getRouteName() == $route_match->getRouteName() && $shortcut_url->getRouteParameters() == $route_match->getRawParameters()->all()) {
$shortcut_id = $shortcut->id();
break;
}
}
$link_mode = isset($shortcut_id) ? "remove" : "add";
if ($link_mode == "add") {
$link_text = shortcut_set_switch_access()->isAllowed() ? t('Add to %shortcut_set shortcuts', array('%shortcut_set' => $shortcut_set->label())) : t('Add to shortcuts');
$route_name = 'shortcut.link_add_inline';
$route_parameters = array('shortcut_set' => $shortcut_set->id());
}
else {
$query['id'] = $shortcut_id;
$link_text = shortcut_set_switch_access()->isAllowed() ? t('Remove from %shortcut_set shortcuts', array('%shortcut_set' => $shortcut_set->label())) : t('Remove from shortcuts');
$route_name = 'entity.shortcut.link_delete_inline';
$route_parameters = array('shortcut' => $shortcut_id);
}
if (theme_get_setting('third_party_settings.shortcut.module_link')) {
$variables['title_suffix']['add_or_remove_shortcut'] = array(
'#attached' => array(
'library' => array(
'shortcut/drupal.shortcut',
),
),
'#type' => 'link',
'#title' => SafeMarkup::format('<span class="shortcut-action__icon"></span><span class="shortcut-action__message">@text</span>', array('@text' => $link_text)),
'#url' => Url::fromRoute($route_name, $route_parameters),
'#options' => array('query' => $query),
'#attributes' => array(
'class' => array(
'shortcut-action',
'shortcut-action--' . $link_mode,
),
),
);
}
}
}
/**
* Implements hook_toolbar().
*/
function shortcut_toolbar() {
$user = \Drupal::currentUser();
$items = [];
$items['shortcuts'] = [
'#cache' => [
'contexts' => [
// Cacheable per user, because each user can have their own shortcut
// set, even if they cannot create or select a shortcut set, because
// an administrator may have assigned a non-default shortcut set.
'user',
],
],
];
if ($user->hasPermission('access shortcuts')) {
$links = shortcut_renderable_links();
$shortcut_set = shortcut_current_displayed_set();
\Drupal::service('renderer')->addCacheableDependency($items['shortcuts'], $shortcut_set);
$configure_link = NULL;
if (shortcut_set_edit_access($shortcut_set)->isAllowed()) {
$configure_link = array(
'#type' => 'link',
'#title' => t('Edit shortcuts'),
'#url' => Url::fromRoute('entity.shortcut_set.customize_form', ['shortcut_set' => $shortcut_set->id()]),
'#options' => array('attributes' => array('class' => array('edit-shortcuts'))),
);
}
if (!empty($links) || !empty($configure_link)) {
$items['shortcuts'] += array(
'#type' => 'toolbar_item',
'tab' => array(
'#type' => 'link',
'#title' => t('Shortcuts'),
'#url' => $shortcut_set->urlInfo('collection'),
'#attributes' => array(
'title' => t('Shortcuts'),
'class' => array('toolbar-icon', 'toolbar-icon-shortcut'),
),
),
'tray' => array(
'#heading' => t('User-defined shortcuts'),
'shortcuts' => $links,
'configure' => $configure_link,
),
'#weight' => -10,
'#attached' => array(
'library' => array(
'shortcut/drupal.shortcut',
),
),
);
}
}
return $items;
}
/**
* Implements hook_themes_installed().
*/
function shortcut_themes_installed($theme_list) {
if (in_array('seven', $theme_list)) {
// Theme settings are not configuration entities and cannot depend on modules
// so to set a module-specific setting, we need to set it with logic.
if (\Drupal::moduleHandler()->moduleExists('shortcut')) {
\Drupal::configFactory()->getEditable('seven.settings')->set('third_party_settings.shortcut.module_link', TRUE)->save(TRUE);
}
}
}

View file

@ -0,0 +1,10 @@
administer shortcuts:
title: 'Administer shortcuts'
customize shortcut links:
title: 'Edit current shortcut set'
description: 'Editing the current shortcut set will affect other users if that set has been assigned to or selected by other users. Granting "Select any shortcut set" permission along with this permission will grant permission to edit any shortcut set.'
switch shortcut sets:
title: 'Select any shortcut set'
description: 'From all shortcut sets, select one to be own active set. Without this permission, an administrator selects shortcut sets for users.'
access shortcuts:
title: 'Use shortcuts'

View file

@ -0,0 +1,97 @@
entity.shortcut_set.delete_form:
path: '/admin/config/user-interface/shortcut/manage/{shortcut_set}/delete'
defaults:
_entity_form: 'shortcut_set.delete'
_title: 'Delete shortcut set'
requirements:
_entity_access: 'shortcut_set.delete'
entity.shortcut_set.collection:
path: '/admin/config/user-interface/shortcut'
defaults:
_entity_list: 'shortcut_set'
_title: 'Shortcuts'
requirements:
_permission: 'administer shortcuts'
shortcut.set_add:
path: '/admin/config/user-interface/shortcut/add-set'
defaults:
_entity_form: 'shortcut_set.add'
_title: 'Add shortcut set'
requirements:
_entity_create_access: 'shortcut_set'
entity.shortcut_set.edit_form:
path: '/admin/config/user-interface/shortcut/manage/{shortcut_set}'
defaults:
_entity_form: 'shortcut_set.edit'
_title: 'Edit shortcut set'
requirements:
_entity_access: 'shortcut_set.update'
shortcut.link_add_inline:
path: '/admin/config/user-interface/shortcut/manage/{shortcut_set}/add-link-inline'
defaults:
_controller: 'Drupal\shortcut\Controller\ShortcutSetController::addShortcutLinkInline'
requirements:
_entity_access: 'shortcut_set.update'
_csrf_token: 'TRUE'
entity.shortcut_set.customize_form:
path: '/admin/config/user-interface/shortcut/manage/{shortcut_set}/customize'
defaults:
_entity_form: 'shortcut_set.customize'
_title: 'List links'
requirements:
_entity_access: 'shortcut_set.update'
shortcut.link_add:
path: '/admin/config/user-interface/shortcut/manage/{shortcut_set}/add-link'
defaults:
_controller: '\Drupal\shortcut\Controller\ShortcutController::addForm'
_title: 'Add link'
requirements:
_entity_create_access: 'shortcut:{shortcut_set}'
entity.shortcut.canonical:
path: '/admin/config/user-interface/shortcut/link/{shortcut}'
defaults:
_entity_form: 'shortcut.default'
_title: 'Edit'
requirements:
_entity_access: 'shortcut.update'
entity.shortcut.edit_form:
path: '/admin/config/user-interface/shortcut/link/{shortcut}'
defaults:
_entity_form: 'shortcut.default'
_title: 'Edit'
requirements:
_entity_access: 'shortcut.update'
entity.shortcut.link_delete_inline:
path: '/admin/config/user-interface/shortcut/link/{shortcut}/delete-inline'
defaults:
_controller: 'Drupal\shortcut\Controller\ShortcutController::deleteShortcutLinkInline'
requirements:
_entity_access: 'shortcut.delete'
_csrf_token: 'TRUE'
entity.shortcut.delete_form:
path: '/admin/config/user-interface/shortcut/link/{shortcut}/delete'
defaults:
_entity_form: 'shortcut.delete'
_title: 'Delete'
requirements:
_entity_access: 'shortcut.delete'
shortcut.set_switch:
path: '/user/{user}/shortcuts'
defaults:
_form: 'Drupal\shortcut\Form\SwitchShortcutSet'
_title: 'Shortcuts'
requirements:
_custom_access: 'Drupal\shortcut\Form\SwitchShortcutSet::checkAccess'
options:
_admin_route: TRUE

View file

@ -0,0 +1,59 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Controller\ShortcutController.
*/
namespace Drupal\shortcut\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\shortcut\ShortcutSetInterface;
use Drupal\shortcut\ShortcutInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides route responses for taxonomy.module.
*/
class ShortcutController extends ControllerBase {
/**
* Returns a rendered edit form to create a new shortcut associated to the
* given shortcut set.
*
* @param \Drupal\shortcut\ShortcutSetInterface $shortcut_set
* The shortcut set this shortcut will be added to.
*
* @return array
* The shortcut add form.
*/
public function addForm(ShortcutSetInterface $shortcut_set) {
$shortcut = $this->entityManager()->getStorage('shortcut')->create(array('shortcut_set' => $shortcut_set->id()));
return $this->entityFormBuilder()->getForm($shortcut, 'add');
}
/**
* Deletes the selected shortcut.
*
* @param \Drupal\shortcut\ShortcutInterface $shortcut
* The shortcut to delete.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* A redirect to the previous location or the front page when destination
* is not set.
*/
public function deleteShortcutLinkInline(ShortcutInterface $shortcut) {
$label = $shortcut->label();
try {
$shortcut->delete();
drupal_set_message($this->t('The shortcut %title has been deleted.', array('%title' => $label)));
}
catch (\Exception $e) {
drupal_set_message($this->t('Unable to delete the shortcut for %title.', array('%title' => $label)), 'error');
}
return $this->redirect('<front>');
}
}

View file

@ -0,0 +1,86 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Controller\ShortcutSetController.
*/
namespace Drupal\shortcut\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Path\PathValidatorInterface;
use Drupal\shortcut\ShortcutSetInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Builds the page for administering shortcut sets.
*/
class ShortcutSetController extends ControllerBase {
/**
* The path validator.
*
* @var \Drupal\Core\Path\PathValidatorInterface
*/
protected $pathValidator;
/**
* Creates a new ShortcutSetController instance.
*
* @param \Drupal\Core\Path\PathValidatorInterface $path_validator
* The path validator.
*/
public function __construct(PathValidatorInterface $path_validator) {
$this->pathValidator = $path_validator;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container->get('path.validator'));
}
/**
* Creates a new link in the provided shortcut set.
*
* @param \Drupal\shortcut\ShortcutSetInterface $shortcut_set
* The shortcut set to add a link to.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* A redirect response to the front page, or the previous location.
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
public function addShortcutLinkInline(ShortcutSetInterface $shortcut_set, Request $request) {
$link = $request->query->get('link');
$name = $request->query->get('name');
if (parse_url($link, PHP_URL_SCHEME) === NULL && $this->pathValidator->isValid($link)) {
$shortcut = $this->entityManager()->getStorage('shortcut')->create(array(
'title' => $name,
'shortcut_set' => $shortcut_set->id(),
'link' => array(
'uri' => 'internal:/' . $link,
),
));
try {
$shortcut->save();
drupal_set_message($this->t('Added a shortcut for %title.', array('%title' => $shortcut->label())));
}
catch (\Exception $e) {
drupal_set_message($this->t('Unable to add a shortcut for %title.', array('%title' => $shortcut->label())), 'error');
}
return $this->redirect('<front>');
}
throw new AccessDeniedHttpException();
}
}

View file

@ -0,0 +1,207 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Entity\Shortcut.
*/
namespace Drupal\shortcut\Entity;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\link\LinkItemInterface;
use Drupal\shortcut\ShortcutInterface;
/**
* Defines the shortcut entity class.
*
* @property \Drupal\link\LinkItemInterface link
*
* @ContentEntityType(
* id = "shortcut",
* label = @Translation("Shortcut link"),
* handlers = {
* "access" = "Drupal\shortcut\ShortcutAccessControlHandler",
* "form" = {
* "default" = "Drupal\shortcut\ShortcutForm",
* "add" = "Drupal\shortcut\ShortcutForm",
* "edit" = "Drupal\shortcut\ShortcutForm",
* "delete" = "Drupal\shortcut\Form\ShortcutDeleteForm"
* },
* "translation" = "Drupal\content_translation\ContentTranslationHandler"
* },
* base_table = "shortcut",
* data_table = "shortcut_field_data",
* translatable = TRUE,
* entity_keys = {
* "id" = "id",
* "uuid" = "uuid",
* "bundle" = "shortcut_set",
* "label" = "title",
* "langcode" = "langcode",
* },
* links = {
* "canonical" = "/admin/config/user-interface/shortcut/link/{shortcut}",
* "delete-form" = "/admin/config/user-interface/shortcut/link/{shortcut}/delete",
* "edit-form" = "/admin/config/user-interface/shortcut/link/{shortcut}",
* },
* list_cache_tags = { "config:shortcut_set_list" },
* bundle_entity_type = "shortcut_set"
* )
*/
class Shortcut extends ContentEntityBase implements ShortcutInterface {
/**
* {@inheritdoc}
*/
public function getTitle() {
return $this->get('title')->value;
}
/**
* {@inheritdoc}
*/
public function setTitle($link_title) {
$this->set('title', $link_title);
return $this;
}
/**
* {@inheritdoc}
*/
public function getWeight() {
return $this->get('weight')->value;
}
/**
* {@inheritdoc}
*/
public function setWeight($weight) {
$this->set('weight', $weight);
return $this;
}
/**
* {@inheritdoc}
*/
public function getUrl() {
return $this->link->first()->getUrl();
}
/**
* {@inheritdoc}
*/
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
parent::postSave($storage, $update);
// Entity::postSave() calls Entity::invalidateTagsOnSave(), which only
// handles the regular cases. The Shortcut entity has one special case: a
// newly created shortcut is *also* added to a shortcut set, so we must
// invalidate the associated shortcut set's cache tag.
if (!$update) {
Cache::invalidateTags($this->getCacheTags());
}
}
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['id'] = BaseFieldDefinition::create('integer')
->setLabel(t('ID'))
->setDescription(t('The ID of the shortcut.'))
->setReadOnly(TRUE)
->setSetting('unsigned', TRUE);
$fields['uuid'] = BaseFieldDefinition::create('uuid')
->setLabel(t('UUID'))
->setDescription(t('The UUID of the shortcut.'))
->setReadOnly(TRUE);
$fields['shortcut_set'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Shortcut set'))
->setDescription(t('The bundle of the shortcut.'))
->setSetting('target_type', 'shortcut_set')
->setRequired(TRUE);
$fields['title'] = BaseFieldDefinition::create('string')
->setLabel(t('Name'))
->setDescription(t('The name of the shortcut.'))
->setRequired(TRUE)
->setTranslatable(TRUE)
->setDefaultValue('')
->setSetting('max_length', 255)
->setDisplayOptions('form', array(
'type' => 'string_textfield',
'weight' => -10,
'settings' => array(
'size' => 40,
),
));
$fields['weight'] = BaseFieldDefinition::create('integer')
->setLabel(t('Weight'))
->setDescription(t('Weight among shortcuts in the same shortcut set.'));
$fields['link'] = BaseFieldDefinition::create('link')
->setLabel(t('Path'))
->setDescription(t('The location this shortcut points to.'))
->setRequired(TRUE)
->setSettings(array(
'link_type' => LinkItemInterface::LINK_INTERNAL,
'title' => DRUPAL_DISABLED,
))
->setDisplayOptions('form', array(
'type' => 'link_default',
'weight' => 0,
))
->setDisplayConfigurable('form', TRUE);
$fields['langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Language'))
->setDescription(t('The language code of the shortcut.'))
->setTranslatable(TRUE)
->setDisplayOptions('view', array(
'type' => 'hidden',
))
->setDisplayOptions('form', array(
'type' => 'language_select',
'weight' => 2,
));
return $fields;
}
/**
* {@inheritdoc}
*/
public function getCacheTags() {
return $this->shortcut_set->entity->getCacheTags();
}
/**
* Sort shortcut objects.
*
* Callback for uasort().
*
* @param \Drupal\shortcut\ShortcutInterface $a
* First item for comparison.
* @param \Drupal\shortcut\ShortcutInterface $b
* Second item for comparison.
*
* @return int
* The comparison result for uasort().
*/
public static function sort(ShortcutInterface $a, ShortcutInterface $b) {
$a_weight = $a->getWeight();
$b_weight = $b->getWeight();
if ($a_weight == $b_weight) {
return strnatcasecmp($a->getTitle(), $b->getTitle());
}
return ($a_weight < $b_weight) ? -1 : 1;
}
}

View file

@ -0,0 +1,129 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Entity\ShortcutSet.
*/
namespace Drupal\shortcut\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\shortcut\ShortcutSetInterface;
/**
* Defines the Shortcut set configuration entity.
*
* @ConfigEntityType(
* id = "shortcut_set",
* label = @Translation("Shortcut set"),
* handlers = {
* "storage" = "Drupal\shortcut\ShortcutSetStorage",
* "access" = "Drupal\shortcut\ShortcutSetAccessControlHandler",
* "list_builder" = "Drupal\shortcut\ShortcutSetListBuilder",
* "form" = {
* "default" = "Drupal\shortcut\ShortcutSetForm",
* "add" = "Drupal\shortcut\ShortcutSetForm",
* "edit" = "Drupal\shortcut\ShortcutSetForm",
* "customize" = "Drupal\shortcut\Form\SetCustomize",
* "delete" = "Drupal\shortcut\Form\ShortcutSetDeleteForm"
* }
* },
* config_prefix = "set",
* bundle_of = "shortcut",
* entity_keys = {
* "id" = "id",
* "label" = "label"
* },
* links = {
* "customize-form" = "/admin/config/user-interface/shortcut/manage/{shortcut_set}/customize",
* "delete-form" = "/admin/config/user-interface/shortcut/manage/{shortcut_set}/delete",
* "edit-form" = "/admin/config/user-interface/shortcut/manage/{shortcut_set}",
* "collection" = "/admin/config/user-interface/shortcut",
* },
* config_export = {
* "id",
* "label",
* }
* )
*/
class ShortcutSet extends ConfigEntityBase implements ShortcutSetInterface {
/**
* The machine name for the configuration entity.
*
* @var string
*/
protected $id;
/**
* The human-readable name of the configuration entity.
*
* @var string
*/
protected $label;
/**
* {@inheritdoc}
*/
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
parent::postSave($storage, $update);
if (!$update && !$this->isSyncing()) {
// Save a new shortcut set with links copied from the user's default set.
$default_set = shortcut_default_set();
// This is the default set, do not copy shortcuts.
if ($default_set->id() != $this->id()) {
foreach ($default_set->getShortcuts() as $shortcut) {
$shortcut = $shortcut->createDuplicate();
$shortcut->enforceIsNew();
$shortcut->shortcut_set->target_id = $this->id();
$shortcut->save();
}
}
}
}
/**
* {@inheritdoc}
*/
public static function preDelete(EntityStorageInterface $storage, array $entities) {
parent::preDelete($storage, $entities);
foreach ($entities as $entity) {
$storage->deleteAssignedShortcutSets($entity);
// Next, delete the shortcuts for this set.
$shortcut_ids = \Drupal::entityQuery('shortcut')
->condition('shortcut_set', $entity->id(), '=')
->execute();
$controller = \Drupal::entityManager()->getStorage('shortcut');
$entities = $controller->loadMultiple($shortcut_ids);
$controller->delete($entities);
}
}
/**
* {@inheritdoc}
*/
public function resetLinkWeights() {
$weight = -50;
foreach ($this->getShortcuts() as $shortcut) {
$shortcut->setWeight(++$weight);
$shortcut->save();
}
return $this;
}
/**
* {@inheritdoc}
*/
public function getShortcuts() {
$shortcuts = \Drupal::entityManager()->getStorage('shortcut')->loadByProperties(array('shortcut_set' => $this->id()));
uasort($shortcuts, array('\Drupal\shortcut\Entity\Shortcut', 'sort'));
return $shortcuts;
}
}

View file

@ -0,0 +1,117 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Form\SetCustomize.
*/
namespace Drupal\shortcut\Form;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Builds the shortcut set customize form.
*/
class SetCustomize extends EntityForm {
/**
* The entity being used by this form.
*
* @var \Drupal\shortcut\ShortcutSetInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);
$form['shortcuts'] = array(
'#tree' => TRUE,
'#weight' => -20,
);
$form['shortcuts']['links'] = array(
'#type' => 'table',
'#header' => array(t('Name'), t('Weight'), t('Operations')),
'#empty' => $this->t('No shortcuts available. <a href="@link">Add a shortcut</a>', array('@link' => $this->url('shortcut.link_add', array('shortcut_set' => $this->entity->id())))),
'#attributes' => array('id' => 'shortcuts'),
'#tabledrag' => array(
array(
'action' => 'order',
'relationship' => 'sibling',
'group' => 'shortcut-weight',
),
),
);
foreach ($this->entity->getShortcuts() as $shortcut) {
$id = $shortcut->id();
$url = $shortcut->getUrl();
if (!$url->access()) {
continue;
}
$form['shortcuts']['links'][$id]['#attributes']['class'][] = 'draggable';
$form['shortcuts']['links'][$id]['name'] = array(
'#type' => 'link',
'#title' => $shortcut->getTitle(),
) + $url->toRenderArray();
unset($form['shortcuts']['links'][$id]['name']['#access_callback']);
$form['shortcuts']['links'][$id]['#weight'] = $shortcut->getWeight();
$form['shortcuts']['links'][$id]['weight'] = array(
'#type' => 'weight',
'#title' => t('Weight for @title', array('@title' => $shortcut->getTitle())),
'#title_display' => 'invisible',
'#default_value' => $shortcut->getWeight(),
'#attributes' => array('class' => array('shortcut-weight')),
);
$links['edit'] = array(
'title' => t('Edit'),
'url' => $shortcut->urlInfo(),
);
$links['delete'] = array(
'title' => t('Delete'),
'url' => $shortcut->urlInfo('delete-form'),
);
$form['shortcuts']['links'][$id]['operations'] = array(
'#type' => 'operations',
'#links' => $links,
'#access' => $url->access(),
);
}
return $form;
}
/**
* {@inheritdoc}
*/
protected function actions(array $form, FormStateInterface $form_state) {
// Only includes a Save action for the entity, no direct Delete button.
return array(
'submit' => array(
'#type' => 'submit',
'#value' => t('Save changes'),
'#access' => (bool) Element::getVisibleChildren($form['shortcuts']['links']),
'#submit' => array('::submitForm', '::save'),
),
);
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
foreach ($this->entity->getShortcuts() as $shortcut) {
$weight = $form_state->getValue(array('shortcuts', 'links', $shortcut->id(), 'weight'));
$shortcut->setWeight($weight);
$shortcut->save();
}
drupal_set_message(t('The shortcut set has been updated.'));
}
}

View file

@ -0,0 +1,41 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Form\ShortcutDeleteForm.
*/
namespace Drupal\shortcut\Form;
use Drupal\Core\Entity\ContentEntityDeleteForm;
use Drupal\Core\Url;
/**
* Builds the shortcut link deletion form.
*/
class ShortcutDeleteForm extends ContentEntityDeleteForm {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'shortcut_confirm_delete';
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('entity.shortcut_set.customize_form', array(
'shortcut_set' => $this->entity->bundle(),
));
}
/**
* {@inheritdoc}
*/
protected function getRedirectUrl() {
return $this->getCancelUrl();
}
}

View file

@ -0,0 +1,80 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Form\ShortcutSetDeleteForm.
*/
namespace Drupal\shortcut\Form;
use Drupal\Core\Entity\EntityDeleteForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\shortcut\ShortcutSetStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Database\Connection;
/**
* Builds the shortcut set deletion form.
*/
class ShortcutSetDeleteForm extends EntityDeleteForm {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* The shortcut storage.
*
* @var \Drupal\shortcut\ShortcutSetStorageInterface
*/
protected $storage;
/**
* Constructs a ShortcutSetDeleteForm object.
*/
public function __construct(Connection $database, ShortcutSetStorageInterface $storage) {
$this->database = $database;
$this->storage = $storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('database'),
$container->get('entity.manager')->getStorage('shortcut_set')
);
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Find out how many users are directly assigned to this shortcut set, and
// make a message.
$number = $this->storage->countAssignedUsers($this->entity);
$info = '';
if ($number) {
$info .= '<p>' . $this->formatPlural($number,
'1 user has chosen or been assigned to this shortcut set.',
'@count users have chosen or been assigned to this shortcut set.') . '</p>';
}
// Also, if a module implements hook_shortcut_default_set(), it's possible
// that this set is being used as a default set. Add a message about that too.
if ($this->moduleHandler->getImplementations('shortcut_default_set')) {
$info .= '<p>' . t('If you have chosen this shortcut set as the default for some or all users, they may also be affected by deleting it.') . '</p>';
}
$form['info'] = array(
'#markup' => $info,
);
return parent::buildForm($form, $form_state);
}
}

View file

@ -0,0 +1,234 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Form\SwitchShortcutSet.
*/
namespace Drupal\shortcut\Form;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\shortcut\Entity\ShortcutSet;
use Drupal\shortcut\ShortcutSetStorageInterface;
use Drupal\user\UserInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Builds the shortcut set switch form.
*/
class SwitchShortcutSet extends FormBase {
/**
* The account the shortcut set is for.
*
* @var \Drupal\user\UserInterface
*/
protected $user;
/**
* The shortcut set storage.
*
* @var \Drupal\shortcut\ShortcutSetStorageInterface
*/
protected $shortcutSetStorage;
/**
* Constructs a SwitchShortcutSet object.
*
* @param \Drupal\shortcut\ShortcutSetStorageInterface $shortcut_set_storage
* The shortcut set storage.
*/
public function __construct(ShortcutSetStorageInterface $shortcut_set_storage) {
$this->shortcutSetStorage = $shortcut_set_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager')->getStorage('shortcut_set')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'shortcut_set_switch';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, UserInterface $user = NULL) {
$account = $this->currentUser();
$this->user = $user;
// Prepare the list of shortcut sets.
$options = array_map(function (ShortcutSet $set) {
return SafeMarkup::checkPlain($set->label());
}, $this->shortcutSetStorage->loadMultiple());
$current_set = shortcut_current_displayed_set($this->user);
// Only administrators can add shortcut sets.
$add_access = $account->hasPermission('administer shortcuts');
if ($add_access) {
$options['new'] = $this->t('New set');
}
$account_is_user = $this->user->id() == $account->id();
if (count($options) > 1) {
$form['set'] = array(
'#type' => 'radios',
'#title' => $account_is_user ? $this->t('Choose a set of shortcuts to use') : $this->t('Choose a set of shortcuts for this user'),
'#options' => $options,
'#default_value' => $current_set->id(),
);
$form['label'] = array(
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#description' => $this->t('The new set is created by copying items from your default shortcut set.'),
'#access' => $add_access,
'#states' => array(
'visible' => array(
':input[name="set"]' => array('value' => 'new'),
),
'required' => array(
':input[name="set"]' => array('value' => 'new'),
),
),
);
$form['id'] = array(
'#type' => 'machine_name',
'#machine_name' => array(
'exists' => array($this, 'exists'),
'replace_pattern' => '[^a-z0-9-]+',
'replace' => '-',
),
// This ID could be used for menu name.
'#maxlength' => 23,
'#states' => array(
'required' => array(
':input[name="set"]' => array('value' => 'new'),
),
),
'#required' => FALSE,
);
if (!$account_is_user) {
$default_set = $this->shortcutSetStorage->getDefaultSet($this->user);
$form['new']['#description'] = $this->t('The new set is created by copying items from the %default set.', array('%default' => $default_set->label()));
}
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Change set'),
);
}
else {
// There is only 1 option, so output a message in the $form array.
$form['info'] = array(
'#markup' => '<p>' . $this->t('You are currently using the %set-name shortcut set.', array('%set-name' => $current_set->label())) . '</p>',
);
}
return $form;
}
/**
* Determines if a shortcut set exists already.
*
* @param string $id
* The set ID to check.
*
* @return bool
* TRUE if the shortcut set exists, FALSE otherwise.
*/
public function exists($id) {
return (bool) $this->shortcutSetStorage->getQuery()
->condition('id', $id)
->execute();
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
if ($form_state->getValue('set') == 'new') {
// Check to prevent creating a shortcut set with an empty title.
if (trim($form_state->getValue('label')) == '') {
$form_state->setErrorByName('label', $this->t('The new set label is required.'));
}
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$account = $this->currentUser();
$account_is_user = $this->user->id() == $account->id();
if ($form_state->getValue('set') == 'new') {
// Save a new shortcut set with links copied from the user's default set.
/* @var \Drupal\shortcut\Entity\ShortcutSet $set */
$set = $this->shortcutSetStorage->create(array(
'id' => $form_state->getValue('id'),
'label' => $form_state->getValue('label'),
));
$set->save();
$replacements = array(
'%user' => $this->user->label(),
'%set_name' => $set->label(),
'@switch-url' => $this->url('<current>'),
);
if ($account_is_user) {
// Only administrators can create new shortcut sets, so we know they have
// access to switch back.
drupal_set_message($this->t('You are now using the new %set_name shortcut set. You can edit it from this page or <a href="@switch-url">switch back to a different one.</a>', $replacements));
}
else {
drupal_set_message($this->t('%user is now using a new shortcut set called %set_name. You can edit it from this page.', $replacements));
}
$form_state->setRedirect(
'entity.shortcut_set.customize_form',
array('shortcut_set' => $set->id())
);
}
else {
// Switch to a different shortcut set.
/* @var \Drupal\shortcut\Entity\ShortcutSet $set */
$set = $this->shortcutSetStorage->load($form_state->getValue('set'));
$replacements = array(
'%user' => $this->user->label(),
'%set_name' => $set->label(),
);
drupal_set_message($account_is_user ? $this->t('You are now using the %set_name shortcut set.', $replacements) : $this->t('%user is now using the %set_name shortcut set.', $replacements));
}
// Assign the shortcut set to the provided user account.
$this->shortcutSetStorage->assignUser($set, $this->user);
}
/**
* Checks access for the shortcut set switch form.
*
* @param \Drupal\user\UserInterface $user
* (optional) The owner of the shortcut set.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function checkAccess(UserInterface $user = NULL) {
return shortcut_set_switch_access($user);
}
}

View file

@ -0,0 +1,41 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Plugin\Block\ShortcutsBlock.
*/
namespace Drupal\shortcut\Plugin\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Session\AccountInterface;
/**
* Provides a 'Shortcut' block.
*
* @Block(
* id = "shortcuts",
* admin_label = @Translation("Shortcuts"),
* category = @Translation("Menus")
* )
*/
class ShortcutsBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function build() {
return array(
shortcut_renderable_links(shortcut_current_displayed_set()),
);
}
/**
* {@inheritdoc}
*/
protected function blockAccess(AccountInterface $account) {
return AccessResult::allowedIfHasPermission($account, 'access shortcuts');
}
}

View file

@ -0,0 +1,79 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\ShortcutAccessControlHandler.
*/
namespace Drupal\shortcut;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityHandlerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines the access control handler for the shortcut entity type.
*
* @see \Drupal\shortcut\Entity\Shortcut
*/
class ShortcutAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {
/**
* The shortcut_set storage.
*
* @var \Drupal\shortcut\ShortcutSetStorageInterface
*/
protected $shortcutSetStorage;
/**
* Constructs a ShortcutAccessControlHandler object.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition.
* @param \Drupal\shortcut\ShortcutSetStorageInterface $shortcut_set_storage
* The shortcut_set storage.
*/
public function __construct(EntityTypeInterface $entity_type, ShortcutSetStorageInterface $shortcut_set_storage) {
parent::__construct($entity_type);
$this->shortcutSetStorage = $shortcut_set_storage;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
return new static(
$entity_type,
$container->get('entity.manager')->getStorage('shortcut_set')
);
}
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
if ($shortcut_set = $this->shortcutSetStorage->load($entity->bundle())) {
return shortcut_set_edit_access($shortcut_set, $account);
}
// @todo Fix this bizarre code: how can a shortcut exist without a shortcut
// set? The above if-test is unnecessary. See https://www.drupal.org/node/2339903.
return AccessResult::neutral()->cacheUntilEntityChanges($entity);
}
/**
* {@inheritdoc}
*/
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
if ($shortcut_set = $this->shortcutSetStorage->load($entity_bundle)) {
return shortcut_set_edit_access($shortcut_set, $account);
}
// @todo Fix this bizarre code: how can a shortcut exist without a shortcut
// set? The above if-test is unnecessary. See https://www.drupal.org/node/2339903.
return AccessResult::neutral();
}
}

View file

@ -0,0 +1,46 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\ShortcutForm.
*/
namespace Drupal\shortcut;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Form\FormStateInterface;
/**
* Form controller for the shortcut entity forms.
*/
class ShortcutForm extends ContentEntityForm {
/**
* The entity being used by this form.
*
* @var \Drupal\shortcut\ShortcutInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$entity = $this->entity;
$status = $entity->save();
if ($status == SAVED_UPDATED) {
$message = $this->t('The shortcut %link has been updated.', array('%link' => $entity->getTitle()));
}
else {
$message = $this->t('Added a shortcut for %title.', array('%title' => $entity->getTitle()));
}
drupal_set_message($message);
$form_state->setRedirect(
'entity.shortcut_set.customize_form',
array('shortcut_set' => $entity->bundle())
);
}
}

View file

@ -0,0 +1,63 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\ShortcutInterface.
*/
namespace Drupal\shortcut;
use Drupal\Core\Entity\ContentEntityInterface;
/**
* Provides an interface defining a shortcut entity.
*/
interface ShortcutInterface extends ContentEntityInterface {
/**
* Returns the title of this shortcut.
*
* @return string
* The title of this shortcut.
*/
public function getTitle();
/**
* Sets the title of this shortcut.
*
* @param string $title
* The title of this shortcut.
*
* @return \Drupal\shortcut\ShortcutInterface
* The called shortcut entity.
*/
public function setTitle($title);
/**
* Returns the weight among shortcuts with the same depth.
*
* @return int
* The shortcut weight.
*/
public function getWeight();
/**
* Sets the weight among shortcuts with the same depth.
*
* @param int $weight
* The shortcut weight.
*
* @return \Drupal\shortcut\ShortcutInterface
* The called shortcut entity.
*/
public function setWeight($weight);
/**
* Returns the URL object pointing to the configured route.
*
* @return \Drupal\Core\Url
* The URL object.
*/
public function getUrl();
}

View file

@ -0,0 +1,52 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\ShortcutSetAccessControlHandler.
*/
namespace Drupal\shortcut;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Session\AccountInterface;
/**
* Defines the access control handler for the shortcut set entity type.
*
* @see \Drupal\shortcut\Entity\ShortcutSet
*/
class ShortcutSetAccessControlHandler extends EntityAccessControlHandler {
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
switch ($operation) {
case 'update':
if ($account->hasPermission('administer shortcuts')) {
return AccessResult::allowed()->cachePerPermissions();
}
if (!$account->hasPermission('access shortcuts')) {
return AccessResult::neutral()->cachePerPermissions();
}
return AccessResult::allowedIf($account->hasPermission('customize shortcut links') && $entity == shortcut_current_displayed_set($account))->cachePerPermissions()->cacheUntilEntityChanges($entity);
case 'delete':
return AccessResult::allowedIf($account->hasPermission('administer shortcuts') && $entity->id() != 'default')->cachePerPermissions();
default:
// No opinion.
return AccessResult::neutral();
}
}
/**
* {@inheritdoc}
*/
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
return AccessResult::allowedIfHasPermission($account, 'administer shortcuts')->orIf(AccessResult::allowedIfHasPermissions($account, ['access shortcuts', 'customize shortcut links'], 'AND'));
}
}

View file

@ -0,0 +1,68 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\ShortcutSetForm.
*/
namespace Drupal\shortcut;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Form\FormStateInterface;
/**
* Form controller for the shortcut set entity edit forms.
*/
class ShortcutSetForm extends EntityForm {
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);
$entity = $this->entity;
$form['label'] = array(
'#type' => 'textfield',
'#title' => t('Set name'),
'#description' => t('The new set is created by copying items from your default shortcut set.'),
'#required' => TRUE,
'#default_value' => $entity->label(),
);
$form['id'] = array(
'#type' => 'machine_name',
'#machine_name' => array(
'exists' => '\Drupal\shortcut\Entity\ShortcutSet::load',
'source' => array('label'),
'replace_pattern' => '[^a-z0-9-]+',
'replace' => '-',
),
'#default_value' => $entity->id(),
'#disabled' => !$entity->isNew(),
// This id could be used for menu name.
'#maxlength' => 23,
);
$form['actions']['submit']['#value'] = t('Create new set');
return $form;
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$entity = $this->entity;
$is_new = !$entity->getOriginalId();
$entity->save();
if ($is_new) {
drupal_set_message(t('The %set_name shortcut set has been created. You can edit it from this page.', array('%set_name' => $entity->label())));
}
else {
drupal_set_message(t('Updated set name to %set-name.', array('%set-name' => $entity->label())));
}
$form_state->setRedirectUrl($this->entity->urlInfo('customize-form'));
}
}

View file

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\ShortcutSetInterface.
*/
namespace Drupal\shortcut;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
/**
* Provides an interface defining a shortcut set entity.
*/
interface ShortcutSetInterface extends ConfigEntityInterface {
/**
* Resets the link weights in a shortcut set to match their current order.
*
* This function can be used, for example, when a new shortcut link is added
* to the set. If the link is added to the end of the array and this function
* is called, it will force that link to display at the end of the list.
*
* @return \Drupal\shortcut\ShortcutSetInterface
* The shortcut set.
*/
public function resetLinkWeights();
/**
* Returns all the shortcuts from a shortcut set sorted correctly.
*
* @return \Drupal\shortcut\ShortcutInterface[]
* An array of shortcut entities.
*/
public function getShortcuts();
}

View file

@ -0,0 +1,53 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\ShortcutSetListBuilder.
*/
namespace Drupal\shortcut;
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Entity\EntityInterface;
/**
* Defines a class to build a listing of shortcut set entities.
*
* @see \Drupal\shortcut\Entity\ShortcutSet
*/
class ShortcutSetListBuilder extends ConfigEntityListBuilder {
/**
* {@inheritdoc}
*/
public function buildHeader() {
$header['name'] = t('Name');
return $header + parent::buildHeader();
}
/**
* {@inheritdoc}
*/
public function getDefaultOperations(EntityInterface $entity) {
$operations = parent::getDefaultOperations($entity);
if (isset($operations['edit'])) {
$operations['edit']['title'] = t('Edit shortcut set');
}
$operations['list'] = array(
'title' => t('List links'),
'url' => $entity->urlInfo('customize-form'),
);
return $operations;
}
/**
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
$row['name'] = $this->getLabel($entity);
return $row + parent::buildRow($entity);
}
}

View file

@ -0,0 +1,135 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\ShortcutSetStorage.
*/
namespace Drupal\shortcut;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\Entity\ConfigEntityStorage;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a storage for shortcut_set entities.
*/
class ShortcutSetStorage extends ConfigEntityStorage implements ShortcutSetStorageInterface {
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a ShortcutSetStorageController object.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_info
* The entity info for the entity type.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory service.
* @param \Drupal\Core\Config\StorageInterface $config_storage
* The config storage service.
* @param \Drupal\Component\Uuid\UuidInterface $uuid_service
* The UUID service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
*/
public function __construct(EntityTypeInterface $entity_info, ConfigFactoryInterface $config_factory, UuidInterface $uuid_service, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) {
parent::__construct($entity_info, $config_factory, $uuid_service, $language_manager);
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
return new static(
$entity_info,
$container->get('config.factory'),
$container->get('uuid'),
$container->get('module_handler'),
$container->get('language_manager')
);
}
/**
* {@inheritdoc}
*/
public function deleteAssignedShortcutSets(ShortcutSetInterface $entity) {
// First, delete any user assignments for this set, so that each of these
// users will go back to using whatever default set applies.
db_delete('shortcut_set_users')
->condition('set_name', $entity->id())
->execute();
}
/**
* {@inheritdoc}
*/
public function assignUser(ShortcutSetInterface $shortcut_set, $account) {
db_merge('shortcut_set_users')
->key('uid', $account->id())
->fields(array('set_name' => $shortcut_set->id()))
->execute();
drupal_static_reset('shortcut_current_displayed_set');
}
/**
* {@inheritdoc}
*/
public function unassignUser($account) {
$deleted = db_delete('shortcut_set_users')
->condition('uid', $account->id())
->execute();
return (bool) $deleted;
}
/**
* {@inheritdoc}
*/
public function getAssignedToUser($account) {
$query = db_select('shortcut_set_users', 'ssu');
$query->fields('ssu', array('set_name'));
$query->condition('ssu.uid', $account->id());
return $query->execute()->fetchField();
}
/**
* {@inheritdoc}
*/
public function countAssignedUsers(ShortcutSetInterface $shortcut_set) {
return db_query('SELECT COUNT(*) FROM {shortcut_set_users} WHERE set_name = :name', array(':name' => $shortcut_set->id()))->fetchField();
}
/**
* {@inheritdoc}
*/
public function getDefaultSet(AccountInterface $account) {
// Allow modules to return a default shortcut set name. Since we can only
// have one, we allow the last module which returns a valid result to take
// precedence. If no module returns a valid set, fall back on the site-wide
// default, which is the lowest-numbered shortcut set.
$suggestions = array_reverse($this->moduleHandler->invokeAll('shortcut_default_set', array($account)));
$suggestions[] = 'default';
$shortcut_set = NULL;
foreach ($suggestions as $name) {
if ($shortcut_set = $this->load($name)) {
break;
}
}
return $shortcut_set;
}
}

View file

@ -0,0 +1,84 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\ShortcutSetStorageInterface.
*/
namespace Drupal\shortcut;
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Defines an interface for shortcut_set entity storage classes.
*/
interface ShortcutSetStorageInterface extends ConfigEntityStorageInterface {
/**
* Assigns a user to a particular shortcut set.
*
* @param \Drupal\shortcut\ShortcutSetInterface $shortcut_set
* An object representing the shortcut set.
* @param $account
* A user account that will be assigned to use the set.
*/
public function assignUser(ShortcutSetInterface $shortcut_set, $account);
/**
* Unassigns a user from any shortcut set they may have been assigned to.
*
* The user will go back to using whatever default set applies.
*
* @param $account
* A user account that will be removed from the shortcut set assignment.
*
* @return bool
* TRUE if the user was previously assigned to a shortcut set and has been
* successfully removed from it. FALSE if the user was already not assigned
* to any set.
*/
public function unassignUser($account);
/**
* Delete shortcut sets assigned to users.
*
* @param \Drupal\shortcut\ShortcutSetInterface $entity
* Delete the user assigned sets belonging to this shortcut.
*/
public function deleteAssignedShortcutSets(ShortcutSetInterface $entity);
/**
* Get the name of the set assigned to this user.
*
* @param \Drupal\user\Entity\User
* The user account.
*
* @return string
* The name of the shortcut set assigned to this user.
*/
public function getAssignedToUser($account);
/**
* Get the number of users who have this set assigned to them.
*
* @param \Drupal\shortcut\ShortcutSetInterface $shortcut_set
* The shortcut to count the users assigned to.
*
* @return int
* The number of users who have this set assigned to them.
*/
public function countAssignedUsers(ShortcutSetInterface $shortcut_set);
/**
* Gets the default shortcut set for a given user account.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The user account whose default shortcut set will be returned.
*
* @return \Drupal\shortcut\ShortcutSetInterface
* An object representing the default shortcut set.
*/
public function getDefaultSet(AccountInterface $account);
}

View file

@ -0,0 +1,75 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Tests\ShortcutCacheTagsTest.
*/
namespace Drupal\shortcut\Tests;
use Drupal\shortcut\Entity\Shortcut;
use Drupal\system\Tests\Entity\EntityCacheTagsTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
* Tests the Shortcut entity's cache tags.
*
* @group shortcut
*/
class ShortcutCacheTagsTest extends EntityCacheTagsTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('shortcut');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Give anonymous users permission to customize shortcut links, so that we
// can verify the cache tags of cached versions of shortcuts.
$user_role = Role::load(RoleInterface::ANONYMOUS_ID);
$user_role->grantPermission('customize shortcut links');
$user_role->grantPermission('access shortcuts');
$user_role->save();
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a "Llama" shortcut.
$shortcut = Shortcut::create(array(
'shortcut_set' => 'default',
'title' => t('Llama'),
'weight' => 0,
'link' => [['uri' => 'internal:/admin']],
));
$shortcut->save();
return $shortcut;
}
/**
* Tests that when creating a shortcut, the shortcut set tag is invalidated.
*/
public function testEntityCreation() {
// Create a cache entry that is tagged with a shortcut set cache tag.
$cache_tags = ['config:shortcut.set.default'];
\Drupal::cache('render')->set('foo', 'bar', \Drupal\Core\Cache\CacheBackendInterface::CACHE_PERMANENT, $cache_tags);
// Verify a cache hit.
$this->verifyRenderCache('foo', $cache_tags);
// Now create a shortcut entity in that shortcut set.
$this->createEntity();
// Verify a cache miss.
$this->assertFalse(\Drupal::cache('render')->get('foo'), 'Creating a new shortcut invalidates the cache tag of the shortcut set.');
}
}

View file

@ -0,0 +1,387 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Tests\ShortcutLinksTest.
*/
namespace Drupal\shortcut\Tests;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Url;
use Drupal\shortcut\Entity\Shortcut;
use Drupal\shortcut\Entity\ShortcutSet;
/**
* Create, view, edit, delete, and change shortcut links.
*
* @group shortcut
*/
class ShortcutLinksTest extends ShortcutTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('router_test', 'views', 'block');
/**
* Tests that creating a shortcut works properly.
*/
public function testShortcutLinkAdd() {
$set = $this->set;
// Create an alias for the node so we can test aliases.
$path = array(
'source' => '/node/' . $this->node->id(),
'alias' => '/' . $this->randomMachineName(8),
);
$this->container->get('path.alias_storage')->save($path['source'], $path['alias']);
// Create some paths to test.
$test_cases = [
'/',
'/admin',
'/admin/config/system/site-information',
'/node/' . $this->node->id() . '/edit',
'/' . $path['alias'],
'/router_test/test2',
'/router_test/test3/value',
];
$test_cases_non_access = [
'/admin',
'/admin/config/system/site-information',
];
// Check that each new shortcut links where it should.
foreach ($test_cases as $test_path) {
$title = $this->randomMachineName();
$form_data = array(
'title[0][value]' => $title,
'link[0][uri]' => $test_path,
);
$this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link', $form_data, t('Save'));
$this->assertResponse(200);
$this->assertText(t('Added a shortcut for @title.', array('@title' => $title)));
$saved_set = ShortcutSet::load($set->id());
$paths = $this->getShortcutInformation($saved_set, 'link');
$this->assertTrue(in_array('internal:' . $test_path, $paths), 'Shortcut created: ' . $test_path);
if (in_array($test_path, $test_cases_non_access)) {
$this->assertNoLink($title, SafeMarkup::format('Shortcut link %url not accessible on the page.', ['%url' => $test_path]));
}
else {
$this->assertLink($title, 0, SafeMarkup::format('Shortcut link %url found on the page.', ['%url' => $test_path]));
}
}
$saved_set = ShortcutSet::load($set->id());
// Test that saving and re-loading a shortcut preserves its values.
$shortcuts = $saved_set->getShortcuts();
foreach ($shortcuts as $entity) {
// Test the node routes with parameters.
$entity->save();
$loaded = Shortcut::load($entity->id());
$this->assertEqual($entity->link->uri, $loaded->link->uri);
$this->assertEqual($entity->link->options, $loaded->link->options);
}
// Login as non admin user, to check that access is checked when creating
// shortcuts.
$this->drupalLogin($this->shortcutUser);
$title = $this->randomMachineName();
$form_data = [
'title[0][value]' => $title,
'link[0][uri]' => '/admin',
];
$this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link', $form_data, t('Save'));
$this->assertResponse(200);
$this->assertRaw(t("The path '@link_path' is inaccessible.", ['@link_path' => '/admin']));
$form_data = [
'title[0][value]' => $title,
'link[0][uri]' => '/node',
];
$this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link', $form_data, t('Save'));
$this->assertLink($title, 0, 'Shortcut link found on the page.');
}
/**
* Tests that the "add to shortcut" and "remove from shortcut" links work.
*/
public function testShortcutQuickLink() {
\Drupal::service('theme_handler')->install(array('seven'));
$this->config('system.theme')->set('admin', 'seven')->save();
$this->config('node.settings')->set('use_admin_theme', '1')->save();
$this->container->get('router.builder')->rebuild();
$this->drupalLogin($this->rootUser);
$this->drupalGet('admin/config/system/cron');
// Test the "Add to shortcuts" link.
$this->clickLink('Add to Default shortcuts');
$this->assertText('Added a shortcut for Cron.');
$this->assertLink('Cron', 0, 'Shortcut link found on page');
$this->drupalGet('admin/structure');
$this->assertLink('Cron', 0, 'Shortcut link found on different page');
// Test the "Remove from shortcuts" link.
$this->clickLink('Cron');
$this->clickLink('Remove from Default shortcuts');
$this->assertText('The shortcut Cron has been deleted.');
$this->assertNoLink('Cron', 'Shortcut link removed from page');
$this->drupalGet('admin/structure');
$this->assertNoLink('Cron', 'Shortcut link removed from different page');
$this->drupalGet('admin/people');
// Test the "Add to shortcuts" link for a page generated by views.
$this->clickLink('Add to Default shortcuts');
$this->assertText('Added a shortcut for People.');
// Due to the structure of the markup in the link ::assertLink() doesn't
// works here.
$link = $this->xpath('//a[normalize-space()=:label]', array(':label' => 'Remove from Default shortcuts'));
$this->assertTrue(!empty($link), 'Link Remove from Default shortcuts found.');
// Test the "Remove from shortcuts" link for a page generated by views.
$this->clickLink('Remove from Default shortcuts');
$this->assertText('The shortcut People has been deleted.');
// Due to the structure of the markup in the link ::assertLink() doesn't
// works here.
$link = $this->xpath('//a[normalize-space()=:label]', array(':label' => 'Add to Default shortcuts'));
$this->assertTrue(!empty($link), 'Link Add to Default shortcuts found.');
// Test two pages which use same route name but different route parameters.
$this->drupalGet('node/add/page');
// Add Shortcut for Basic Page.
$this->clickLink('Add to Default shortcuts');
$this->assertText('Added a shortcut for Create Basic page.');
// Assure that Article does not have its shortcut indicated as set.
$this->drupalGet('node/add/article');
$link = $this->xpath('//a[normalize-space()=:label]', array(':label' => 'Remove from Default shortcuts'));
$this->assertTrue(empty($link), 'Link Remove to Default shortcuts not found for Create Article page.');
// Add Shortcut for Article.
$this->clickLink('Add to Default shortcuts');
$this->assertText('Added a shortcut for Create Article.');
}
/**
* Tests that shortcut links can be renamed.
*/
public function testShortcutLinkRename() {
$set = $this->set;
// Attempt to rename shortcut link.
$new_link_name = $this->randomMachineName();
$shortcuts = $set->getShortcuts();
$shortcut = reset($shortcuts);
$this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title[0][value]' => $new_link_name), t('Save'));
$saved_set = ShortcutSet::load($set->id());
$titles = $this->getShortcutInformation($saved_set, 'title');
$this->assertTrue(in_array($new_link_name, $titles), 'Shortcut renamed: ' . $new_link_name);
$this->assertLink($new_link_name, 0, 'Renamed shortcut link appears on the page.');
$this->assertText(t('The shortcut @link has been updated.', array('@link' => $new_link_name)));
}
/**
* Tests that changing the path of a shortcut link works.
*/
public function testShortcutLinkChangePath() {
$set = $this->set;
// Tests changing a shortcut path.
$new_link_path = '/admin/config';
$shortcuts = $set->getShortcuts();
$shortcut = reset($shortcuts);
$this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title[0][value]' => $shortcut->getTitle(), 'link[0][uri]' => $new_link_path), t('Save'));
$saved_set = ShortcutSet::load($set->id());
$paths = $this->getShortcutInformation($saved_set, 'link');
$this->assertTrue(in_array('internal:' . $new_link_path, $paths), 'Shortcut path changed: ' . $new_link_path);
$this->assertLinkByHref($new_link_path, 0, 'Shortcut with new path appears on the page.');
$this->assertText(t('The shortcut @link has been updated.', array('@link' => $shortcut->getTitle())));
}
/**
* Tests that changing the route of a shortcut link works.
*/
public function testShortcutLinkChangeRoute() {
$this->drupalLogin($this->rootUser);
$this->drupalGet('admin/content');
$this->assertResponse(200);
// Disable the view.
entity_load('view', 'content')->disable()->save();
/** @var \Drupal\Core\Routing\RouteBuilderInterface $router_builder */
$router_builder = \Drupal::service('router.builder');
$router_builder->rebuildIfNeeded();
$this->drupalGet('admin/content');
$this->assertResponse(200);
}
/**
* Tests deleting a shortcut link.
*/
public function testShortcutLinkDelete() {
$set = $this->set;
$shortcuts = $set->getShortcuts();
$shortcut = reset($shortcuts);
$this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id() . '/delete', array(), 'Delete');
$saved_set = ShortcutSet::load($set->id());
$ids = $this->getShortcutInformation($saved_set, 'id');
$this->assertFalse(in_array($shortcut->id(), $ids), 'Successfully deleted a shortcut.');
// Delete all the remaining shortcut links.
entity_delete_multiple('shortcut', array_filter($ids));
// Get the front page to check that no exceptions occur.
$this->drupalGet('');
}
/**
* Tests that the add shortcut link is not displayed for 404/403 errors.
*
* Tests that the "Add to shortcuts" link is not displayed on a page not
* found or a page the user does not have access to.
*/
public function testNoShortcutLink() {
// Change to a theme that displays shortcuts.
\Drupal::service('theme_handler')->install(array('seven'));
$this->config('system.theme')
->set('default', 'seven')
->save();
$this->drupalGet('page-that-does-not-exist');
$result = $this->xpath('//a[contains(@class, "shortcut-action--add")]');
$this->assertTrue(empty($result), 'Add to shortcuts link was not shown on a page not found.');
// The user does not have access to this path.
$this->drupalGet('admin/modules');
$result = $this->xpath('//a[contains(@class, "shortcut-action--add")]');
$this->assertTrue(empty($result), 'Add to shortcuts link was not shown on a page the user does not have access to.');
// Verify that the testing mechanism works by verifying the shortcut link
// appears on admin/content.
$this->drupalGet('admin/content');
$result = $this->xpath('//a[contains(@class, "shortcut-action--remove")]');
$this->assertTrue(!empty($result), 'Remove from shortcuts link was shown on a page the user does have access to.');
// Verify that the shortcut link appears on routing only pages.
$this->drupalGet('router_test/test2');
$result = $this->xpath('//a[contains(@class, "shortcut-action--add")]');
$this->assertTrue(!empty($result), 'Add to shortcuts link was shown on a page the user does have access to.');
}
/**
* Tests that the 'access shortcuts' permissions works properly.
*/
public function testAccessShortcutsPermission() {
// Change to a theme that displays shortcuts.
\Drupal::service('theme_handler')->install(array('seven'));
$this->config('system.theme')
->set('default', 'seven')
->save();
// Add cron to the default shortcut set.
$this->drupalLogin($this->rootUser);
$this->drupalGet('admin/config/system/cron');
$this->clickLink('Add to Default shortcuts');
// Verify that users without the 'access shortcuts' permission can't see the
// shortcuts.
$this->drupalLogin($this->drupalCreateUser(array('access toolbar')));
$this->assertNoLink('Shortcuts', 'Shortcut link not found on page.');
// Verify that users without the 'administer site configuration' permission
// can't see the cron shortcuts.
$this->drupalLogin($this->drupalCreateUser(array('access toolbar', 'access shortcuts')));
$this->assertNoLink('Shortcuts', 'Shortcut link not found on page.');
$this->assertNoLink('Cron', 'Cron shortcut link not found on page.');
// Verify that users with the 'access shortcuts' permission can see the
// shortcuts.
$this->drupalLogin($this->drupalCreateUser(array(
'access toolbar', 'access shortcuts', 'administer site configuration',
)));
$this->clickLink('Shortcuts', 0, 'Shortcut link found on page.');
$this->assertLink('Cron', 0, 'Cron shortcut link found on page.');
$this->verifyAccessShortcutsPermissionForEditPages();
}
/**
* Tests the shortcuts are correctly ordered by weight in the toolbar.
*/
public function testShortcutLinkOrder() {
// Ensure to give permissions to access the shortcuts.
$this->drupalLogin($this->drupalCreateUser(array('access toolbar', 'access shortcuts', 'access content overview', 'administer content types')));
$this->drupalGet(Url::fromRoute('<front>'));
$shortcuts = $this->cssSelect('#toolbar-item-shortcuts-tray .toolbar-menu a');
$this->assertEqual((string) $shortcuts[0], 'Add content');
$this->assertEqual((string) $shortcuts[1], 'All content');
foreach($this->set->getShortcuts() as $shortcut) {
$shortcut->setWeight($shortcut->getWeight() * -1)->save();
}
$this->drupalGet(Url::fromRoute('<front>'));
$shortcuts = $this->cssSelect('#toolbar-item-shortcuts-tray .toolbar-menu a');
$this->assertEqual((string) $shortcuts[0], 'All content');
$this->assertEqual((string) $shortcuts[1], 'Add content');
}
/**
* Tests that the 'access shortcuts' permission is required for shortcut set
* administration page access.
*/
private function verifyAccessShortcutsPermissionForEditPages() {
// Create a user with customize links and switch sets permissions but
// without the 'access shortcuts' permission.
$test_permissions = array(
'customize shortcut links',
'switch shortcut sets',
);
$noaccess_user = $this->drupalCreateUser($test_permissions);
$this->drupalLogin($noaccess_user);
// Verify that set administration pages are inaccessible without the
// 'access shortcuts' permission.
$edit_paths = array(
'admin/config/user-interface/shortcut/manage/default/customize',
'admin/config/user-interface/shortcut/manage/default',
'user/' . $noaccess_user->id() . '/shortcuts',
);
foreach ($edit_paths as $path) {
$this->drupalGet($path);
$message = format_string('Access is denied on %s', array('%s' => $path));
$this->assertResponse(403, $message);
}
}
/**
* Tests that the 'access shortcuts' permission is required to access the
* shortcut block.
*/
public function testShortcutBlockAccess() {
// Creates a block instance and place in a region through api.
$block = $this->drupalPlaceBlock('shortcuts');
// Verify that users with the 'access shortcuts' permission can see the
// shortcut block.
$this->drupalLogin($this->shortcutUser);
$this->drupalGet('');
$this->assertBlockAppears($block);
$this->drupalLogout();
// Verify that users without the 'access shortcuts' permission can see the
// shortcut block.
$this->drupalLogin($this->drupalCreateUser(array()));
$this->drupalGet('');
$this->assertNoBlockAppears($block);
}
}

View file

@ -0,0 +1,202 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Tests\ShortcutSetsTest.
*/
namespace Drupal\shortcut\Tests;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\shortcut\Entity\ShortcutSet;
/**
* Create, view, edit, delete, and change shortcut sets.
*
* @group shortcut
*/
class ShortcutSetsTest extends ShortcutTestBase {
/**
* Tests creating a shortcut set.
*/
function testShortcutSetAdd() {
$this->drupalGet('admin/config/user-interface/shortcut');
$this->clickLink(t('Add shortcut set'));
$edit = array(
'label' => $this->randomMachineName(),
'id' => strtolower($this->randomMachineName()),
);
$this->drupalPostForm(NULL, $edit, t('Save'));
$new_set = $this->container->get('entity.manager')->getStorage('shortcut_set')->load($edit['id']);
$this->assertIdentical($new_set->id(), $edit['id'], 'Successfully created a shortcut set.');
$this->drupalGet('user/' . $this->adminUser->id() . '/shortcuts');
$this->assertText($new_set->label(), 'Generated shortcut set was listed as a choice on the user account page.');
}
/**
* Tests editing a shortcut set.
*/
function testShortcutSetEdit() {
$set = $this->set;
$shortcuts = $set->getShortcuts();
// Visit the shortcut set edit admin ui.
$this->drupalGet('admin/config/user-interface/shortcut/manage/' . $set->id() . '/customize');
// Test for the page title.
$this->assertTitle(t('List links') . ' | Drupal');
// Test for the table.
$element = $this->xpath('//div[@class="layout-content"]//table');
$this->assertTrue($element, 'Shortcut entity list table found.');
// Test the table header.
$elements = $this->xpath('//div[@class="layout-content"]//table/thead/tr/th');
$this->assertEqual(count($elements), 3, 'Correct number of table header cells found.');
// Test the contents of each th cell.
$expected_items = array(t('Name'), t('Weight'), t('Operations'));
foreach ($elements as $key => $element) {
$this->assertIdentical((string) $element[0], $expected_items[$key]);
}
// Look for test shortcuts in the table.
$weight = count($shortcuts);
$edit = array();
foreach ($shortcuts as $shortcut) {
$title = $shortcut->getTitle();
// Confirm that a link to the shortcut is found within the table.
$this->assertLink($title);
// Look for a test shortcut weight select form element.
$this->assertFieldByName('shortcuts[links][' . $shortcut->id() . '][weight]');
// Change the weight of the shortcut.
$edit['shortcuts[links][' . $shortcut->id() . '][weight]'] = $weight;
$weight--;
}
$this->drupalPostForm(NULL, $edit, t('Save changes'));
$this->assertRaw(t('The shortcut set has been updated.'));
\Drupal::entityManager()->getStorage('shortcut')->resetCache();
// Check to ensure that the shortcut weights have changed and that
// ShortcutSet::.getShortcuts() returns shortcuts in the new order.
$this->assertIdentical(array_reverse(array_keys($shortcuts)), array_keys($set->getShortcuts()));
}
/**
* Tests switching a user's own shortcut set.
*/
function testShortcutSetSwitchOwn() {
$new_set = $this->generateShortcutSet($this->randomMachineName());
// Attempt to switch the default shortcut set to the newly created shortcut
// set.
$this->drupalPostForm('user/' . $this->adminUser->id() . '/shortcuts', array('set' => $new_set->id()), t('Change set'));
$this->assertResponse(200);
$current_set = shortcut_current_displayed_set($this->adminUser);
$this->assertTrue($new_set->id() == $current_set->id(), 'Successfully switched own shortcut set.');
}
/**
* Tests switching another user's shortcut set.
*/
function testShortcutSetAssign() {
$new_set = $this->generateShortcutSet($this->randomMachineName());
\Drupal::entityManager()->getStorage('shortcut_set')->assignUser($new_set, $this->shortcutUser);
$current_set = shortcut_current_displayed_set($this->shortcutUser);
$this->assertTrue($new_set->id() == $current_set->id(), "Successfully switched another user's shortcut set.");
}
/**
* Tests switching a user's shortcut set and creating one at the same time.
*/
function testShortcutSetSwitchCreate() {
$edit = array(
'set' => 'new',
'id' => strtolower($this->randomMachineName()),
'label' => $this->randomString(),
);
$this->drupalPostForm('user/' . $this->adminUser->id() . '/shortcuts', $edit, t('Change set'));
$current_set = shortcut_current_displayed_set($this->adminUser);
$this->assertNotEqual($current_set->id(), $this->set->id(), 'A shortcut set can be switched to at the same time as it is created.');
$this->assertEqual($current_set->label(), $edit['label'], 'The new set is correctly assigned to the user.');
}
/**
* Tests switching a user's shortcut set without providing a new set name.
*/
function testShortcutSetSwitchNoSetName() {
$edit = array('set' => 'new');
$this->drupalPostForm('user/' . $this->adminUser->id() . '/shortcuts', $edit, t('Change set'));
$this->assertRaw(\Drupal::translation()->formatPlural(1, '1 error has been found: !errors', '@count errors have been found: !errors', [
'!errors' => SafeMarkup::set('<a href="#edit-label">Label</a>')
]));
$current_set = shortcut_current_displayed_set($this->adminUser);
$this->assertEqual($current_set->id(), $this->set->id(), 'Attempting to switch to a new shortcut set without providing a set name does not succeed.');
$this->assertFieldByXPath("//input[@name='label' and contains(concat(' ', normalize-space(@class), ' '), ' error ')]", NULL, 'The new set label field has the error class');
}
/**
* Tests renaming a shortcut set.
*/
function testShortcutSetRename() {
$set = $this->set;
$new_label = $this->randomMachineName();
$this->drupalGet('admin/config/user-interface/shortcut');
$this->clickLink(t('Edit shortcut set'));
$this->drupalPostForm(NULL, array('label' => $new_label), t('Save'));
$set = ShortcutSet::load($set->id());
$this->assertTrue($set->label() == $new_label, 'Shortcut set has been successfully renamed.');
}
/**
* Tests unassigning a shortcut set.
*/
function testShortcutSetUnassign() {
$new_set = $this->generateShortcutSet($this->randomMachineName());
$shortcut_set_storage = \Drupal::entityManager()->getStorage('shortcut_set');
$shortcut_set_storage->assignUser($new_set, $this->shortcutUser);
$shortcut_set_storage->unassignUser($this->shortcutUser);
$current_set = shortcut_current_displayed_set($this->shortcutUser);
$default_set = shortcut_default_set($this->shortcutUser);
$this->assertTrue($current_set->id() == $default_set->id(), "Successfully unassigned another user's shortcut set.");
}
/**
* Tests deleting a shortcut set.
*/
function testShortcutSetDelete() {
$new_set = $this->generateShortcutSet($this->randomMachineName());
$this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $new_set->id() . '/delete', array(), t('Delete'));
$sets = ShortcutSet::loadMultiple();
$this->assertFalse(isset($sets[$new_set->id()]), 'Successfully deleted a shortcut set.');
}
/**
* Tests deleting the default shortcut set.
*/
function testShortcutSetDeleteDefault() {
$this->drupalGet('admin/config/user-interface/shortcut/manage/default/delete');
$this->assertResponse(403);
}
/**
* Tests creating a new shortcut set with a defined set name.
*/
function testShortcutSetCreateWithSetName() {
$random_name = $this->randomMachineName();
$new_set = $this->generateShortcutSet($random_name, $random_name);
$sets = ShortcutSet::loadMultiple();
$this->assertTrue(isset($sets[$random_name]), 'Successfully created a shortcut set with a defined set name.');
$this->drupalGet('user/' . $this->adminUser->id() . '/shortcuts');
$this->assertText($new_set->label(), 'Generated shortcut set was listed as a choice on the user account page.');
}
}

View file

@ -0,0 +1,138 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Tests\ShortcutTestBase.
*/
namespace Drupal\shortcut\Tests;
use Drupal\shortcut\Entity\Shortcut;
use Drupal\shortcut\Entity\ShortcutSet;
use Drupal\shortcut\ShortcutSetInterface;
use Drupal\simpletest\WebTestBase;
/**
* Defines base class for shortcut test cases.
*/
abstract class ShortcutTestBase extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('node', 'toolbar', 'shortcut');
/**
* User with permission to administer shortcuts.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* User with permission to use shortcuts, but not administer them.
*
* @var \Drupal\user\UserInterface
*/
protected $shortcutUser;
/**
* Generic node used for testing.
*
* @var \Drupal\node\NodeInterface
*/
protected $node;
/**
* Site-wide default shortcut set.
*
* @var \Drupal\shortcut\ShortcutSetInterface
*/
protected $set;
protected function setUp() {
parent::setUp();
if ($this->profile != 'standard') {
// Create Basic page and Article node types.
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
$this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
// Populate the default shortcut set.
$shortcut = Shortcut::create(array(
'shortcut_set' => 'default',
'title' => t('Add content'),
'weight' => -20,
'link' => array(
'uri' => 'internal:/node/add',
),
));
$shortcut->save();
$shortcut = Shortcut::create(array(
'shortcut_set' => 'default',
'title' => t('All content'),
'weight' => -19,
'link' => array(
'uri' => 'internal:/admin/content',
),
));
$shortcut->save();
}
// Create users.
$this->adminUser = $this->drupalCreateUser(array('access toolbar', 'administer shortcuts', 'view the administration theme', 'create article content', 'create page content', 'access content overview', 'administer users', 'link to any page', 'edit any article content'));
$this->shortcutUser = $this->drupalCreateUser(array('customize shortcut links', 'switch shortcut sets', 'access shortcuts', 'access content'));
// Create a node.
$this->node = $this->drupalCreateNode(array('type' => 'article'));
// Log in as admin and grab the default shortcut set.
$this->drupalLogin($this->adminUser);
$this->set = ShortcutSet::load('default');
\Drupal::entityManager()->getStorage('shortcut_set')->assignUser($this->set, $this->adminUser);
}
/**
* Creates a generic shortcut set.
*/
function generateShortcutSet($label = '', $id = NULL) {
$set = ShortcutSet::create(array(
'id' => isset($id) ? $id : strtolower($this->randomMachineName()),
'label' => empty($label) ? $this->randomString() : $label,
));
$set->save();
return $set;
}
/**
* Extracts information from shortcut set links.
*
* @param \Drupal\shortcut\ShortcutSetInterface $set
* The shortcut set object to extract information from.
* @param string $key
* The array key indicating what information to extract from each link:
* - 'title': Extract shortcut titles.
* - 'link': Extract shortcut paths.
* - 'id': Extract the shortcut ID.
*
* @return array
* Array of the requested information from each link.
*/
function getShortcutInformation(ShortcutSetInterface $set, $key) {
$info = array();
\Drupal::entityManager()->getStorage('shortcut')->resetCache();
foreach ($set->getShortcuts() as $shortcut) {
if ($key == 'link') {
$info[] = $shortcut->link->uri;
}
else {
$info[] = $shortcut->{$key}->value;
}
}
return $info;
}
}

View file

@ -0,0 +1,118 @@
<?php
/**
* @file
* Contains \Drupal\shortcut\Tests\ShortcutTranslationUITest.
*/
namespace Drupal\shortcut\Tests;
use Drupal\content_translation\Tests\ContentTranslationUITestBase;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Language\Language;
/**
* Tests the shortcut translation UI.
*
* @group Shortcut
*/
class ShortcutTranslationUITest extends ContentTranslationUITestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array(
'language',
'content_translation',
'link',
'shortcut',
'toolbar'
);
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->entityTypeId = 'shortcut';
$this->bundle = 'default';
parent::setUp();
}
/**
* {@inheritdoc}
*/
protected function getTranslatorPermissions() {
return array_merge(parent::getTranslatorPermissions(), array('access shortcuts', 'administer shortcuts', 'access toolbar'));
}
/**
* {@inheritdoc}
*/
protected function createEntity($values, $langcode, $bundle_name = NULL) {
$values['link']['uri'] = 'internal:/user';
return parent::createEntity($values, $langcode, $bundle_name);
}
/**
* {@inheritdoc}
*/
protected function getNewEntityValues($langcode) {
return array('title' => array(array('value' => $this->randomMachineName()))) + parent::getNewEntityValues($langcode);
}
protected function doTestBasicTranslation() {
parent::doTestBasicTranslation();
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
foreach ($this->langcodes as $langcode) {
if ($entity->hasTranslation($langcode)) {
$language = new Language(array('id' => $langcode));
// Request the front page in this language and assert that the right
// translation shows up in the shortcut list with the right path.
$this->drupalGet('<front>', array('language' => $language));
$expected_path = \Drupal::urlGenerator()->generateFromRoute('user.page', array(), array('language' => $language));
$label = $entity->getTranslation($langcode)->label();
$elements = $this->xpath('//nav[contains(@class, "toolbar-lining")]/ul[@class="toolbar-menu"]/li/a[contains(@href, :href) and normalize-space(text())=:label]', array(':href' => $expected_path, ':label' => $label));
$this->assertTrue(!empty($elements), format_string('Translated @language shortcut link @label found.', array('@label' => $label, '@language' => $language->getName())));
}
}
}
/**
* {@inheritdoc}
*/
protected function doTestTranslationEdit() {
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$languages = $this->container->get('language_manager')->getLanguages();
foreach ($this->langcodes as $langcode) {
// We only want to test the title for non-english translations.
if ($langcode != 'en') {
$options = array('language' => $languages[$langcode]);
$url = $entity->urlInfo('edit-form', $options);
$this->drupalGet($url);
$title = t('@title [%language translation]', array(
'@title' => $entity->getTranslation($langcode)->label(),
'%language' => $languages[$langcode]->getName(),
));
$this->assertRaw($title);
}
}
}
/**
* Tests the basic translation workflow.
*/
protected function doTestTranslationChanged() {
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$this->assertFalse(
$entity instanceof EntityChangedInterface,
format_string('%entity is not implementing EntityChangedInterface.' , array('%entity' => $this->entityTypeId))
);
}
}

View file

@ -0,0 +1,50 @@
<?php
/**
* @file
* Contains \Drupal\Tests\shortcut\Unit\Menu\ShortcutLocalTasksTest.
*/
namespace Drupal\Tests\shortcut\Unit\Menu;
use Drupal\Tests\Core\Menu\LocalTaskIntegrationTestBase;
/**
* Tests existence of shortcut local tasks.
*
* @group shortcut
*/
class ShortcutLocalTasksTest extends LocalTaskIntegrationTestBase {
protected function setUp() {
$this->directoryList = array(
'shortcut' => 'core/modules/shortcut',
'user' => 'core/modules/user',
);
parent::setUp();
}
/**
* Checks shortcut listing local tasks.
*
* @dataProvider getShortcutPageRoutes
*/
public function testShortcutPageLocalTasks($route) {
$tasks = array(
0 => array('shortcut.set_switch', 'entity.user.canonical', 'entity.user.edit_form',),
);
$this->assertLocalTasks($route, $tasks);
}
/**
* Provides a list of routes to test.
*/
public function getShortcutPageRoutes() {
return array(
array('entity.user.canonical'),
array('entity.user.edit_form'),
array('shortcut.set_switch'),
);
}
}