epub.module in Epub 6
Same filename and directory in other branches
Provide ePub content type and enable the creation of ePub files from book contents.
File
epub.moduleView source
<?php
/**
* @file
* Provide ePub content type and enable the creation of ePub files
* from book contents.
*/
///////////////////////////////////// HOOKS ///////////////////////////////////
/**
* Implementation of hook_help().
*/
function epub_help($path, $arg) {
switch ($path) {
case 'admin/help#epub':
return '<p>' . t('The ePub module allows administrators to associate ePub content with book outlines.') . '</p><p>' . t('By enabling this module, you will allow the user to download the ePub conversion of your book contents.<br/>') . t('EPub settings can be found at <a href="@epub_config_page">ePub Settings</a> page, while the administration panel is found at <a href="@epub_admin_page">ePub List</a> page.<br/>', array(
'@epub_config_page' => url('admin/content/epub/settings'),
'@epub_admin_page' => url('admin/content/epub'),
)) . t('In order to create or download an ePub, visit a book page where you can find the proper ePub tab in the menu.') . '</p>';
case 'admin/content/epub':
return '<p>' . t('The ePub module is suited for exporting book contents to ePub file format.') . '</p>';
}
}
/**
* Implementation of hook_perm().
*/
function epub_perm() {
return array(
'administer epub',
'access epub',
);
}
/**
* Implementation of hook_access().
*/
function epub_access($op, $node, $account) {
switch ($op) {
case 'create':
case 'delete':
case 'update':
return user_access('administer epub', $account);
case 'view':
return user_access('access epub', $account);
default:
return FALSE;
}
}
/**
* Implementation of hook_menu().
*/
function epub_menu() {
$items = array();
$items['epub/book_list_autocomplete'] = array(
'title' => 'Book List Autocomplete Callback',
'page callback' => 'epub_list_books_autocomplete',
'access arguments' => array(
'administer epub',
),
'type' => MENU_CALLBACK,
);
$items['admin/content/epub'] = array(
'title' => 'ePubs',
'description' => "Manage your site' s ePubs",
'page callback' => 'epub_admin_overview',
'access arguments' => array(
'administer epub',
),
'file' => 'epub.admin.inc',
);
$items['admin/content/epub/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/content/epub/settings'] = array(
'title' => 'Settings',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'epub_admin_settings',
),
'access arguments' => array(
'administer site configuration',
),
'type' => MENU_LOCAL_TASK,
'file' => 'epub.admin.inc',
);
$items['node/%/epub'] = array(
'title callback' => 'epub_read_title_callback',
'title arguments' => array(
1,
),
'description' => 'Read an ePub',
'page callback' => 'epub_read_page_callback',
'page arguments' => array(
1,
),
'access callback' => 'epub_read_access_callback',
'access arguments' => array(
1,
'access epub',
),
'type' => MENU_LOCAL_TASK,
'weight' => 10,
);
return $items;
}
/**
* Implementation of hook_node_info().
*/
function epub_node_info() {
return array(
'epub' => array(
'name' => t('ePub'),
'description' => 'An ePub is a special format used in ebook readers to render books',
'module' => 'epub',
'has_title' => TRUE,
'has_body' => TRUE,
'body_label' => t('Description'),
),
);
}
/**
* Implementation of hook_nodeapi().
*/
function epub_nodeapi(&$node, $op, $teaser, $page) {
switch ($op) {
case 'delete_revision':
db_query(db_rewrite_sql('DELETE FROM {epub} WHERE vid = %d'), $node->vid);
break;
}
}
/**
* Implementation of hook_form().
*/
function epub_form(&$node) {
$type = node_get_types('type', $node);
$form = array();
if ($type->has_title) {
$form['title'] = array(
'#type' => 'textfield',
'#title' => check_plain($type->title_label),
'#required' => TRUE,
'#default_value' => $node->title,
'#weight' => -5,
);
}
if ($type->has_body) {
$form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
}
_epub_add_form_elements($form, $node);
return $form;
}
/**
* Implementation of hook_insert().
*/
function epub_insert($node) {
$result = db_query_range(db_rewrite_sql("SELECT b.nid FROM {node} AS n\n INNER JOIN {book} AS b ON b.nid = n.nid\n WHERE b.nid = b.bid AND n.title = '%s'"), $node->book_outline, 0, 1);
$book = db_fetch_object($result);
$node->bid = $book->bid;
var_dump($node);
exit;
/*
if (!drupal_write_record('epub', $node)) {
node_delete($node->nid);
drupal_set_message(t('Error occurred during the creation of the ePub.'), 'error');
//drupal_goto('node/add/epub');
}*/
}
/**
* Implementation of hook_update().
*/
function epub_update($node) {
if ($node->revision) {
epub_insert($node);
}
else {
$result = db_query_range(db_rewrite_sql("SELECT b.nid FROM {node} AS n\n INNER JOIN {book} AS b ON b.nid = n.nid\n WHERE b.nid = b.bid AND n.title = '%s'"), $node->book_outline, 0, 1);
$book = db_fetch_object($result);
$result = db_query(db_rewrite_sql("UPDATE {epub} SET\n bid = %d,\n author_name = '%s',\n language_code = '%s',\n identifier = '%s',\n identifier_type = '%s',\n publisher_name = '%s',\n publisher_site = '%s',\n creation_date = '%s',\n rights = '%s',\n source_url = '%s'\n WHERE vid = %d"), $book->nid, $node->author_name, $node->language_code, $node->identifier, $node->identifier_type, $node->publisher_name, $node->publisher_site, $node->creation_date, $node->rights, $node->source_url, $node->vid);
if (!$result) {
drupal_set_message(t('Error occurred while updating the ePub.'), 'error');
//drupal_goto("node/$node->vid/epub");
}
}
}
/**
* Implementation of hook_delete().
*/
function epub_delete($node) {
$result = db_query(db_rewrite_sql('DELETE FROM {epub} WHERE nid = %d'), $node->nid);
// node_delete($node->nid);
if (!$result) {
drupal_set_message(t('Error occurred while removing the ePub.'), 'error');
}
//drupal_goto('admin/content/epub');
}
/**
* Implementation of hook_load().
*/
function epub_load($node) {
return db_fetch_object(db_query(db_rewrite_sql('SELECT n.title AS book_outline, e.bid, e.author_name, e.language_code, e.identifier, e.identifier_type, e.publisher_name,
e.publisher_site, e.creation_date, e.rights, e.source_url
FROM {epub} AS e
LEFT JOIN {node} AS n ON e.bid = n.nid
WHERE e.vid = %d'), $node->vid));
}
/**
* Implementation of hook_view().
*/
function epub_view($node, $teaser = FALSE, $page = FALSE) {
$node = node_prepare($node, $teaser);
$node->content['epub'] = array(
'#value' => theme('epub_info', l($node->book_outline, 'node/' . $node->bid), check_plain($node->author_name), check_plain($node->language_code), check_plain($node->identifier), check_plain($node->identifier_type), check_plain($node->creation_date), check_plain($node->publisher_name), l($node->publisher_site, $node->publisher_site), check_plain($node->rights), l($node->source_url, $node->source_url)),
'#weight' => 1,
);
return $node;
}
/**
* Implementation of hook_theme().
*/
function epub_theme() {
return array(
'epub_info' => array(
'template' => 'epub_info',
'arguments' => array(
'book_outline' => NULL,
'author_name' => NULL,
'language_code' => NULL,
'identifier' => NULL,
'identifier_type' => NULL,
'creation_date' => NULL,
'publisher_name' => NULL,
'publisher_site' => NULL,
'rights' => NULL,
'source_url' => NULL,
),
),
);
}
/**
* Implementation of hook_autocomplete().
*/
function epub_list_books_autocomplete($arg) {
$matches = array();
$result = db_query_range(db_rewrite_sql("SELECT n.title FROM {node} AS n\n INNER JOIN {book} AS b ON b.nid = n.nid\n WHERE b.nid = b.bid AND n.title LIKE '%%%s%%'"), $arg, 0, 10);
while ($row = db_fetch_object($result)) {
$matches[$row->title] = check_plain($row->title);
}
print drupal_to_js($matches);
}
/**
* Implementation of hook_views_api().
*/
function epub_views_api() {
return array(
'api' => 2.0,
);
}
//////////////////////////////// MENU CALLBACKS ///////////////////////////////
/**
* Determine correct menu title
*
* @param $nid
* An integer containing node id.
*
* @return
* A string containing the title for menu tab of a book node.
*/
function epub_read_title_callback($nid) {
$node = node_load($nid);
if ($node->type === 'book') {
return _epub_links_book($nid) ? t('Download ePub') : t('Add ePub');
}
elseif ($node->type === 'epub') {
return _epub_links_book($node->bid) ? t('Download ePub') : t('Add ePub');
}
}
/**
* Determine whether the user has to create the ePub or can download it.
*
* @param $nid
* An integer containing node id.
*
* @return
* If the ePub exists, it send the corresponding file to the user
* else it redirects to the 'add ePub' page.
*/
function epub_read_page_callback($nid) {
$node = node_load($nid);
if ($node->type === 'book') {
_epub_links_book($nid) ? epub_create_file($nid) : drupal_goto('node/add/epub');
}
elseif ($node->type === 'epub') {
if (_epub_links_book($node->bid)) {
epub_create_file($node->bid);
}
else {
drupal_set_message(t('You have to link a book outline before you can download its ePub'), 'warning');
drupal_goto("node/{$nid}/epub");
}
}
}
/**
* Determine whether the user can download an ePub or not.
*
* @param $nid
* An integer containing node id.
* @param $perm
* The permission, such as "administer nodes", being checked for.
*
* @return
* A boolean value telling if the user has the rights to access the content.
*/
function epub_read_access_callback($nid, $perm) {
$node = node_load(array(
'nid' => $nid,
));
return ($node->type === 'book' || $node->type === 'epub' && _epub_links_book($node->bid)) && user_access($perm);
}
/////////////////////////////////// EPUB API //////////////////////////////////
/**
* Generate an ePub file from a book.
*
* @param $nid
* An integer containing book node id.
*
* @return
* If a book structure exists, send the ePub file to the user
* else set an error message and redirect to the node page.
*/
function epub_create_file($nid) {
$epub_library_path = 'sites/all/libraries/epub';
require_once "{$epub_library_path}/EPubChapterSplitter.php";
require_once "{$epub_library_path}/EPub.php";
$epub_nid = _epub_links_book($nid);
if (!$epub_nid) {
drupal_set_message(t('Error during the creation of the ePub'), 'error');
drupal_goto("node/{$nid}");
}
$epub_node = node_load(array(
'nid' => $epub_nid,
));
$book_node = node_load(array(
'nid' => $nid,
));
$book_tree = _epub_get_book_tree($book_node);
if (empty($book_tree)) {
drupal_set_message(t('Error during the creation of the ePub'), 'error');
drupal_goto("node/{$nid}");
}
$epub = new EPub();
// Title and Identifier are mandatory!
$epub
->setTitle($epub_node->title);
// Could also be the ISBN, ISSN, UUID or others.
$epub
->setIdentifier($epub_node->identifier ? $epub_node->identifier : "1234", "URI");
// Language is mandatory, but EPub defaults to "en".
//Use RFC3066 Language codes, such as "en", "da", "fr" etc.
$epub
->setLanguage($epub_node->language_code);
$epub
->setDescription($epub_node->body);
$epub
->setAuthor($epub_node->author_name, $epub_node->author_name);
$epub
->setPublisher($epub_node->publisher_name, $epub_node->publisher_site);
// Strictly not needed as the book date defaults to time().
$epub
->setDate(date('U', $epub_node->creation_date));
// As this is generated, this _could_ contain the name or licence information
// of the user who purchased the book, if needed. If this is used that way,
// the identifier must also be made unique for the book.
$epub
->setRights($epub_node->rights);
$epub
->setSourceURL($epub_node->source_url);
_epub_traverse_book_tree($book_tree, $epub);
$epub
->setIgnoreEmptyBuffer(true);
$epub
->finalize();
// Finalize the book, and build the archive.
$data = $epub
->getBook();
$headers = array(
'Pragma: public',
'Last-Modified: ' . date('r', time()),
'Expires: 0',
'Accept-Ranges: bytes',
'Connection: close',
'Content-Type: application/epub+zip',
'Content-Disposition: attachment; filename="' . $epub_node->title . '.epub";',
'Content-Transfer-Encoding: binary',
'Content-Length: "' . drupal_strlen($data),
);
_epub_file_transfer($data, $headers);
}
/////////////////////////////////// HELPERS ///////////////////////////////////
/**
* Check if ePub links a book
*
* @param $nid
* An integer containing node id.
*
* @return
* On success, the ePub nid;
* On failure, FALSE.
*/
function _epub_links_book($nid) {
$book = db_fetch_object(db_query(db_rewrite_sql("SELECT nid FROM {epub} WHERE bid = %d"), $nid));
return $book->nid ? $book->nid : FALSE;
}
/**
* Build the common elements of the epub form for the node forms.
*
* @param &$form
* The form structure to be altered
* @param $node
* The node containing ePub information
*/
function _epub_add_form_elements(&$form, $node) {
$form['epub'] = array(
'#type' => 'fieldset',
'#title' => t('ePub settings'),
'#weight' => 5,
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['epub']['book_outline'] = array(
'#type' => 'textfield',
'#title' => t('Book'),
'#description' => t("ePub file' s cover page, root of the book."),
'#required' => TRUE,
'#autocomplete_path' => 'epub/book_list_autocomplete',
'#default_value' => $node->book_outline,
);
$form['epub']['author_name'] = array(
'#type' => 'textfield',
'#title' => t('Author'),
'#description' => t('Who wrote this book'),
'#required' => FALSE,
'#default_value' => $node->author_name,
);
$form['epub']['language_code'] = array(
'#type' => 'iso_639',
'#title' => t('Language'),
'#description' => t('RFC3066 Language codes, such as "en", "da", "fr", ...'),
'#required' => TRUE,
'#default_value' => $node->language_code ? $node->language_code : 'en',
);
$form['epub']['identifier'] = array(
'#type' => 'textfield',
'#title' => t('Identifier'),
'#description' => t("The ePub' s ISBN number, preferred for published books, or a UUID."),
'#required' => FALSE,
'#default_value' => $node->identifier,
);
$form['epub']['identifier_type'] = array(
'#type' => 'textfield',
'#title' => t('Identifier Type'),
'#description' => t("The ePub' s identifier type, ie: ISBN, ISSN, UUID, ..."),
'#required' => FALSE,
'#default_value' => $node->identifier_type,
);
$form['epub']['publisher_name'] = array(
'#type' => 'textfield',
'#title' => t("Publisher' s Name"),
'#description' => t("Publisher' s name."),
'#required' => FALSE,
'#default_value' => $node->publisher_name,
);
$form['epub']['publisher_site'] = array(
'#type' => 'textfield',
'#title' => t("Publisher' s Site"),
'#description' => t("Publisher' s site."),
'#required' => FALSE,
'#default_value' => $node->publisher_site,
);
$form['epub']['rights'] = array(
'#type' => 'textfield',
'#title' => t('Rights'),
'#description' => t('Copyright and license information specific for the ePub.'),
'#required' => FALSE,
'#default_value' => $node->rights,
);
$form['epub']['creation_date'] = array(
'#type' => 'textfield',
'#title' => t('Creation date'),
'#default_value' => date('Y-m-d H:i:s'),
'#description' => t('Creation date(YYYY-MM-DD HH:MM:SS format).'),
'#required' => FALSE,
'#default_value' => isset($node->creation_date) ? $node->creation_date : date('Y-m-d H:i:s'),
);
$form['epub']['source_url'] = array(
'#type' => 'textfield',
'#title' => t('Source URL'),
'#description' => t('URL of the site in which the ePub is available.'),
'#required' => FALSE,
'#default_value' => $node->source_url,
);
}
/**
* Return a structured book hierarchy.
*
* @param $node
* A node object.
* @param &$rows
* An array in which store the structured book data.
*
* @return
* An array containing the structured data of a book.
*/
function _epub_get_book_tree($node, &$rows = array()) {
$mlid = $node->book['mlid'];
$rows[$mlid]['book_page'] = $node;
$result = db_query(db_rewrite_sql("SELECT mlid FROM {menu_links} WHERE plid = %d"), $mlid);
while ($row = db_fetch_object($result)) {
$book = db_fetch_object(db_query(db_rewrite_sql("SELECT * FROM {book} WHERE mlid = %d"), $row->mlid));
_epub_get_book_tree(node_load(array(
'nid' => $book->nid,
)), $rows[$mlid]);
}
return $rows;
}
/**
* Append chapters to a book recursively.
*
* @param $node
* A node object.
* @param $book
* An EPub object.
*
* @return
* NULL.
*/
function _epub_traverse_book_tree($node, $book) {
foreach ($node as $key => $child_node) {
if ($key === 'book_page' && isset($node[$key])) {
if (count($node[$key]->body) > 250000) {
$splitter = new EPubChapterSplitter();
$splitter
->setSplitSize(variable_get('epub_custom_chapter_size', 250) * 1000);
$chapter = $splitter
->splitChapter($node[$key]->body);
}
else {
$chapter = $node[$key]->body;
}
$book
->addChapter($node[$key]->title, $node[$key]->title . '.html', $chapter);
}
else {
_epub_traverse_book_tree($child_node, $book);
}
}
}
/**
* Transfer ePub file to the user
*
* @param $data
* The ebook binary file, which basically is a zipped xml file.
* @param $headers
* An array containing http headers for the response.
*
* @return
* NULL.
*/
function _epub_file_transfer($data, $headers) {
if (ob_get_level()) {
ob_end_clean();
}
foreach ($headers as $header) {
// To prevent HTTP header injection, we delete new lines that are
// not followed by a space or a tab.
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
$header = preg_replace('/\\r?\\n(?!\\t| )/', '', $header);
drupal_set_header($header);
}
echo $data;
exit;
}
Functions
Name | Description |
---|---|
epub_access | Implementation of hook_access(). |
epub_create_file | Generate an ePub file from a book. |
epub_delete | Implementation of hook_delete(). |
epub_form | Implementation of hook_form(). |
epub_help | Implementation of hook_help(). |
epub_insert | Implementation of hook_insert(). |
epub_list_books_autocomplete | Implementation of hook_autocomplete(). |
epub_load | Implementation of hook_load(). |
epub_menu | Implementation of hook_menu(). |
epub_nodeapi | Implementation of hook_nodeapi(). |
epub_node_info | Implementation of hook_node_info(). |
epub_perm | Implementation of hook_perm(). |
epub_read_access_callback | Determine whether the user can download an ePub or not. |
epub_read_page_callback | Determine whether the user has to create the ePub or can download it. |
epub_read_title_callback | Determine correct menu title |
epub_theme | Implementation of hook_theme(). |
epub_update | Implementation of hook_update(). |
epub_view | Implementation of hook_view(). |
epub_views_api | Implementation of hook_views_api(). |
_epub_add_form_elements | Build the common elements of the epub form for the node forms. |
_epub_file_transfer | Transfer ePub file to the user |
_epub_get_book_tree | Return a structured book hierarchy. |
_epub_links_book | Check if ePub links a book |
_epub_traverse_book_tree | Append chapters to a book recursively. |