diff --git a/menu_example/menu_example.info.yml b/menu_example/menu_example.info.yml new file mode 100644 index 0000000..e9f6e4e --- /dev/null +++ b/menu_example/menu_example.info.yml @@ -0,0 +1,7 @@ +name: Menu Example +type: module +description: 'An example module showing the main steps to define and handling menu links in Drupal 8 ' +package: 'Example modules' +core: 8.x +dependencies: + - drupal:examples diff --git a/menu_example/menu_example.links.menu.yml b/menu_example/menu_example.links.menu.yml new file mode 100644 index 0000000..01059b7 --- /dev/null +++ b/menu_example/menu_example.links.menu.yml @@ -0,0 +1,77 @@ +examples.menu_example: + title: Menu Example + description: 'Simplest possible menu type, and the parent menu entry for others' + expanded: 1 + route_name: examples.menu_example + +examples.menu_example.alternate_menu: + title: Menu Example: Menu in alternate menu + menu_name: 'main' + route_name: examples.menu_example.alternate_menu + +examples.menu_example.permissioned: + title: Permissioned Example + parent: examples.menu_example + expanded: 1 + route_name: examples.menu_example.permissioned + weight: 10 + +examples.menu_example.permissioned_controlled: + title: Permissioned Menu Item + parent: examples.menu_example.permissioned + route_name: examples.menu_example.permissioned_controlled + weight: 10 + +examples.menu_example.custom_access: + title: Custom Access Example + parent: examples.menu_example + expanded: 1 + route_name: examples.menu_example.custom_access + weight: -5 + +examples.menu_example.custom_access_page: + title: Custom Access Menu Item + parent: examples.menu_example.custom_access + route_name: examples.menu_example.custom_access_page + +examples.menu_example.path_only: + title: Route only example + parent: examples.menu_example + route_name: examples.menu_example.path_only + weight: 20 + +examples.menu_example.tabs: + title: Tabs + description: 'Shows how to create primary and secondary tabs' + parent: examples.menu_example + route_name: examples.menu_example.tabs + weight: 30 + +examples.menu_example.use_url_arguments: + title: Extra Arguments + description: 'The page callback can use the arguments provided after the path used as key' + parent: examples.menu_example + route_name: examples.menu_example.use_url_arguments + weight: 40 + +examples.menu_example.title_callbacks: + title: Dynamic title + description: 'The title of this menu item is dynamically generated' + parent: examples.menu_example + route_name: examples.menu_example.title_callbacks + weight: 50 + +examples.menu_example.placeholder_argument: + title: Placeholder Arguments + description: '' + parent: examples.menu_example + route_name: examples.menu_example.placeholder_argument + weight: 60 + +example.menu_example.default_arg.arg_optional: + title: Processed placeholder Arguments + description: '' + parent: examples.menu_example + route_name: examples.menu_example.default_arg.arg_optional + weight: 60 + diff --git a/menu_example/menu_example.links.task.yml b/menu_example/menu_example.links.task.yml new file mode 100644 index 0000000..c1e58a1 --- /dev/null +++ b/menu_example/menu_example.links.task.yml @@ -0,0 +1,41 @@ +examples.menu_example.tabs: + route_name: examples.menu_example.tabs + title: 'Default Primary Tab' + base_route: examples.menu_example.tabs + weight: 1 + +examples.menu_example.tabOne: + route_name: examples.menu_example.tabname + title: 'two' + base_route: examples.menu_example.tabs + weight: 2 + +examples.menu_example.tabTwo: + route_name: examples.menu_example.tabnameThree + title: 'three' + base_route: examples.menu_example.tabs + weight: 3 + +examples.menu_example.tabThree: + route_name: examples.menu_example.tabnameFour + title: 'four' + base_route: examples.menu_example.tabs + weight: 4 + +examples.menu_example.secondaryDefault: + route_name: examples.menu_example.tabs + title: 'Default Secondary Tab' + parent_id: examples.menu_example.tabs + weight: 1 + +examples.menu_example.secondaryDefaultOne: + route_name: examples.menu_example.defaultSecond + title: 'Second Secondary Tab' + parent_id: examples.menu_example.tabs + weight: 2 + +examples.menu_example.secondaryDefaultTwo: + route_name: examples.menu_example.defaultThird + title: 'Third Secondary Tab' + parent_id: examples.menu_example.tabs + weight: 3 diff --git a/menu_example/menu_example.module b/menu_example/menu_example.module new file mode 100644 index 0000000..6b639f1 --- /dev/null +++ b/menu_example/menu_example.module @@ -0,0 +1,6 @@ +t('visit a similar page with no menu link'), $url)->toString(); + return [ + '#markup' => $this->t('This page is displayed by the simplest (and base) + menu example. Note that the title of the page is the same as the link + title. You can also @link. There are a number of + examples here, from the most basic (like this one) to extravagant + mappings of loaded placeholder arguments. Enjoy!', ['@link' => $link]), + ]; + } + + /** + * + */ + public function alternateMenu() { + return [ + '#markup' => t('This will be in the Main menu instead of the default Tools menu'), + ]; + + } + + /** + * + */ + public function permissioned() { + $url = Url::fromUri('internal:/examples/menu_example/permissioned/controlled'); + $link = Link::fromTextAndUrl($this->t('examples/menu_example/permissioned/controlled'), $url)->toString(); + return [ + '#markup' => $this->t('A menu item that requires the "access protected menu example" permission is at @link', ['@link' => $link]), + ]; + } + + /** + * + */ + public function permissionedControlled() { + return [ + '#markup' => $this->t('This menu entry will not show and the page will not be accessible without the "access protected menu example" permission.'), + ]; + } + + /** + * + */ + public function customAccess() { + $url = Url::fromUri('internal:/examples/menu_example/custom-access-page'); + $link = Link::fromTextAndUrl($this->t('examples/menu_example/custom-access-page'), $url)->toString(); + return [ + '#markup' => $this->t('A menu item that requires the user to posess a role of "authenticated" is at @link', ['@link' => $link]), + ]; + } + + /** + * + */ + public function pathonly() { + $url = Url::fromUri('internal:/examples/menu_example/path-only/callback'); + $link = Link::fromTextAndUrl($this->t('examples/menu_example/path-only/callback'), $url)->toString(); + return [ + '#markup' => $this->t('A menu entry with no menu link (MENU_CALLBACK) is at @link', ['@link' => $link]), + ]; + } + + /** + * + */ + public function tabs() { + return [ + '#markup' => $this->t('This is the "tabs" menu entry.'), + ]; + } + + /** + * + */ + public function tabNameSecond() { + return [ + '#markup' => $this->t('This is the tab "Second" in the "basic tabs" example'), + ]; + } + + /** + * + */ + public function tabNameThird() { + return [ + '#markup' => $this->t('This is the tab "Thrid" in the "basic tabs" example'), + ]; + } + + /** + * + */ + public function tabNameForth() { + return [ + '#markup' => $this->t('This is the tab "Forth" in the "basic tabs" example'), + ]; + } + + /** + * + */ + public function defaultSecond() { + return [ + '#markup' => $this->t('This is the secondary tab second in the "basic tabs" example "default"tab'), + ]; + } + + /** + * + */ + public function defaultThird() { + return [ + '#markup' => $this->t('This is the secondary tab Third in the "basic tabs" example "default"tab'), + ]; + } + + /** + * + */ + public function urlArgument() { + $url = Url::fromUri('internal:/examples/menu_example/use-url-arguments/firstarg/secondarg'); + $link = Link::fromTextAndUrl($this->t('examples/menu_example/use-url-arguments/firstarg/secondarg'), $url)->toString(); + $urle = Url::fromUri('internal:/examples/menu_example/use-url-arguments/one/two'); + $linker = Link::fromTextAndUrl($this->t('examples/menu_example/use-url-arguments/one/two'), $urle)->toString(); + return [ + '#markup' => $this->t('This page demonstrates using arguments in the path (portions of the path after "menu_example/use-url-arguments". For example, access it with @link or @linker', ['@link' => $link, '@linker' => $linker]), + + ]; + } + + /** + * + */ + public function urlArgument1($arg1 = '') { + return [ + '#markup' => $this->t('Argument1 = @arg1', ['@arg1' => $arg1]), + ]; + } + + /** + * + */ + public function urlArgument2($arg1, $arg2) { + return [ + '#markup' => $this->t('Argument1 = @arg1 , Argument2 = @arg2', ['@arg1' => $arg1, '@arg2' => $arg2]), + ]; + } + + /** + * + */ + public function titleCallback() { + return [ + '#markup' => $this->t('The title of this page is dynamically changed by the title callback for this route.'), + ]; + } + + /** + * + */ + public function backTitle($title = '') { + // Pass your uid. + $user = \Drupal::currentUser(); + $title = $user->getDisplayName(); + // $title = $account->getUsername()->toString(); + // $account = \Drupal::currentUser()->id(); + // $title=$account->get('name')->value; + return [ + '#markup' => $this->t('Dynamic Title: username = @title', ['@title' => $title]), + ]; + } + + /** + * + */ + public function argument() { + $url = Url::fromUri('internal:/examples/menu_example/placeholder-argument/3343/display'); + $link = Link::fromTextAndUrl($this->t('examples/menu_example/placeholder-argument/3343/display'), $url)->toString(); + return [ + '#markup' => $this->t('Demonstrate placeholders by visiting @link', ['@link' => $link]), + ]; + } + + /** + * + */ + public function display($node = '') { + return [ + '#markup' => $node, + ]; + + } + + /** + * + */ + public function orginalPath() { + return [ + '#markup' => $this->t('This menu item was created strictly to allow the hook_menu_alter() function to have something to operate on.hook_menu defined the path as examples/menu_example/menu_original_path. The hook_menu_alter() changes it to examples/menu_example/menu_altered_path. You can try navigating to both paths and see what happens!'), + ]; + } + + /** + * + */ + public function pathOnlycallback() { + return [ + '#markup' => $this->t('The menu entry for this page is of type MENU_CALLBACK, so it provides only a path but not a link in the menu links, but it is the same in every other way to the simplest example.'), + ]; + } + + /** + * + */ + public function argOptional($no = '') { + $mapped_value = NULL; + static $mappings = [ + 1 => 'one', + 2 => 'two', + 3 => 'three', + 99 => 'jackpot! default', + ]; + if (isset($mappings[$no])) { + $mapped_value = $mappings[$no]; + } + if (!empty($mapped_value)) { + return [ + '#markup' => $this->t('Loaded value was @loaded', ['@loaded' => $mapped_value]), + ]; + } + else { + return [ + '#markup' => $this->t('Sorry, the id @id was not found to be loaded', ['@id' => $no]), + ]; + } + + } + + /** + * + */ + public function customAccessPage() { + return [ + '#markup' => $this->t('This menu entry will not be visible and access will result + in a 403 error unless the user has the "authenticated" role. This is + accomplished with a custom access callback.'), + ]; + } + + /** + * + */ + public function getRole(AccountInterface $account) { + + return AccessResult::allowedIf($account->hasPermission('authenticated')); + } + +} + +/** + * + */ diff --git a/menu_example/tests/menu_example/functional/MenuExampleTest.php b/menu_example/tests/menu_example/functional/MenuExampleTest.php new file mode 100644 index 0000000..37f383b --- /dev/null +++ b/menu_example/tests/menu_example/functional/MenuExampleTest.php @@ -0,0 +1,106 @@ +assertTrue(\Drupal::moduleHandler()->moduleExists('menu_example')); + } + + public function testMenuExample() { + + $this->drupalGet(''); + $this->clickLink(t('Menu Example')); + $this->assertText(t('This is the base page of the Menu Example')); + + $this->clickLink(t('Custom Access Example')); + $this->assertText(t('Custom Access Example')); + + $this->drupalGet('examples/menu_example/permissioned'); + $this->assertText(t('Permissioned Example')); + + $this->clickLink('examples/menu_example/permissioned/controlled'); + $this->assertResponse(403); + + $this->drupalGet('examples/menu_example'); + + $this->clickLink(t('MENU_CALLBACK example')); + + $this->clickLink(t('Tabs')); + $this->assertText(t('This is the "tabs" menu entry')); + + $this->drupalGet('examples/menu_example/tabs/second'); + $this->assertText(t('This is the tab "second" in the "basic tabs" example')); + + $this->clickLink(t('third')); + $this->assertText(t('This is the tab "third" in the "basic tabs" example')); + + $this->clickLink(t('Extra Arguments')); + + $this->drupalGet('examples/menu_example/use-url-arguments/one/two'); + $this->assertText(t('Argument 1=one Argument 2=two')); + + $this->clickLink(t('Placeholder Arguments')); + + $this->clickLink(t('examples/menu_example/placeholder-argument/3343/display')); + $this->assertRaw('
3343
'); + + $this->clickLink(t('Processed Placeholder Arguments')); + $this->assertText(t('Loaded value was jackpot! default')); + + // Create a user with permissions to access protected menu entry. + $web_user = $this->drupalCreateUser(array('access protected menu example')); + + // Use custom overridden drupalLogin function to verify the user is logged + // in. + $this->drupalLogin($web_user); + + // Check that our title callback changing /user dynamically is working. + // Using ' because of the format_username function. + $this->assertRaw(t("@name's account", array('@name' => $web_user->getUsername())), format_string('Title successfully changed to account name: %name.', array('%name' => $web_user->getUsername()))); + + // Now start testing other menu entries. + $this->drupalGet('examples/menu_example'); + + $this->clickLink(t('Custom Access Example')); + $this->assertText(t('Custom Access Example')); + + $this->drupalGet('examples/menu_example/custom-access/page'); + $this->assertResponse(200); + + $this->drupalGet('examples/menu_example/permissioned'); + $this->assertText('Permissioned Example'); + $this->clickLink('examples/menu_example/permissioned/controlled'); + $this->assertText('This menu entry will not show'); + + $this->drupalGet('examples/menu_altered_path'); + + + } +}