Nice job. Just tested this and looked at the code. This is very well put together! I also like how you made it configurable. Also, I tested and it works equally well in 2.0 and 2.1.
Your module is so nicely coded that I have one suggested optimization to make it even better. That would be to add more conditions before adding hooks. This is so that your hooks don't get added when they aren't applicable, like on the public side of the site, or when a user isn't logged in. Your module already accounts for that, but only after the hooks have already been put in place. Page::loaded can be an expensive hook (it gets called for every single page loaded in memory in the request, which could feasibly be hundreds of calls). So it's desirable to be selective about when you add a hook to it, as you can save some CPU cycles by not adding that hook when you don't need it.
The init() function can't tell anything about the current $page simply because modules are init'd before PW even tries to handle the page request. So a good strategy is to have your init() just determine if your hooks will possibly be useful to the current user. If it's a guest user, there's no point in even adding hooks. But if it's not a guest user, then your hooks may be useful, depending on what the current $page->process is. Since we can't tell anything about the $page yet from init(), just delay the decision till Page::render, like this:
<?php
public function init() {
// if there is no logged-in user, don't bother continuing
if($this->user->isGuest()) return;
// if the user is logged in, add a hook before Page::render
// which will determine whether to add more hooks
$this->addHookBefore('Page::render', $this, 'pageRender');
}
public function pageRender(HookEvent $event) {
// modules are init()'d before the page is loaded
// so we couldn't check the value of page->process from init()
// that's why we're doing it here instead.
// this checks to make sure that we are in the admin and on a Process we wish to hook
if(in_array((string) $this->page->process, array('ProcessPageEdit', 'ProcessTemplateEdit', 'ProcessField'))) {
// hook before a redirect occurs, so we can modify the redirect URL:
$this->session->addHookBefore('redirect', $this, 'sessionRedirect');
if($this->page->process == 'ProcessPageEdit') {
// hook after the page has been loaded and modify the breadcrumbs array
$this->session->addHookAfter('Page::loaded', $this, 'breadcrumbsEdit');
}
}
}