Location: PHPKode > projects > Processed Book Open Source > pbos-1.01/books.php
<?php
/**
 * Add a new book or edit an existing book's content.
 *
 * Copyright (C) 2005 Wayne Davison <hide@address.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

require 'lib/main.inc';
require 'lib/wiki.inc';

main_page();

function main_page()
{
    global $uID;

    open_db();

    list($uID, $scrname, $can_add) = get_auth_cookie();

    add_sidebar_divider();
    add_sidebar_image('btn-library', '.');
    if ($uID)
	add_sidebar_image('btn-bookmarks', 'bkmk.php');
    sidebar_signin($uID, $scrname);

    if (!$uID) {
	start_content('Error', 'wide');
	echo <<<EOT
You must be signed in to edit/add a book.
<p>If you already have an account, please <a href="signin.php">sign in</a>.
<p>Otherwise, either <a href="signin.php?do=signup">sign up</a> or press the "Back"
button on your browser.
EOT;
    } else {
	$book_num = $_GET['b'] + 0;

	if ($book_num)
	    edit_book($book_num);
	else
	    add_book($can_add);
    }

    close_db();
    end_content();
}

function add_book($can_add)
{
    global $uID, $heading, $book_btn;

    $heading = 'Adding a book';
    start_content($heading, 'wide');
    $book_btn = 'Create book';
    if (!$can_add) {
	echo <<<EOT
You do not have the necessary privileges to add new content.
<p> Please press the "Back" button on your browser.
EOT;
	return;
    }

    if ($_POST['bookdata'])
	save_book_data(0, $uID);
    else
	edit_book_data(0);
}

function edit_book($book_num)
{
    global $uID, $heading, $book_btn, $upload, $sect_html;
    global $btitle, $author, $description, $copyright;

    $do = "SELECT UserID, GroupID, Title, Author, Description, Copyright, Access, SectionCount
	FROM Books
	WHERE BookID = $book_num";
    $result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
    list($book_user, $book_group, $btitle, $author, $description, $copyright, $access, $sect_cnt) = mysql_fetch_row($result);
    $heading = "Editing \"$btitle\"";
    start_content($heading, 'wide');
    $book_btn = 'Save changes';
    if ($book_user != $uID && $uID != 1) {
	echo <<<EOT
You do not have the necessary privileges to edit that book.
<p> Please press the "Back" button on your browser.
EOT;
	return;
    }

    if (preg_match('/delete/i', $_POST['sub'])) {
	echo 'Deleting...';
	delete_book($book_num, $book_group);
	echo "done!\n<META HTTP-EQUIV=Refresh CONTENT='0; URL=.'>";
	return;
    }

    $sect_html = '<p><input type=submit name=sub value="Add A New Section">';
    if ($sect_cnt) {
	$sect_html .= '<p><select name=sect>'
		   . '<option value="">-- Select a section to edit --';
	$do = "SELECT SectionID, Title
	    FROM BookSections
	    WHERE BookID = $book_num
	    ORDER BY SectionID";
	$result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
	while (($row = mysql_fetch_row($result)) !== FALSE) {
	    $j = $row[0];
	    $sect_html .= "<option value=$j>Edit section $j) " . $row[1];
	}
	$sect_html .= '</select><br><input type=submit name=sub value="Edit The Section">';
    }

    $upload = <<<EOT
</tr><tr valign=top>
<th>Upload an image file:</th>
<td><input type=hidden name=MAX_FILE_SIZE value=256000>
<input type=file name=newimage size=50></td>
EOT;
    if ($uID == 1) {
	$do = "SELECT UserID, Name
	    FROM Users
	    ORDER BY Name";
	$result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
	while (($row = mysql_fetch_row($result)) !== FALSE) {
	    $sel = preg_match("/(^|,){$row[0]}(,|$)/", $access) ? ' SELECTED' : '';
	    $us_sel .= "<option value={$row[0]}$sel>{$row[1]}\n";
	}
	$sel = preg_match("/(^|,)0(,|$)/", $access) ? ' SELECTED' : '';
	$upload .= <<<EOT
</tr><tr valign=top>
<th>Can Read:</th>
<td><select name="access[]" size=4 multiple>
<option value=0$sel>Everyone
$us_sel</select></td>
EOT;
	$us_sel = preg_replace(array('/ SELECTED/', "/(=$book_user)>/"), array('', '$1 SELECTED>'), $us_sel);
	$upload .= <<<EOT
</tr><tr valign=top>
<th>Can Edit:</th>
<td><select name=editor>
$us_sel</select></td>
EOT;
    }

    if ($_POST['bookdata'])
	save_book_data($book_num, $book_user);
    elseif ($_POST['sectdata'] && $_POST['format'] == 0)
	save_sect_data($book_num);
    elseif ($_GET['do'])
	edit_sect_data($book_num);
    else
	edit_book_data($book_num);
}

function edit_book_data($book_num)
{
    global $heading, $book_btn, $upload, $sect_html;
    global $btitle, $author, $description, $copyright;

    echo <<<EOT
<h2 style='margin:0'>$heading</h2>
<script>
 function presubmit()
 {
  var form = document.bookform;
  for (var j = 0; j < 4; j++) {
   if (form[j].value.match(/^\s*$/)) {
    alert('You did not fill-in a required field: ' + form[j].name);
    return false;
   }
  }
  return true;
 }
</script>
<form name=bookform method=post onsubmit="return presubmit()"
 enctype="multipart/form-data">
<table align=center><tr valign=top>
<th align=right>Title:</th>
<td><input type=text name=title SIZE=60 value="$btitle"></td>
</tr><tr valign=top>
<th align=right>Author:</th>
<td><input type=text name=author SIZE=60 value="$author"></td>
</tr><tr valign=top>
<th align=right>Copyright:</th>
<td><input type=text name=copyright SIZE=60 value="$copyright"></td>
</tr><tr valign=top>
<th align=right>Description:</th>
<td><textarea name=description ROWS=8 COLS=67>$description</textarea></td>
$upload
</tr><tr valign=top>
<td colspan=2 align=center><input type=submit name=sub value='$book_btn'>
$sect_html</td>
</tr></table>
<input type=hidden name=bookdata value=1>
</form>
EOT;

    $images = array();
    if ($book_num) {
	$dh = @opendir("book-images/$book_num");
	if (!$dh) {
	    mkdir("book-images/$book_num", 0775);
	    $dh = opendir("book-images/$book_num");
	}
	if ($dh) {
	    while ($file = readdir($dh)) {
		if (preg_match('/^\./', $file))
		    continue;
		$images[] = $file;
	    }
	    closedir($dh);
	}
    }
    if ($images) {
	echo "<p>The following images have already been uploaded for this book:<ul>\n",
	    '<li>', join("</li>\n<li>", $images), "</li></ul>\n";
    }

    if ($sect_html != '') {
	echo <<<EOT
<hr>
<script>
 function double_confirm_delete()
 {
  if (confirm('First confirmation:  delete this book?')
   && confirm('Second confirmation:  are you certain?'))
    return true;
  return false;
 }
</script>
<form method=post onsubmit="return double_confirm_delete()">
<table border=1 cellpadding=20 align=center><tr><td>
<center><input type=submit name=sub value="Delete This Book"></center>
<b>Caution:</b> This will delete all the sections of this book, all associated<br>
Annotations, any uploaded images, and the above info.
</td></tr></table></form>
EOT;
    }
}

function save_book_data($book_num, $book_user)
{
    global $uID, $btitle, $author, $description, $copyright;

    $btitle = preg_replace('/</', '&lt;', $_POST['title']);
    $author = preg_replace('/</', '&lt;', $_POST['author']);
    $description = preg_replace('/</', '&lt;', $_POST['description']);
    $copyright = preg_replace('/</', '&lt;', $_POST['copyright']);
    $editor = $uID == 1 && $_POST['editor']+0 ? $_POST['editor']+0 : $book_user;

    if ($uID == 1 && is_array($_POST['access'])) {
	$access = join(',', $_POST['access']);
	if (!preg_match('/^\d+(,\d+)*$/', $access)) {
	    echo 'Invalid request';
	    return;
	}
	$access = ", Access = '" . mysql_real_escape_string($access) ."'";
    } else
	$access = '';

    if ($book_num) {
	$type = 'UPDATE';
	$where = "WHERE BookID = $book_num AND UserID = $book_user";
    } else
	$type = 'INSERT INTO';
    $do = "$type Books
	SET UserID = $editor, Title = '" . mysql_real_escape_string($btitle) . "',
	    Author = '" . mysql_real_escape_string($author) ."',
	    Description = '" . mysql_real_escape_string($description) ."',
	    Copyright = '" . mysql_real_escape_string($copyright) ."'$access
	$where";
    if (!mysql_query($do)) {
	if (preg_match('/Duplicate entry/i', mysql_error())) {
	    echo "That book title already exists.<p>Please press the back button.";
	    return;
	}
	die("$type failed: " . mysql_error() . " (cmd: $do)");
    }

    if (!$book_num) {
	$do = 'SELECT LAST_INSERT_ID()';
	$result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
	list($book_num) = mysql_fetch_row($result);

	setup_a_book($book_num, $btitle, $editor);
    }

    if (is_uploaded_file($_FILES['newimage']['tmp_name'])) {
	if (!preg_match(':^image/:', $_FILES['newimage']['type']))
	    $err = 'You must upload an image file.';
	elseif ($_FILES['newimage']['size'] <= 0)
	    $err = 'That image is empty.';
	elseif ($_FILES['newimage']['size'] > 256000)
	    $err = 'That image is too large.';
	elseif (!preg_match('/\.(png|jpg|jpeg|gif)$/i', $_FILES['newimage']['name']))
	    $err = 'The filename does not end with a suffix of .png, .jpg, .jpeg, or .gif.';
	else {
	    $fn = preg_replace('/[^-a-z0-9+.]+/i', '_', $_FILES['newimage']['name']);
	    $fn = "./book-images/$book_num/$fn";
	    move_uploaded_file($_FILES['newimage']['tmp_name'], $fn);
	    chmod($fn, 0664);
	    $err = '';
	}
	if ($err)
	    echo "<p><b style='color:red'>", $err, "</b>\n";
	else {
	    echo "<p><b>New image uploaded.</b>\n";
	    $err = 'none';
	}
    }

    if ($err == '') {
	if ($_POST['sect'] != '') {
	    $book_sect = $_POST['sect'] + 0;
	    echo "<META HTTP-EQUIV=Refresh CONTENT='0; URL=?do=edit&b=$book_num&s=$book_sect'>";
	} elseif (preg_match('/create|section/i', $_POST['sub']))
	    echo "<META HTTP-EQUIV=Refresh CONTENT='0; URL=?do=new&b=$book_num'>";
	else
	    redir_main();
    } else
	edit_book_data($book_num);
}

function edit_sect_data($book_num)
{
    if ($_GET['do'] == 'new') {
	$book_sect = 32767;
	$stitle = $text = '';
	$hidden = "<input type=hidden name=newsect value=1>\n";
	$split = "\n<input type=checkbox name=split id=split value=1><label for=split>\n"
	       . "Split text into multiple sections at each \"== section heading ==\"</label><br>";
	$btn = 'Save As New Section';
    } else {
	$book_sect = $_GET['s'] + 0;
	$do = "SELECT Title, Text
	    FROM BookSections
	    WHERE BookID = $book_num AND SectionID = $book_sect
	    LIMIT 1";
	$result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
	list($stitle, $text) = mysql_fetch_row($result);
	$btn = 'Save Section';
    }

    if (is_uploaded_file($_FILES['upload']['tmp_name'])) {
	if (!preg_match(':^text/:i', $_FILES['upload']['type']))
	    $err = 'You must upload a text file (either text/plain or text/html), not ' . $_FILES['upload']['type'] . '.';
	elseif ($_FILES['upload']['size'] <= 0)
	    $err = 'The uploaded file was empty.';
	elseif ($_FILES['upload']['size'] > 2000000)
	    $err = 'That section is too large.  Please break it up.';
	else {
	    $text = file_get_contents($_FILES['upload']['tmp_name']);
	    if ($text === FALSE)
		$err = "Failed to get the file's contents.";
	    else {
		$err = '';
		$did_upload = 1;
		if (preg_match(':^text/html:i', $_FILES['upload']['type']))
		    $html_selected = ' SELECTED';
	    }
	}
	if ($err)
	    echo "<p><b style='color:red'>", $err, "</b>\n";
    } elseif ($_POST['sectdata']) {
	switch ($_POST['format']) {
	case 1:
	    $text = text2wiki($_POST['text']);
	    break;
	case 2:
	    $text = html2wiki($_POST['text']);
	    break;
	}
	$stitle = preg_replace('/</', '&lt;', $_POST['stitle']);
    }

    if ($stitle == '')
	$stitle = find_section_title($text);
    $stitle = trim(external_wiki_string($stitle, 1));

    echo <<<EOT
<script>
 function presubmit()
 {
  var form = document.sectform;
  if (form.text.value.match(/^\s*$/)) {
   alert('You did not enter any text in the section.');
   return false;
  }
  if (form.format.options[0].selected) {
   section_text_changed();
   if (form.stitle.value.match(/^\s*$/)) {
    alert("You did not enter the section's title.");
    return false;
   }
   if (!form.text.value.match(/<>/)) {
    if (confirm('May I mark the sentences?'))
     mark_sentences();
   }
  }
  return true;
 }
</script>
<table width=600><tr><td>
<form name=sectform method=post onsubmit="return presubmit()"
 enctype="multipart/form-data">
Enter the section's text using <a href="help-wiki.html" target="_blank">our
wiki-style markup</a>, HTML, or plain text.<br>
(Each section must be no larger than 256 KB.)
<div><script>output_toolbar('text','&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;')</script>
Markup: <select name=format onchange="textarea_format_change('$btn')">
</div>
<option value=0>Wiki text<option value=1>plain text<option value=2$html_selected>HTML text</select><br>
<textarea name=text rows=24 cols=70 onchange="section_text_changed()">
EOT;
    output_external_wiki($text, $did_upload ? 0 : $book_num, $book_sect);
    echo <<<EOT
</textarea><br>$split
Section title: <input type=text name=stitle size=60 value="$stitle"><br>
<input type=submit name=sub value='$btn'> &nbsp;
<input type=button value="Cancel" onclick="window.location = '?b=$book_num'"> &nbsp;
<input type=button name=mark value="Mark Sentences" onclick="mark_sentences()"><br>
If you enter HTML or plain-text, your input will first be converted into our internal
Wiki-like text format so that you may edit it for accuracy and mark the sentences (which
makes sentences display in a more book-like manner, and makes the book dissection able
to find proper sentence breaks).
$hidden<input type=hidden name=sectdata value=1>
</form>
<hr>
<form name=uploadform method=post enctype="multipart/form-data">
If you wish to upload this section's text from a file, enter its name here:<br>
<input type=hidden name=MAX_FILE_SIZE value=2000000>
<input type=file name=upload size=50> &nbsp;
<input type=submit value=Upload>
<br>Doing an upload of a file will replace any text in the box above.
You may upload up to 2 MB of data at a time (but this does not supersede
the per-section limit mentioned above for each finished section).
$hidden</form>
<script>textarea_format_change('$btn')</script>
</td></tr></table>
EOT;
}

function save_sect_data($book_num)
{
    if ($_POST['newsect']) {
	$do = "SELECT SectionCount
	    FROM Books
	    WHERE BookID = $book_num";
	$result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
	list($book_sect) = mysql_fetch_row($result);
    } else {
	$book_sect = $_GET['s'] + 0;
	$do = "SELECT Text
	    FROM BookSections
	    WHERE BookID = $book_num AND SectionID = $book_sect
	    LIMIT 1";
	$result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
	list($oldtext) = mysql_fetch_row($result);
    }

    $text = $_POST['text'];
    $stitle = preg_replace('/</', '&lt;', $_POST['stitle']);

    if ($_POST['split'] && $_POST['newsect']) {
	preg_match_all('/^==[^=].*?[^=]==\r?$/m', $text, $sects, PREG_OFFSET_CAPTURE);
	if (count($sects[0]) > 1) {
	    $offset = 0;
	    each($sects[0]); // Just dump the first offset.
	    while (list(,$match) = each($sects[0])) {
		list(,$end_offset) = $match;
		save_a_section(substr($text, $offset, $end_offset - $offset),
			       $stitle, $book_num, $book_sect, 1);
		$stitle = '';
		$offset = $end_offset;
		$book_sect++;
	    }
	    $text = substr($text, $offset);
	    $stitle = find_section_title($text);
	}
    }

    save_a_section($text, $stitle, $book_num, $book_sect, $_POST['newsect']);

    echo "<META HTTP-EQUIV=Refresh CONTENT='0; URL=?b=$book_num'>";
}

function save_a_section($text, $stitle, $book_num, $book_sect, $is_new)
{
    list($text, $para_count, $sntc_count, $word_count) = to_internal_wiki($text, $book_num, $book_sect);
    if ($stitle == '')
	$stitle = find_section_title($text);

    echo "<p>Saving section $book_sect) \"$stitle\"...<p>Sentences: $sntc_count<br>Paragraphs: $para_count<br>Words: $word_count";

    $stitle = trim(internal_wiki_string($stitle, 0));
    $text = mysql_real_escape_string(preg_replace('/\s*\z/', "\n", $text, 1));
    $stitle = mysql_real_escape_string($stitle);

    if (strlen($text) > 256000)
	die("Section size is larger than 256000 characters.  Please press 'Back' and shorten it.");

    if ($is_new) {
	$do = "INSERT INTO BookSections
	    SET BookID = $book_num, SectionID = $book_sect, Text = '$text', Title = '$stitle',
		WordCount = $word_count, SentenceCount = $sntc_count, ParagraphCount = $para_count";
	mysql_query($do) or die('INSERT failed: ' . mysql_error() . " (cmd: $do)");
	$do = "UPDATE Books SET SectionCount = SectionCount + 1 WHERE BookID = $book_num";
	mysql_query($do) or die('UPDATE failed: ' . mysql_error() . " (cmd: $do)");
    } else {
	$do = "UPDATE BookSections
	    SET Text = '$text', Offsets = NULL, Title = '$stitle',
		WordCount = $word_count, SentenceCount = $sntc_count, ParagraphCount = $para_count
	    WHERE BookID = $book_num AND SectionID = $book_sect";
	mysql_query($do) or die('REPLACE failed: ' . mysql_error() . " (cmd: $do)");
    }
}

function delete_book($book_num, $book_group)
{
    $dir = "book-images/$book_num";
    if (($dh = opendir($dir)) !== FALSE) {
	while ($file = readdir($dh)) {
	    if (preg_match('/^\.\.?$/', $file))
		continue;
	    unlink("$dir/$file");
	}
	closedir($dh);
	rmdir($dir);
    }
    $do = "DELETE FROM EnabledGroups WHERE GroupID = $book_group";
    mysql_query($do) or die('DELETE failed: ' . mysql_error() . " (cmd: $do)");
    $do = "DELETE FROM Groups WHERE GroupID = $book_group";
    mysql_query($do) or die('DELETE failed: ' . mysql_error() . " (cmd: $do)");
    $do = "DELETE FROM BookAnnotations WHERE BookID = $book_num";
    mysql_query($do) or die('DELETE failed: ' . mysql_error() . " (cmd: $do)");
    $do = "DELETE FROM BookSections WHERE BookID = $book_num";
    mysql_query($do) or die('DELETE failed: ' . mysql_error() . " (cmd: $do)");
    $do = "DELETE FROM Books WHERE BookID = $book_num";
    mysql_query($do) or die('DELETE failed: ' . mysql_error() . " (cmd: $do)");
}

function find_section_title($text)
{
    if (preg_match('/\A(?:(?:\s*<[^>]*>)*\s*\n)?== (.*) ==\s*=== (.*) ===$/m', $text, $out))
	$stitle = $out[1] . ' -- ' . $out[2];
    else if (preg_match('/\A(?:\s*\n)?== (.*) ==$/m', $text, $out))
	$stitle = $out[1];
    else
	return '';
    return preg_replace(array('#\s*<br\s*/?\s*>\s*#', '#<.*?>#', '/</'), array(' ', '', '&lt;'), $stitle);
}

