Drupal Book Lending System

Joined: 12/16/2007

Hey everybody,
I am working on starting a book swapping system for my church. It would be like a 'church library', but instead of everybody donating books and leaving them at church, they just add them to the list and wait for requests.
This is so books don't have to be stored at the church, and we can tap into all the great resources of christian books stored on people bookshelves.
I'd like to do this in Drupal, and I have a fair idea on the basics of how to do it.
Some thing's I still need to work out are:
-> A module which could suck in book covers from Amazon or some other place so people can easily add book covers to the node when they add a book.
-> A way to create a link to a user's contact page with some text already in it (eg: "User1 wants to borrow Book2") or some other way notifications like this can be constructed

If anyone could help me with these two issues, that would be great!
Paul

Paul Vaartjes

Joined: 10/18/2008
Have you tried the amazon

Have you tried the amazon module?

You'd also need a way to mark books as "checked out/in"

/ * Begin Signature */
It's a strange thing about determined seekers-after-wisdom that, no matter where they happen to be, they'll always seek that wisdom which is a long way off. Wisdom is one of the few things that looks bigger the further away it is.

Joined: 12/16/2007
No, i haven't used amazon

No, i haven't used amazon module. have you had any experience with it and do you know if it will do that task well? I dont want it to be too complex.I thought the user could add a URL or a product code or something, and it'd grab the corresponding book cover.

For checked in/out i was thinking of just using a CCK check box which will allow me to change the display of the node based on if it is true or false (ie: display a different image in a box for checked in and out)

Paul Vaartjes

Joined: 10/18/2008
Humm, you might want to give

Humm, you might want to give the lender permission to set their own books as checked out. Might also be nice to allow reader comments/review on the books.

I used the d5 equivalent of the amazon module and it was pretty nice. d6 ought to be a lot better. The lender would just enter the isbn and the module would look up all the info.

You could probably have a bunch of fields (title, description, photo, etc) for the lender to fill in just in case the module can't find a specific book. If it can find it, then maybe use some js to hide those fields...?

Might also be nice to be able to flag certain books just in case someone sneaks in a cult book or something that will need to be removed from the list.

/ * Begin Signature */
It's a strange thing about determined seekers-after-wisdom that, no matter where they happen to be, they'll always seek that wisdom which is a long way off. Wisdom is one of the few things that looks bigger the further away it is.

Joined: 12/16/2007
- Yep sorry, i did mean a

- Yep sorry, i did mean a check box the lender could set (when i said "me" i mean I would code up the system to change colour/picture etc)
- Comments/Reviews is a great Idea. I hadn't thought of that. Will probably just do the same as on G&G and use 5-star module and let comments be reviews
- Another great idea with the javascript!
- I probably won't need flagging as we are a small church and I'll easily be able to manage it all, but if this grows, I will definetly keep that in mind.

Thanks heaps for all your help! I'm really excited about setting this up - i love using Drupal for a whole range of different things over a basic site.

I think the main part I still need to work out is the 2nd point in my original post: what is the best way for someone to request a book and the lender be easily notified?

Paul Vaartjes

Joined: 10/18/2008
Maybe a simple contact form?

Maybe a simple contact form? Name & Email then on submit, set the book as checked out.

With the Amazon module, you could also set up an affiliate account for your church in case someone wants to purchase the book.

/ * Begin Signature */
It's a strange thing about determined seekers-after-wisdom that, no matter where they happen to be, they'll always seek that wisdom which is a long way off. Wisdom is one of the few things that looks bigger the further away it is.

Joined: 12/16/2007
How would I set up the

