Custom blocks¶
Most blocks in this module extend \Drupal\o365\Block\O365BlockBase or
\Drupal\o365\Block\O365UncachedBlockBase. Use them for your own blocks too. They
save you the constructor boilerplate and, more importantly, they get the access
check right.
What the base class does for you¶
O365BlockBase injects the Graph service and exposes it as $this->graphService,
so you do not have to write a create() method.
It also overrides access(). The block is forbidden for anonymous users, and
forbidden for authenticated users who are not linked to a Microsoft 365 account.
That second check is the one people forget when they roll their own: a local
editor account that never logged in through Microsoft has no token, and every
Graph call for that user comes back empty. Without the check you get a block full
of nothing instead of no block at all.
A block¶
<?php
namespace Drupal\my_module\Plugin\Block;
use Drupal\o365\Block\O365BlockBase;
/**
* Lists the Microsoft 365 groups of the current user.
*
* @Block(
* id = "my_module_my_groups",
* admin_label = @Translation("My Microsoft 365 groups"),
* )
*/
class MyGroupsBlock extends O365BlockBase {
/**
* {@inheritdoc}
*/
public function build() {
$response = $this->graphService->getGraphData('/me/memberOf?$select=id,displayName');
$items = [];
foreach ($response['value'] ?? [] as $group) {
$items[] = $group['displayName'];
}
return [
'#theme' => 'item_list',
'#items' => $items,
'#cache' => [
'contexts' => ['user'],
'max-age' => 900,
],
];
}
}
Caching¶
This is the part that bites.
The base class adds no cache context
A block that renders per user data will happily be cached and served to the
next visitor. Either set 'contexts' => ['user'] on the render array
yourself, as above, or extend O365UncachedBlockBase instead.
O365UncachedBlockBase is O365BlockBase plus UncacheableDependencyTrait. It
makes the block uncacheable, full stop, which means a Graph call on every request
that renders it. That is the right answer for something like presence, which
changes by the minute. It is the wrong answer for a list of groups, which changes
twice a year. Pick deliberately; an uncacheable block in a sidebar on every page
is a noticeable amount of traffic to Microsoft.
Access on top of the base check¶
If your block needs its own condition, call the parent and combine the results rather than replacing them:
public function access(AccountInterface $account, $return_as_object = FALSE) {
$access = parent::access($account, TRUE)
->andIf(AccessResult::allowedIfHasPermission($account, 'view my groups block'));
return $return_as_object ? $access : $access->isAllowed();
}