function setup_a_book($book_num, $btitle, $editor)
{
    mkdir("book-images/$book_num", 0775);

    $btitle = mysql_real_escape_string($btitle);
    $do = "INSERT INTO Groups
	SET GroupName = 'Book-Default#$book_num', BookDefault = $book_num,
	CanView = 0, CanAdd = $editor, CanCull = $editor, CanAdmin = $editor";

    mysql_query($do) or die('INSERT failed: ' . mysql_error() . " (cmd: $do)");

    $do = 'SELECT LAST_INSERT_ID()';
    $result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
    list($group_ID) = mysql_fetch_row($result);

    $do = "UPDATE Books
	SET GroupID = $group_ID
	WHERE BookID = $book_num";
    mysql_query($do) or die('UPDATE failed: ' . mysql_error() . " (cmd: $do)");

    $userIDs = array(0);
    $do = 'SELECT UserID FROM Users';
    $result = mysql_query($do) or die('SELECT failed: ' . mysql_error() . " (cmd: $do)");
    while (($row = mysql_fetch_row($result)) !== FALSE)
	$userIDs[] = $row[0];

    foreach ($userIDs as $id) {
	$do = "INSERT INTO EnabledGroups SET UserID = $id, GroupID = $group_ID";
	mysql_query($do) or die('INSERT failed: ' . mysql_error() . " (cmd: $do)");
    }
}
Return current item: Processed Book Open Source