foldershare-8.x-1.2/src/Form/UIAncestorMenu.php
src/Form/UIAncestorMenu.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | <?php namespace Drupal\foldershare\Form; use Drupal\Core\Url; use Drupal\Component\Utility\Html; use Symfony\Component\HttpFoundation\Request; use Drupal\foldershare\Constants; use Drupal\foldershare\Entity\FolderShare; use Drupal\foldershare\FolderShareInterface; /** * Provides support for the ancestor menu used on multiple pages. * * <B>Warning:</B> This class is strictly internal to the FolderShare * module. The class's existance, name, and content may change from * release to release without any promise of backwards compatability. * * @ingroup foldershare */ final class UIAncestorMenu { /*-------------------------------------------------------------------- * * Functions. * *-------------------------------------------------------------------*/ /** * Creates a render description for an ancestor menu. * * The last item in the ancestor menu is a list of available root lists * for the user: * - Personal files. * - Public files. * - All files. * * The anonoymous user always sees only the "public" list, regardless of * the value of the $allowPublicRootList argument. * * The "all" list is only included if the user has module admin permissions. * * The "public" list is only included if $allowPublicRootList is TRUE. * * @param int $currentId * (optional, default = FolderShareInterface::USER_ROOT_LIST) The integer * entity ID of the current FolderShare item. If the value is negative or * FolderShareInterface::USER_ROOT_LIST, if the current item is * the user's root list. * @param bool $allowPublicRootList * (optional, default = TRUE) When TRUE, the public root list is available. * * @return array * Returns an array of renderable elements for an ancestor menu's * entries. */ public static function build( int $currentId = FolderShareInterface::USER_ROOT_LIST, bool $allowPublicRootList = TRUE) { // // Setup. // ------ // Get services and the current request. $routeProvider = \Drupal::service( 'router.route_provider' ); $titleResolver = \Drupal::service( 'title_resolver' ); $dummyRequest = new Request(); // // Load entity. // ------------ // Load the current entity, if any. $currentItem = NULL; if ( $currentId >= 0) { $currentItem = FolderShare::load( $currentId ); if ( $currentItem === NULL) { $currentId = FolderShareInterface::USER_ROOT_LIST; } } $uiClass = 'foldershare-ancestormenu' ; $menuButtonClass = $uiClass . '-menu-button' ; $menuClass = $uiClass . '-menu' ; // // Add ancestor list markup. // ------------------------- // Build a list of ancestor folders for the ancestor menu. For each one, // include the ancestor's URL as an attribute. Javascript will use this // to load the appropriate page. The URL is not included in an <a> for // the menu item so that menu items aren't styled as links. // // File classes are added so that themes can style the item with // folder icons. $menuMarkup = "<ul class=\"hidden $menuClass\">" ; if ( $currentItem !== NULL) { // The page is for entity. Get its ancestors. $folders = $currentItem ->findAncestorFolders(); // Reverse ancestors from root first to root last. $folders = array_reverse ( $folders ); // Push the current entity onto the ancestor list so that it gets // included in the menu. array_unshift ( $folders , $currentItem ); // Add ancestors to menu. foreach ( $folders as $item ) { // Get the URL to the folder. $url = rawurlencode( $item ->toUrl( 'canonical' , [ 'absolute' => TRUE, ])->toString()); // Get the name for the folder. $name = Html::escape( $item ->getName()); // Add the HTML. Include file classes that mark this as a folder // or file. if ( $item ->isFolder() === TRUE) { $fileClasses = 'file file--folder file--mime-folder-directory' ; } else { $mimes = explode ( '/' , $item ->getMimeType()); $fileClasses = 'file file--' . $mimes [0] . ' file--mime-' . $mimes [0] . '-' . $mimes [1]; } if ( $item ->isSystemDisabled() === TRUE) { $attr = '' ; } else { $attr = 'data-foldershare-id="' . $item ->id() . '"' ; } $menuMarkup .= "<li $attr data-foldershare-url=\"$url\"><div><span class=\"$fileClasses\"></span>$name</div></li>" ; } } // // Add root list. // -------------- // Show a list of available root lists in a submenu. If there is only // one available root list, then don't use a submenu. $currentUser = \Drupal::currentUser(); // Start by assembling a list of available root lists for this user. $rootLists = []; if ( $currentUser ->isAnonymous() === TRUE) { // The anonymous user can only view the public list. $rootLists [] = [ "public" , Constants::ROUTE_ROOT_ITEMS_PUBLIC, ]; } elseif ( $currentUser ->hasPermission(Constants::ADMINISTER_PERMISSION) === TRUE) { // An admin user can view all of the lists. $rootLists [] = [ "personal" , Constants::ROUTE_ROOT_ITEMS_PERSONAL, ]; if ( $allowPublicRootList === TRUE) { $rootLists [] = [ "public" , Constants::ROUTE_ROOT_ITEMS_PUBLIC, ]; } $rootLists [] = [ "all" , Constants::ROUTE_ROOT_ITEMS_ALL, ]; } else { // Authenticated non-admin users can view their personal list. $rootLists [] = [ "personal" , Constants::ROUTE_ROOT_ITEMS_PERSONAL, ]; if ( $allowPublicRootList === TRUE) { $rootLists [] = [ "public" , Constants::ROUTE_ROOT_ITEMS_PUBLIC, ]; } } // Build markup for the available root lists. $fileClasses = 'file file--folder file--mime-rootfolder-group-directory' ; if ( count ( $rootLists ) === 1) { $rootListName = $rootLists [0][0]; $rootListRoute = $rootLists [0][1]; // Get the root list's route. $route = $routeProvider ->getRouteByName( $rootListRoute ); // Create a route URL to the root list. $url = Url::fromRoute( $rootListRoute , [], [ 'absolute' => TRUE])->toString(); // Get the route's title. $title = $titleResolver ->getTitle( $dummyRequest , $route ); // Create markup for a menu entry to the root list. $attr = "data-foldershare-id=\"$rootListName\" data-foldershare-url=\"$url\"" ; $menuMarkup .= "<li $attr><div><span class=\"$fileClasses\"></span>$title</div></li>" ; } else { $title = (string) t( 'Lists' ); $menuMarkup .= "<li><div>$title</div><ul>" ; foreach ( $rootLists as $rootList ) { $rootListName = $rootList [0]; $rootListRoute = $rootList [1]; // Get the root list's route. $route = $routeProvider ->getRouteByName( $rootListRoute ); // Create a route URL to the root list. $url = Url::fromRoute( $rootListRoute , [], [ 'absolute' => TRUE])->toString(); // Get the route's title. $title = $titleResolver ->getTitle( $dummyRequest , $route ); // Create markup for a menu entry to the root list. $attr = "data-foldershare-id=\"$rootListName\" data-foldershare-url=\"$url\"" ; $menuMarkup .= "<li $attr><div><span class=\"$fileClasses\"></span>$title</div></li>" ; } $menuMarkup .= "</ul></li>" ; } $menuMarkup .= '</ul>' ; // // Create menu button. // ------------------- // Create HTML for a button. Include: // // - Class 'hidden' so that the button is initially hidden and only shown // later by Javascript, if the browser supports scripting. $buttonText = (string) t( 'Ancestors' ); $buttonMarkup = "<button type=\"button\" class=\"hidden $menuButtonClass\"><span>$buttonText</span></button>" ; // // Create UI // --------- // Everything is hidden initially, and only exposed by Javascript, if // the browser supports Javascript. $renderable = [ '#attributes' => [ 'class' => [ 'foldershare-ancestormenu' , ], ], $uiClass => [ '#type' => 'container' , '#weight' => -90, '#attributes' => [ 'class' => [ $uiClass , 'hidden' , ], ], // Add a hierarchical menu of ancestors. Javascript uses jQuery.ui // to build a menu from this and presents it from a menu button. // The menu was built and marked as hidden. // // Implementation note: The field is built using an inline template // to avoid Drupal's HTML cleaning that can remove classes and // attributes on the menu items, which we need to retain to provide // the URLs of ancestor folders. Those URLs are used by Javascript // to load the appropriate page when a menu item is selected. $menuClass => [ '#type' => 'inline_template' , '#template' => '{{ menu|raw }}' , '#context' => [ 'menu' => $menuMarkup , ], ], // Add a button to go up a folder. Javascript binds a behavior // to the button to load the parent page. The button is hidden // initially and only shown if the browser supports Javascript. // // Implementation note: The field is built using an inline template // so that we get a <button>. If we used the '#type' 'button', // Drupal instead creates an <input>. Since we specifically want a // button so that jQuery.button() will button-ize it, we have to // bypass Drupal. $menuButtonClass => [ '#type' => 'inline_template' , '#template' => '{{ button|raw }}' , '#context' => [ 'button' => $buttonMarkup , ], ], ], ]; return $renderable ; } } |