How would I set up the contact form? and have it emailed to the lender?
(Sorry - haven't had much experience in that area)

Paul Vaartjes

Joined: 09/09/2008
Contact or webform

The contact module, it's part of core, is the simplest. Most of my sites use the sitewide contact form with "categories" (not to be confused with taxonomies) used to direct the email message to a particular individual or group. You could probably use the individual's (lender's) contact form; sorry, I don't have much experience with these.

A slightly more complex method, but probably still quite workable, would use webforms. You'd create fields for book title, etc., and still generate an email to the lender. One big advantage over the contact form is that the webform gets stored in the database and can be viewed or retrieved at a later time.

Hope this helps,
Curt

Joined: 12/16/2007
OK, I had considered the core

OK, I had considered the core contact module, but couldnt think of a way to actually make that tied to a specific book.
So with webform module, would it be able to automatically create a form per user (or per book) which emails to the lender as generated email with the fields in the form?

Paul Vaartjes

Joined: 12/16/2007
Actions

Is this something possible with the Actions module instead?

Paul Vaartjes

Joined: 09/09/2008
Some research is required

I suspect that you could use one webform and somehow prepopulate the email address (probably the userid of the lendor since you wouldn't want to expose email addresses) and the book title.

I've used webforms, but I haven't used an exactly analogous situation.

Perhaps someone else has more details or another approach.

Good luck,
Curt

Joined: 10/18/2008
You could use webform or you

You could use webform or you could just write it yourself. I wrote a simple one for one website (it was basically a brute force method, but it worked for what I wanted to do). I cut it down quite a bit and made some mod's so you can get the general idea (written for D5 and drupal_mail has changed in D6):

<?php
/********************/
/* Contact Form     */
/********************/
global $user;

// Use php's GET for the nid
$nid = $_GET['nid'];
if (
$nid > 0) {
 
// Get whatever node data you need
}

if (
$_POST[btnContact]) {
 
// Check if user is signed in
 
if (empty($user->name)){
     if (!
valid_email_address($_POST[mail])) {
       
form_set_error('mail', t('You must enter a valid e-mail address.'));
       
$seterror = 1;
     }
     if (empty(
$_POST[name])){
       
form_set_error('name', t('You must enter your name.'));
       
$seterror = 1;
     }
  }else{
    
$_POST[mail] = $user->mail;
    
$_POST[name] = $user->name
  }

  if (!
$seterror){
    
$headers["MIME-Version"] = '1.0';
    
$headers["Content-Transfer-Encoding"] = '8bit';
    
$headers["Content-Type"] = "text/html; charset='utf-8';";
 
    
$body .= "<p>" . check_plain($_POST[name]) . "(" . $_POST[mail] .
             
") would like to check out your book</p>";
 
    
drupal_mail("Comment Form", $book->owneremail,
                
$_POST[name] . " would like to check out your book.",
                
$body, $_POST[mail], $headers);
 
     print
"<p>Email sent!</p>";
  }
} else {
 
$_POST[name] = "";
 
$_POST[mail] = "";
 
$_POST[email_subject] = "";
 
$_POST[subject] = "";
 
$_POST[message] = "";
}
if (!
$_POST[btnContact] || ($_POST[btnContact] && $seterror)) {
?>

<form action="thispage&nid=<?php print $book->nid?>" method="post" id="contactbook-mail-page">

<div>
<?php if (!empty($user->name) && !empty($user->mail)): ?>
  <div class="form-item"><strong><?php print "From: <span style='position:relative; left:40px;'>" . $user->name . " (" . $user->mail . ")" ?></span></strong></div>
<? else: ?>
  <div class="form-item">
  <label for="edit-name">Your name: <span class="form-required" title="This field is required.">*</span></label>
  <input type="text" maxlength="255" name="name" id="edit-name"  size="30" value="<?php print $_POST[name]?>" style="position:relative; left:70px;" class="form-text required" />
  </div>
  <div class="form-item">
  <label for="edit-mail">Your e-mail address: <span class="form-required" title="This field is required.">*</span></label>
  <input type="text" maxlength="255" name="mail" id="edit-mail"  size="30" value="<?php print $_POST[mail]?>" class="form-text required" />
  </div>
<?php endif; ?>
<div class="form-item"><strong><?php print "To: <span style='position:relative; left:60px;'>" . $book->ownername ?></span></strong></div>
<div class="form-item">
<input type="submit" name="btnContact" value="Send e-mail" />
</div>

</form>

<?php } } ?>

You could probably make it even more simple depending on whether or not the checkee is required to be signed in (which would save you from having to validate the input and all you would need is a button to press for check out).

With this code, you can basically create a link or button on your book page that sends the book id (nid?) to a contact page where people enter their name & email to send the email.

Or you could have the name, email, and button on your book page and send it to a different page for validation.

But however you do it, somewhere in the process you can automatically mark the book as checked out.

/ * Begin Signature */
It's a strange thing about determined seekers-after-wisdom that, no matter where they happen to be, they'll always seek that wisdom which is a long way off. Wisdom is one of the few things that looks bigger the further away it is.

Joined: 12/16/2007
Thank you so much for this

Thank you so much for this code. It looks so helpful in simply doing what I want.
If you are able to, would you be able to guide me through making these alterations to the code:
- Basically just have a button ("Request") be displayed if the user is logged in.
- When pressed send the email automatically and change a cck field for available to FALSE (if this can be done)

Thank you so much for your help. Your help is enabling me to get this system running in a way it allows us to share our books in a really easy and simple way.

Paul Vaartjes

G&G Moderator
EdRoss's picture
Joined: 10/05/2006
Drupal Book Lending System

sejtraav, if you get this book lending system all set up and "sweet", as the G&G guys might say, have you considered making it a module and sharing it with others? There may be other communities that would benefit from the functionality.

Joined: 12/16/2007
Would love to.

I would love to put together something that would be able to be used by many churches and organisations.

The theory we are going with is to set it up online as we have, and have the church members just add books online instead of physically collecting them at the church.

I see this system as having many advantages for a small new library. First, people are more likely to lend their books if they are not giving them away forever. They can still keep them with them, and just lend them when requested. This system also keep all these good books in our houses, ready to be grabbed if needed, instead of sitting collecting dust in the church when not being read: we are not removing these resources from homes.
The system can also be stopped easily if for some reason it does not work out, and a physical location is not needed to keep the books.
Finally, every time some one hands there book over to someone else who has requested it, that is a time where discussion can begin about the book's contents. By having people giving each other the books instead of someone just grabbing it off a shelf, people are more likely to mention their thoughts to the lender/lendee, and start discussing the issues the book brings up..

So I think something like what I'm trying to set up would be something for other churches without a library system to think about setting up also. However, there are some technical issues with creating a drupal module for it.
The biggest issue is that I just don't really know how. At the moment its looking like the system will involve nodes, Views, CCK fields, and some of BishopBooyah's code, as well as css theming etc. I don't have enough experience with module making to know how to package this into a working and distributable little module.
The second issue I kind of already mentioned: the system involves a variety of different modules (at least at the moment) and i'm not sure if it is even possible to modularise that.

So I would love to help turn this into some kind of module. Maybe it could just be the custom codey kind of stuff, with instructions on how to set it up with the modules. Whatever it ends up being, I would probably need at least a little help in getting it done, so if anyone is willing, once I get it working for me, we can try getting it working for more poeple

Paul Vaartjes

G&G Moderator
EdRoss's picture
Joined: 10/05/2006
re: Drupal Book Lending System - scope creep

Also, have you thought about adding other "lendable" items, such as DVDs and CDs?

Joined: 10/18/2008
I wouldn't consider this

I wouldn't consider this module worthy, but it would work. You could probably create a module to do this, but this is just quick and dirty.

Create a contact page to send the email:

global $user;

// Use php's GET for the nid
$nid = $_GET['booknid'];
$booknode = node_load($nid);

$body .= "<p>" . $user->name . "(" . $user->mail .
        ") would like to check out your book</p>";
       
$headers = array(
  'MIME-Version'              => '1.0',
  'Content-Type'              => 'text/plain; charset=UTF-8; format=flowed; delsp=yes',
  'Content-Transfer-Encoding' => '8Bit',
  'X-Mailer'                  => 'Drupal'
);
$headers['From'] = $user->mail;

$message = array(
  'id'       => 'checkout_book',
  'to'       => $booknode->owneremail,
  'from'     => $user->name,
  'language' => user_preferred_language($user),
  'params'   => $user,
  'subject'  => $user->name . " would like to check out your book.",
  'body'     => $body,
);

$message['headers'] = $headers;

drupal_mail_send($message);
          
$booknode->field_checkedout[0]['value'] = 1;
node_save($booknode);

print "<p>Email sent!</p>";
print "<p><a href='bookpath/" . $booknode->nid . "'>Return to " . $booknode->title . "</a>";

Add a simple form to each book page:

<form action="checkoutpage&booknid=<?php print $book->nid?>" method="post" id="contactbook-mail-page">
<div>
<input type="hidden" name="booknid" value="<?php print $booknode->nid ?>" />
</div>
<div class="form-item">
<input type="submit" name="btnRequest" value="Request" />
</div>
</form>

You might have to do some debugging / experiment, but it should get it done for you.

/ * Begin Signature */
It's a strange thing about determined seekers-after-wisdom that, no matter where they happen to be, they'll always seek that wisdom which is a long way off. Wisdom is one of the few things that looks bigger the further away it is.

Joined: 12/16/2007
Sorry

Sorry to be tedious, but where should I put hte first block of code to "create the contact page"? The second block is then a reference/link to this contact page is it?

Paul Vaartjes

Joined: 10/18/2008
The first block of code could

The first block of code could just go in a page in php mode.

Then the second block could just be attached to the bottom of your book type (if you theme/template your book type that would be a good place for it).

The form from the book page would then go to the checkout page and then the magic happens.

/ * Begin Signature */
It's a strange thing about determined seekers-after-wisdom that, no matter where they happen to be, they'll always seek that wisdom which is a long way off. Wisdom is one of the few things that looks bigger the further away it is.

G&G Moderator
EdRoss's picture
Joined: 10/05/2006
extending book lending idea to others

If making this into a separate module isn't an option, what about adding the final method/code to the Tutorials section of this site, or maybe the Drupal Site Recipes or Drupal HowTo guides (not sure which is most appropriate)?

Joined: 12/16/2007
That sounds like a good idea

That sounds like a good idea also. I'll be sure to do that once everything is set up and running. Thanks for the suggestions Ed.
I think the system could easily be expanded to include DVDs and CDs, simply by adding some taxonomy, and maybe extra fields which could appear based on the selection of the taxonomy.

Paul Vaartjes

Joined: 12/16/2007
Coming along really well

This is coming along really well, thanks guys.

My next hurdle is with the node creation screen.
Do you know how I can write some code to be done during node creation.
Here's what I want to do:
Someone can either put in a ISBN or fill in each field manually for the book. If they do put in an ISBN, i'd like to grab all the information out of that array, and put it in the manual cck fields for author etc. That way when doing views all the information for every node, whether manually or amazon-ly done, wll be in the same fields.

So is there a way I can do this? If so, how, and where can I put the code?

Paul Vaartjes

Joined: 12/16/2007
OK, I think I'm working out

OK, I think I'm working out how to do this using 'presave' in the hook_nodeapi and a custom module.

Paul Vaartjes

Joined: 12/16/2007
New problem

I now have anew problem :)
I am importing the Author's name/s, and would somehow like to add them as tags in a taxonomy during the 'presave' time i mentioned before. Can anyone assist with this? Taking an array of authors and adding them as taxonomy terms?

Paul Vaartjes

Joined: 12/16/2007
Solved!

I think I have solved my problem thanks to http://drupal.org/project/content_taxonomy module.
:D I like solving my own problems for once!

The system now basically 'works'. I just need to do some pretty extensive theming and more code for the amazon import, then make up some views and navigation etc and I think I might kinda be done....?! wow. thanks again guys.

Paul Vaartjes

Joined: 12/16/2007
I've done alot of work on

I've done alot of work on this system, and am finishing off the rest of my church's site before it all goes live.

One question I'd like to solve:

is there a way to theme the node add form differently to the node edit form?

I would prefer the check box to make a book as checked out to not appear when creating the node, but only when editing it.

Even better, I'd love it if I could make a button on the node-view page which is shown only to the book's owner which says "Mark as Available" if the book has been checked out.

If anyone can lend a hand in working out how to do this, that would be great!

Thanks once again

Paul

Paul Vaartjes

Joined: 10/18/2008
You could use hook_form_alter

You could use hook_form_alter to check to see if arg(1) == "add"


function mymodule_form-alter(&$form, $form_state, $form_id){
  if (($form_id == "my_cool_booksys") && (arg(1) == "add")):
    $form['field_checked_out'][0]['value'] = array("attributes" => array("hidden" => "hidden"));
  endif;
}

Regarding the button on node view, you could copy the form that is displayed during node/add or edit and then chop it down to basically:

<form action="/node/XX" accept-charset="UTF-8" method="post" id="booklending-form-name" enctype="multipart/form-data">
<?php if ($node->field_book_is_checkedout[0]['value']): ?>
  <input type="hidden" name="field_booklib_checkedin[0][value]" value="1" />
  <input type="submit" name="op" id="edit-submit" value="Check Out"  class="form-submit" />
<?php else: ?>
  <input type="hidden" name="field_booklib_checkedin[0][value]" value="0" />
  <input type="submit" name="op" id="edit-submit" value="Check In"  class="form-submit" />
<?php endif;?>
</form>

I'm not sure if the above code would work right out of the box, but you get the idea. If you use the same form as your edit page, then it should go through the system and then you could just watch for it in hook_nodeapi.

/ * Begin Signature */
It's a strange thing about determined seekers-after-wisdom that, no matter where they happen to be, they'll always seek that wisdom which is a long way off. Wisdom is one of the few things that looks bigger the further away it is.

Joined: 12/16/2007
Thanks again BishopBooyah.

Thanks again BishopBooyah. You've been so much help though all this!
I have successfully removed the checkbox from my add node page!
I'm just having some trouble with the second lot of code. I fiddled around and tried different things. The form shows, It just doesnt seem to actually do anythign when i click the button. Here's what I'm using at the moment:

  1. <?php if ($node->field_available[0]['value'] == 1): ?>
  2. <form action="/development/node/<?php print $node->nid ?>" accept-charset="UTF-8" method="post" id="node-form" enctype="multipart/form-data">
  3. <input type="hidden" name="form_id" id="edit-book-node-form" value="book_node_form" />
  4. <input type="hidden" name="field_available[value]" id="edit-field-available-value" value="0" />
  5. <input type="submit" name="op" id="edit-submit" value="Check In" class="form-submit" />
  6. </form>
  7. <?php endif;?>

Can you see what my problem might be here?

Paul Vaartjes

Joined: 10/18/2008
Oh, my bad. I looked into it

Oh, my bad. I looked into it and you can't just declare a form because it won't go through. There's a unique form identifier that gets set through the drupal system.

There's two ways you can go with this and it depends on if you want quick and dirty or a more proper way of doing it.

Quick and dirty (and probably the way I'd do it if I was in a hurry). Basically have the form do the work for you. At the bottom of your node you can do something like:

<?php
if (user_access("edit own book_content")): // make sure user can check books in / out

if ($_POST['book_available_submit']): // button was pressed
 
$sql = "update {node} SET field_available_value = %d" where nid = " . arg(1);
 
$db_query($sql, $_POST['book_available']);
endif;
?>

<form action="/development/node/<?php print $node->nid ?>" accept-charset="UTF-8" method="post" id="node-form" enctype="multipart/form-data">

<?php if ($node->field_available[0]['value'] == 1): // Book is NOT available ?>

<input type="hidden" name="book_available" id="edit-book-available-value" value="0" />
<input type="submit" name="checkin_submit" id="checkin_submit" value="Check In" class="form-submit" />

<?php else: // Book is available ?>

<input type="hidden" name="book_available_submit" id="edit-book-available-value" value="1" />
<input type="submit" name="checkin_submit" id="checkin_submit" value="Check Out" class="form-submit" />

<?php endif;?>
</form>
<?php endif; ?>

But the proper way to do it would be to create your own module with a form. Basically a submit button, textfield to hold the nid, and checkbox for checkin/out. Then go through the drupal system. You can see how to do it here: http://drupal.org/node/262422

I'd just copy one of the examples. You don't need a validate function or menu.

Then when you display your node, use drupal_get_form(mymodule_checkin_form). You could then use hook_form_alter to set your checkbox value and nid:

function mymodule_checkin_form_alter(&$form, $form_state, $form_id){
  if ($form_id == "mymodule_checkin_form"):
    $nid = arg(1);
    $thebook = node_load($nid);
    $form['field_available'][0]['#default_value']['value'] = $thebook->field_available[0]['value'];
    $form['field_textfield_nid'][0]['value'] = $nid;
  endif;

Then your submit function:

function mymodule_checkin_form_submit($form, &$form_state) {
  $sql = "update {node} SET field_available_value = %d" where nid = %d";
  $db_query($sql, $form['field_available'][0]['value'], $form['field_textfield_nid'[0]['value']);
  drupal_set_message("Yay, it's checked in / out");
}

/ * Begin Signature */
It's a strange thing about determined seekers-after-wisdom that, no matter where they happen to be, they'll always seek that wisdom which is a long way off. Wisdom is one of the few things that looks bigger the further away it is.

Joined: 10/18/2008
The nice thing about writing

The nice thing about writing your own module to do the check in / out is that you could also use it to send the email to the lender from the lendee. Basically in your form_submit, check the value of available and if it's to be checked out, then use $user and grab the lendee's (member) info, package it into an email and send it to the lender (owner).

With the press of a button, email gets sent, book is checked out.

Might also want to create a checked_out_by field for your type - that way the other members know who has the book (if you want to show that info publically) but at the very least the lender should see that info. I know that I have a hard time remembering who I lent what to, so that would be SUPER helpful.

/ * Begin Signature */
It's a strange thing about determined seekers-after-wisdom that, no matter where they happen to be, they'll always seek that wisdom which is a long way off. Wisdom is one of the few things that looks bigger the further away it is.

G&G Moderator
EdRoss's picture
Joined: 10/05/2006
The Preacher's Library

Have you heard this month's episode? Maybe The Preacher's Library is what you are looking for.