<?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('/</', '<', $_POST['title']);
$author = preg_replace('/</', '<', $_POST['author']);
$description = preg_replace('/</', '<', $_POST['description']);
$copyright = preg_replace('/</', '<', $_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('/</', '<', $_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',' ')</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'>
<input type=button value="Cancel" onclick="window.location = '?b=$book_num'">
<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>
<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('/</', '<', $_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(' ', '', '<'), $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)");
}
}