5 Shelf - Perl extension for Virtual Bookshelves
21 Module for querying and stocking Virtual Bookshelves
23 1. can contain a list of items, a list of biblioitems, or a list of biblios
24 2. can have an arbitrary name, and will have a unique numerical identifier
25 3. will have arbitrary metadata (properties) associated with it
26 * Sharing information (private, only visible by the owner of the
27 shelf; shared with a group of patrons; public, viewable by anybody)
28 * Special circulation rules - Do not return to home branch, do not
29 circulate, reduced loan time (ie 3 day loan)
30 * Search query term - if the shelf is the result of a query, the
31 query itself can be stored with the list of books that resulted
32 * Creation date - useful for 'retiring' a stale cached query result
33 * Access information - who has "write" or "read" access to the shelf.
34 * Searchable - If a patron can perform a search query on the contents
38 Patrons typically will only use "biblioitem" bookshelves, and will not need to
39 be presented with the differences between biblioitem and item bookshelves.
42 Some uses for VirtualBookshelves
44 1. Cache search results for faster response on popular searches
45 2. Name search results so that patrons can pull up saved searches
46 3. Creation of sub-collections within a library or branch
47 4. replacing "itemtypes" field... this would allow an individual item to be
48 a member of more than one itemtype
49 5. store a patron's reading record (if he chooses to store such data)
50 6. store a patron's "To be read" list
51 7. A teacher of a course could add a list of books to a shelf for his course
52 and ask that those items be marked non-circulating so students always
53 have access to them at the library.
54 * The teacher creates the list of materials that she wants to be
55 non-circulating (or reduced to 3-day loan) and marks them as such
56 * A librarian receives a notice that a shelf requires her attention.
57 He can pull up a list of the contents of the shelf, the owner of
58 the shelf, and the reason the owner is requesting this change in
59 circulation rules. The librarian can approve or deny the request.
60 * Optionally, create an access flag that grants teachers the right to
61 put items on modified circulation shelves without librarian
71 Base constructor for the class.
73 my $shelf=Shelf->new(56);
74 will load bookshelf 56.
75 my $shelf=Shelf->new(-name => 'Fiction');
76 my $shelf=Shelf->new('Fiction');
77 will load the internal 'Fiction' shelf
78 my $shelf=Shelf->new('Favourite Books', 'sjohnson');
79 my $shelf=Shelf->new(-name => 'Favourite Books', -owner => 'sjohnson');
80 will load sjohnson's "Favourite Books" bookshelf
82 Any of the last four invocations will create a new shelf with the name and
83 owner given if one doesn't already exist.
92 $self->{OWNDER}=undef;
93 $self->{BIBLIOCONTENTS}={};
94 $self->{BIBLIOITEMCONTENTS}={};
95 $self->{ITEMCONTENTS}={};
96 $self->{ATTRIBUTES}={};
97 $self->{CACHE}=new Cache::FileCache( { 'namespace' => 'KohaShelves' } );
100 my $dbh=C4::Context->dbh();
104 # load attributes of shelf #ID
106 $sth=$dbh->prepare("select bookshelfname,bookshelfowner from bookshelves where bookshelfid=?");
107 $sth->execute($self->{ID});
108 ($self->{NAME},$self->{OWNER}) = $sth->fetchrow;
109 $sth=$dbh->prepare("select attribute,value from bookshelfattributes where bookshelfid=?");
110 $sth->execute($self->{ID});
111 while (my ($attribute,$value) = $sth->fetchrow) {
112 $self->{ATTRIBUTES}->{$attribute}=$value;
115 my ($name,$owner,$attributes);
119 $owner=$params{owner};
120 $attributes=$params{attributes};
126 my $sth=$dbh->prepare("select bookshelfid from bookshelves where bookshelfname=? and bookshelfowner=?");
127 $sth->execute($name, $owner);
129 ($self->{ID})=$sth->fetchrow;
130 $sth=$dbh->prepare("select attribute,value from bookshelfattributes where bookshelfid=?");
131 $sth->execute($self->{ID});
132 while (my ($attribute,$value) = $sth->fetchrow) {
133 $self->{ATTRIBUTES}->{$attribute}=$value;
136 $sth=$dbh->prepare("insert into bookshelves (bookshelfname, bookshelfowner) values (?, ?)");
137 $sth->execute($name,$owner);
138 $sth=$dbh->prepare("select bookshelfid from bookshelves where bookshelfname=? and bookshelfowner=?");
139 $sth->execute($name,$owner);
140 ($self->{ID})=$sth->fetchrow();
141 foreach my $attribute (keys %$attributes) {
142 my $value=$attributes->{$attribute};
143 $self->attribute($attribute,$value);
153 =head2 C<itemcontents()>
155 retrieve a slice of itemnumbers from a shelf.
157 my $arrayref = $shelf->itemcontents(-orderby=>'title',
165 my ($orderby,$startat,$number);
168 $orderby=$params{'-orderby'};
169 $startat=$params{'-startat'};
170 $number=$params{'-number'};
172 ($orderby,$startat,$number)=@_;
175 unless ($self->{ITEMCONTENTS}->{orderby}->{$orderby}) {
176 $self->loadcontents(-orderby=>$orderby, -startat=>$startat, -number=>$number);
178 my $endat=$startat+$number;
180 foreach (@{$self->{ITEMCONTENTS}->{orderby}->{$orderby}}[$startat..$endat]) {
186 =head2 C<biblioitemcontents()>
188 retrieve a slice of biblioitemnumbers from a shelf.
190 my $arrayref = $shelf->biblioitemcontents(-orderby=>'title',
196 sub biblioitemcontents {
198 my ($orderby,$startat,$number);
201 $orderby=$params{'-orderby'};
202 $startat=$params{'-startat'};
203 $number=$params{'-number'};
205 ($orderby,$startat,$number)=@_;
207 unless ($self->{BIBLIOITEMCONTENTS}->{orderby}->{$orderby}) {
208 $self->loadcontents(-orderby=>$orderby, -startat=>$startat, -number=>$number);
210 my $endat=$startat+$number;
212 foreach (@{$self->{BIBLIOITEMCONTENTS}->{orderby}->{$orderby}}[$startat..$endat]) {
218 =head2 C<biblioitemcontents()>
220 retrieve a slice of biblionumbers from a shelf.
222 my $arrayref = $shelf->bibliocontents(-orderby=>'title',
230 my ($orderby,$startat,$number);
233 $orderby=$params{'-orderby'};
234 $startat=$params{'-startat'};
235 $number=$params{'-number'};
237 ($orderby,$startat,$number)=@_;
239 unless ($self->{BIBLIOCONTENTS}->{orderby}->{$orderby}) {
240 $self->loadcontents(-orderby=>$orderby, -startat=>$startat, -number=>$number);
242 my $endat=$startat+$number;
244 foreach (@{$self->{BIBLIOCONTENTS}->{orderby}->{$orderby}}[$startat..$endat]) {
251 =head2 C<itemcounter()>
253 returns the number of items on the shelf
255 my $itemcount=$shelf->itemcounter();
260 unless ($self->{ITEMCONTENTS}->{orderby}->{'natural'}) {
261 $self->loadcontents();
263 my @temparray=@{$self->{ITEMCONTENTS}->{orderby}->{'natural'}};
264 return $#temparray+1;
272 =head2 C<clearcontents()>
274 Removes all contents from the shelf.
276 $shelf->clearcontents();
282 my $dbh=C4::Context->dbh();
283 my $sth=$dbh->prepare("delete from bookshelfcontents where bookshelfid=?");
284 $sth->execute($self->{ID});
285 foreach my $level ('ITEM', 'BIBLIOITEM', 'BIBLIO') {
286 delete $self->{$level."CONTENTS"};
287 $self->{$level."CONTENTS"}={};
295 =head2 C<addtoshelf()>
297 adds an array of items to a shelf. If any modifications are actually made to
298 the shelf then the per process caches and the FileCache for that shelf are
301 $shelf->addtoshelf(-add => [[ 45, 54, 67], [69, 87, 143]]);
310 $add=$params{'-add'};
314 my $dbh=C4::Context->dbh();
316 my $bookshelfid=$self->{ID};
319 my ($biblionumber,$biblioitemnumber,$itemnumber) = @$_;
320 $sth=$dbh->prepare("select count(*) from bookshelfcontents where bookshelfid=? and itemnumber=? and biblioitemnumber=? and biblionumber=?");
321 $sth->execute($bookshelfid,$itemnumber,$biblioitemnumber,$biblionumber);
322 my $rows=$sth->fetchrow();
324 $sth=$dbh->prepare("insert into bookshelfcontents (bookshelfid,biblionumber,biblioitemnumber,itemnumber) values (?,?,?,?)");
325 $sth->execute($bookshelfid,$biblionumber,$biblioitemnumber,$itemnumber);
329 ($clearcache) && ($self->clearcache());
333 sub removefromshelf {
337 =head2 C<attribute()>
339 Returns or sets the value of a given attribute for the shelf.
341 my $loanlength=$shelf->attribute('loanlength');
342 $shelf->attribute('loanlength', '21 days');
349 my ($attribute, $value);
353 $self->{ATTRIBUTES}->{$attribute}=$value;
354 my $dbh=C4::Context->dbh();
355 my $sth=$dbh->prepare("select value from bookshelfattributes where bookshelfid=? and attribute=?");
356 $sth->execute($self->{ID}, $attribute);
358 my $sti=$dbh->prepare("update bookshelfattributes set value=? where bookshelfid=? and attribute=?");
359 $sti->execute($value, $self->{ID}, $attribute);
361 my $sti=$dbh->prepare("insert into bookshelfattributes (bookshelfid, attribute, value) values (?, ?, ?)");
362 $sti->execute($self->{ID}, $attribute, $value);
365 return $self->{ATTRIBUTES}->{$attribute};
369 =head2 C<attributes()>
371 Returns a hash reference of the shelf attributes
373 my $attributes=$shelf->attributes();
374 my $loanlength=$attributes->{loanlength};
380 return $self->{ATTRIBUTES};
383 =head2 C<clearcache()>
385 Clears the per process in-memory cache and the FileCache if any changes are
388 $shelf->clearshelf();
394 foreach my $level ('ITEM','BIBLIOITEM','BIBLIO') {
395 delete $self->{$level."CONTENTS"};
396 foreach my $sorttype (('author', 'title')) {
397 $self->{CACHE}->remove($self->{ID}."_".$level."CONTENTS_".$sorttype);
403 =head2 C<loadcontents()>
405 loads the contents of a particular shelf and loads into a per process memory
406 cache as well as a shared Cache::FileCache.
408 This subroutine is normally only used internally (called by itemcontents,
409 biblioitemcontents, or bibliocontents).
411 $shelf->loadcontents(-orderby => 'author', -startat => 30, -number => 10);
418 my ($orderby,$startat,$number);
421 $orderby=$params{'-orderby'};
422 $startat=$params{'-startat'};
423 $number=$params{'-number'};
425 ($orderby,$startat,$number)=@_;
427 my $bookshelfid=$self->{ID};
428 ($orderby) || ($orderby='natural');
429 $self->{ITEMCONTENTS}->{orderby}->{$orderby}=$self->{CACHE}->get( "$bookshelfid\_ITEMCONTENTS_$orderby" );
430 $self->{BIBLIOITEMCONTENTS}->{orderby}->{$orderby}=$self->{CACHE}->get( "$bookshelfid\_BIBLIOITEMCONTENTS_$orderby" );
431 $self->{BIBLIOCONTENTS}->{orderby}->{$orderby}=$self->{CACHE}->get( "$bookshelfid\_BIBLIOCONTENTS_$orderby" );
432 if ( defined $self->{ITEMCONTENTS}->{orderby}->{$orderby}) {
435 my $dbh=C4::Context->dbh();
438 if ($startat && $number) {
439 $limit="limit $startat,$number";
443 my $biblioitemnumbers;
444 if ($orderby eq 'author') {
445 $sth=$dbh->prepare("select itemnumber,BSC.biblionumber,BSC.biblioitemnumber from bookshelfcontents BSC, biblio B where BSC.biblionumber=B.biblionumber and bookshelfid=? order by B.author $limit");
446 } elsif ($orderby eq 'title') {
447 $sth=$dbh->prepare("select itemnumber,BSC.biblionumber,BSC.biblioitemnumber from bookshelfcontents BSC, biblio B where BSC.biblionumber=B.biblionumber and bookshelfid=? order by B.title $limit");
449 $sth=$dbh->prepare("select itemnumber,biblionumber,biblioitemnumber from bookshelfcontents where bookshelfid=? $limit");
451 $sth->execute($bookshelfid);
454 my @biblioitemresults;
455 while (my ($itemnumber,$biblionumber,$biblioitemnumber) = $sth->fetchrow) {
456 unless ($biblionumbers->{$biblionumber}) {
457 $biblionumbers->{$biblionumber}=1;
458 push @biblioresults, $biblionumber;
460 unless ($biblioitemnumbers->{$biblioitemnumber}) {
461 $biblioitemnumbers->{$biblioitemnumber}=1;
462 push @biblioitemresults, $biblioitemnumber;
464 push @results, $itemnumber;
466 $self->{CACHE}->set("$bookshelfid\_ITEMCONTENTS_$orderby", \@results, "3 hours");
467 $self->{CACHE}->set("$bookshelfid\_BIBLIOITEMCONTENTS_$orderby", \@results, "3 hours");
468 $self->{CACHE}->set("$bookshelfid\_BIBLIOCONTENTS_$orderby", \@results, "3 hours");
469 $self->{ITEMCONTENTS}->{orderby}->{$orderby}=\@results;
470 $self->{BIBLIOOCONTENTS}->{orderby}->{$orderby}=\@biblioresults;
471 $self->{BIBLIOITEMCONTENTS}->{orderby}->{$orderby}=\@biblioitemresults;