diff --git a/notifications_book/notifications_book.info b/notifications_book/notifications_book.info
new file mode 100644
index 0000000..cfa92f0
--- /dev/null
+++ b/notifications_book/notifications_book.info
@@ -0,0 +1,7 @@
+name = "Book subscriptions"
+description = "Allows subscriptions to a whole book."
+package = Notifications
+dependencies[] = notifications
+dependencies[] = notifications_content
+dependencies[] = book
+core = 6.x
diff --git a/notifications_book/notifications_book.module b/notifications_book/notifications_book.module
new file mode 100644
index 0000000..10918ca
--- /dev/null
+++ b/notifications_book/notifications_book.module
@@ -0,0 +1,142 @@
+<?php
+/**
+ * @file notifications_book.module
+ * Notifications for books.
+ */
+
+/**
+ * Implementation of hook_help().
+ */
+function notifications_book_help($path, $arg) {
+  switch ($path) {
+    case 'admin/help#notifications_book':
+      return t("The Book notifications module allows users to subscribe to a book as a whole. Subscribed users will receive notification of addition of new content to the book or updating of any of the book's content.");
+  }
+}
+
+/**
+ * Implementation of hook_perm().
+ */
+function notifications_book_perm() {
+  return array('subscribe to books');
+}
+
+/**
+ * Implementation of hook_notifications()
+ */
+function notifications_book_notifications($op, $event = NULL, $arg1 = NULL, $arg2 = NULL) {
+  switch ($op) {
+    case 'subscription types':
+      // Define a new subscription type for a book.
+      $types['book'] = array(
+        'event_type' => 'node',
+        'object_type' => 'node',
+        'title' => t('Book'),
+        'access' => 'subscribe to books',
+        'user page' => 'user/%user/notifications/book',
+        'fields' => array('bid'),
+        'description' => t('Subscribe to all content in a particular book.'),
+        // No idea what these do, and notifications_content.module doesn't even define its callbacks.
+        'page callback' => 'notifications_book_page_book',
+        'name callback' => 'notifications_book_subscription_name',
+      );
+      return $types;
+    case 'subscription fields':
+      // Information about available fields for subscriptions
+      $fields['bid'] = array(
+        'name' => t('Book'),
+        'field' => 'bid',
+        'type' => 'int',
+        'object_type' => 'node',
+        'options callback' => 'notifications_book_bid_options_callback',
+        'value callback' => 'notifications_book_bid_value_callback',
+      );
+      return $fields;
+  }
+}
+
+/**
+ * Wrapper for options callback based on subscription.
+ *
+ * @param $subscription
+ *   Optional subscription for which to find allowed books.
+ */
+function notifications_book_bid_options_callback($subscription = NULL) {
+  $options = array();
+  foreach (book_get_books() as $book) {
+    $options[$book['bid']] = $book['title'];
+  }
+  return $options;
+}
+
+/**
+ * Value callback for the bid subscription field.
+ *
+ * No idea why we have to implement this, as it just returns the value, but
+ * without it, notifications_field_real_value() fails for subscribing to
+ * books in the UI.
+ */
+function notifications_book_bid_value_callback($value) {
+  return $value;
+}
+
+/**
+ * Implementation of hook_notifications_object_OBJECT().
+ *
+ * Provide various information about particular nodes.
+ */
+function notifications_book_notifications_object_node($op, $node, $account = NULL) {
+  // We have to load the node ourselves because for unknown reasons the
+  // incoming $node has no book property.
+  $node = node_load($node->nid);
+  // Act only on book nodes.
+  if (!isset($node->book)) {
+    return;
+  }
+
+  switch ($op) {
+    case 'conditions':
+      return array(
+        'bid' => $node->book['bid'],
+      );
+    case 'subscriptions':
+      // Return available subscription options for this node.
+      $options = array();
+      // Check whether book subscriptions are available for this node type.
+      if (notifications_content_type_enabled($node->type, 'book')) {
+        $options[] = array(
+          'name' => t('This book'),
+          'type' => 'book',
+          'fields' => array('bid' => $node->book['bid']),
+        );
+      }
+      return $options;
+      break;
+  }
+}
+
+/**
+ * Implementation of hook_notifications_event().
+ *
+ * Act and do stuff with events.
+ *
+ * @param $op
+ *  One of:
+ *    - 'query':
+ *    - 'load':
+ *    - 'trigger': Invoked when an event is about to be triggered. The event
+ *      object may be modified.
+ *    - 'queued':
+ */
+function notifications_book_notifications_event($op, &$event, $account = NULL) {
+  if ($op == 'trigger') {
+    // A node event has been created and is about to be triggered.
+    // Add our data to it.
+    // We have to load the node ourselves because for unknown reasons the
+    // incoming $event->objects['node'] has no book property.
+    $node = node_load($event->params['nid']);
+    if (isset($node->book)) {
+      $event->params['bid'] = $node->book['bid'];
+    }
+  }
+}