1 package org.paneris.bibliomania;
2
3 import java.io.BufferedOutputStream;
4 import java.io.BufferedReader;
5 import java.io.File;
6 import java.io.FileOutputStream;
7 import java.io.FileReader;
8 import java.io.IOException;
9 import java.io.OutputStream;
10 import java.io.Reader;
11 import java.io.StringReader;
12 import java.net.InetAddress;
13 import java.sql.PreparedStatement;
14 import java.sql.ResultSet;
15 import java.sql.Timestamp;
16 import java.util.Enumeration;
17 import java.util.Hashtable;
18 import java.util.Locale;
19 import java.util.Properties;
20 import java.util.Vector;
21
22 import org.melati.Melati;
23 import org.melati.MelatiConfig;
24 import org.melati.poem.AccessToken;
25 import org.melati.poem.Capability;
26 import org.melati.poem.Group;
27 import org.melati.poem.NoSuchRowPoemException;
28 import org.melati.poem.PoemTask;
29 import org.melati.poem.PoemThread;
30 import org.melati.poem.PreparedStatementFactory;
31 import org.melati.poem.Setting;
32 import org.melati.poem.TableInfo;
33 import org.melati.template.webmacro.MelatiFastWriter;
34 import org.melati.util.Email;
35 import org.melati.util.FileUtils;
36 import org.melati.util.IoUtils;
37 import org.melati.util.MelatiRuntimeException;
38 import org.melati.util.UnexpectedExceptionException;
39 import org.paneris.bibliomania.fti.FTIException;
40 import org.paneris.bibliomania.fti.IndexOther;
41 import org.paneris.bibliomania.fti.Library;
42 import org.paneris.bibliomania.fti.Text;
43 import org.paneris.bibliomania.generated.BibliomaniaDatabaseBase;
44 import org.paneris.bibliomania.metasearch.BookshopBackend;
45 import org.paneris.bibliomania.metasearch.BookshopBackendFactory;
46 import org.paneris.bibliomania.pagination.Pagination;
47 import org.webmacro.Context;
48 import org.webmacro.InitException;
49 import org.webmacro.WM;
50 import org.webmacro.WebMacro;
51 import org.webmacro.WebMacroException;
52 import org.webmacro.engine.FileTemplate;
53 import org.webmacro.engine.StreamTemplate;
54
55 public class BibliomaniaDatabase
56 extends BibliomaniaDatabaseBase
57 implements BibliomaniaDatabaseTables {
58 private IndexOther fti = null, infoFTI = null;
59 private Properties ftiConfig;
60 private Pagination pagination = null;
61
62 public static final int defaultDefaultSearchHitsPerText = 5,
63 defaultSearchHitsPerPage = 20,
64 defaultBookStockingsCheckIntervalDays = 7,
65 defaultBibBookBookshopSearchTimeoutSeconds = 120,
66 defaultBookStockingsCheckConcurrentMax = 2,
67 defaultBookStockingsOutputStartOffset = 25,
68 defaultBookStockingsCacheSizeMax = 10000;
69
70 public static final String defaultPasswordReminderMessage =
71 "defaultPasswordReminderMessage.wm",
72 defaultPasswordReminderFrom = "help",
73 defaultOrderEmailFrom = "orders@bibliomania.com",
74 defaultConfirmationEmailFrom = "customercare@bibliomania.com",
75 defaultOrderEmailTo = "purchases@bibliomania.com",
76 defaultStratusEmailTo = "info@houseofstratus.com",
77 defaultContentEncoding = "ISO-8859-1",
78 defaultUploadDir = "/usr/local/share/bibliomania/books/",
79 defaultUploadURL = "/";
80
81
82 private Setting workspaceDir,
83 contentRootDir,
84 staticRootURL,
85 contentStaticRootURL,
86 cacheRootURL,
87 homepageURL,
88 orderEmailFrom,
89 confirmationEmailFrom,
90 orderEmailTo,
91 stratusEmailTo,
92 defaultSearchHitsPerText,
93 searchHitsPerPage,
94 paginationTexHeader,
95 bookStockingsCheckIntervalDays,
96 bookStockingsCheckConcurrentMax,
97 bookStockingsInBackground,
98 bibBookBookshopSearchTimeoutSeconds,
99 smtpServer,
100 passwordReminderMessage,
101 passwordReminderFrom,
102 infoFTIDir,
103 bannerURLPath,
104 bookStockingsOutputStartOffset,
105 bookStockingsCacheDir,
106 bookStockingsCacheSizeMax,
107 contentEncoding,
108 uploadDir,
109 uploadURL;
110
111 private Currency UKCurrency;
112
113
114 private Group registeredUserGroup;
115
116 private Capability registeredUserCapability,
117 contentModificationCapability,
118 contentAdditionCapability;
119
120 private User templateRegisterUser;
121
122 private Bookshop[] bookshops = null;
123
124 private SectionGroup readSectionGroup,
125 studySectionGroup,
126 researchSectionGroup,
127 shopSectionGroup,
128 searchSectionGroup;
129
130 private boolean bookStockingsInBackgroundAppropriate, openAuxDBs;
131
132 private Section drama;
133 private Author shakespeare;
134 private Book macbeth;
135
136 public BibliomaniaDatabase() {
137 this(false);
138 }
139
140 public BibliomaniaDatabase(boolean bookStockingsInBackgroundAppropriate) {
141 this(bookStockingsInBackgroundAppropriate, true);
142 }
143
144 public BibliomaniaDatabase(
145 boolean bookStockingsInBackgroundAppropriate,
146 boolean openAuxDBs) {
147 this(bookStockingsInBackgroundAppropriate, openAuxDBs, null);
148 }
149
150 public BibliomaniaDatabase(
151 boolean bookStockingsInBackgroundAppropriate,
152 boolean openAuxDBs,
153 Properties ftiConfig) {
154 this.bookStockingsInBackgroundAppropriate =
155 bookStockingsInBackgroundAppropriate;
156 this.openAuxDBs = openAuxDBs;
157 this.ftiConfig = ftiConfig;
158 }
159
160 public boolean logSQL() {
161 return false;
162 }
163
164 public boolean debug() {
165 return false;
166 }
167
168 public Capability getCanAdminister() {
169 return administerCapability();
170 }
171
172 public void connect(
173 String name,
174 String dbmsclass,
175 String url,
176 String username,
177 String password,
178 int maxConnections) {
179 super.connect(name, dbmsclass, url, username, password, maxConnections);
180
181 inSession(AccessToken.root, new PoemTask() {
182 public void run() {
183 String root = "/usr/local/share/bibliomania";
184 ((UserTable)getUserTable()).setBoardManager(
185 ((UserTable)getUserTable()).ensure(
186 "_manager_","FIXME","board_manager@paneris.co.uk","Bibliomania Board Manager"));
187
188
189 readSectionGroup =
190 getSectionGroupTable().ensure(
191 "#ff9900",
192 "read",
193 "read.gif",
194 "readg.gif",
195 "1",
196 "read",
197 "",
198 false,
199 null);
200
201 studySectionGroup =
202 getSectionGroupTable().ensure(
203 "#9999ff",
204 "study",
205 "study.gif",
206 "studyg.gif",
207 "4",
208 "study",
209 "",
210 false,
211 null);
212
213 researchSectionGroup =
214 getSectionGroupTable().ensure(
215 "#cc9966",
216 "research",
217 "researchb.gif",
218 "researcha.gif",
219 "6",
220 "research",
221 "",
222 false,
223 null);
224
225
226
227
228
229 searchSectionGroup =
230 getSectionGroupTable().ensure(
231 "#ff0000",
232 "search",
233 "search.gif",
234 "searchg.gif",
235 "2",
236 "search",
237 "",
238 true,
239 "/b/org.paneris.bibliomania.Search");
240
241
242 drama = getSectionTable().ensure("Drama", readSectionGroup);
243 shakespeare = getAuthorTable().ensure("William Shakespeare", drama);
244 macbeth = getBookTable().ensure("Macbeth", shakespeare, drama);
245
246 registeredUserGroup = getGroupTable().ensure("Registered users");
247 registeredUserCapability =
248 getCapabilityTable().ensure("Access study material");
249 contentModificationCapability =
250 getCapabilityTable().ensure("Edit content database");
251 contentAdditionCapability =
252 getCapabilityTable().ensure("Add to content database");
253
254 templateRegisterUser =
255 (User)getUserTable().getLoginColumn().firstWhereEq(
256 "_registerTemplate_");
257
258 if (templateRegisterUser == null) {
259 templateRegisterUser = (User)getUserTable().newPersistent();
260 templateRegisterUser.setLogin("_registerTemplate_");
261 templateRegisterUser.setPassword("0.804461358580738");
262 templateRegisterUser.setEmail("none@bogus");
263 templateRegisterUser.setFulltimeeducation(false);
264 templateRegisterUser.setWantspam(false);
265 templateRegisterUser.setWantemailalerts(false);
266 templateRegisterUser.setDodgeyemail(false);
267 templateRegisterUser.setName(
268 "<Template for newly registering users>");
269 getUserTable().create(templateRegisterUser);
270 }
271
272 workspaceDir =
273 getSettingTable().ensure(
274 "WorkspaceDir",
275 root + "/workspace",
276 "Workspace directory",
277 "Where the system should store its working files");
278
279 infoFTIDir =
280 getSettingTable().ensure(
281 "InfoFTIDir",
282 "/infoFTI",
283 "Info-FTI directory",
284 "Where the system should store the info-page index, relative "
285 + "to WorkspaceDir");
286
287 contentRootDir =
288 getSettingTable().ensure(
289 "ContentRootDir",
290 root + "/books",
291 "Content root directory",
292 "Where (in the local filesystem) the system should look for "
293 + "the book content files");
294
295 staticRootURL =
296 getSettingTable().ensure(
297 "StaticRootURL",
298 "/bibliomania-static",
299 "Static root URL",
300 "Where the browser can find the Bibliomania chrome, "
301 + "relative to the server root URL");
302
303 contentStaticRootURL =
304 getSettingTable().ensure(
305 "ContentStaticRootURL",
306 "/bibliomania-content-static",
307 "Content static root URL",
308 "Where the browser can find the Bibliomania static content, "
309 + "relative to the server root URL");
310
311 bannerURLPath =
312 getSettingTable().ensure(
313 "BannerURLPath",
314 "/bibliomania-content-static",
315 "Banner URL Path",
316 "Where the browser can find the Bibliomania static content, "
317 + "relative to the server root URL");
318
319 cacheRootURL =
320 getSettingTable().ensure(
321 "CacheRootURL",
322 "",
323 "Cache root URL",
324 "Where the web server should look for the banner ad, "
325 + "relative to the server root URL");
326
327 homepageURL =
328 getSettingTable().ensure(
329 "HomepageURL",
330 "http://www.bibliomania.com",
331 "Home page URL",
332 "A full URL for the front page of bibliomania");
333
334 defaultSearchHitsPerText =
335 getSettingTable().ensure(
336 "DefaultSearchHitsPerText",
337 defaultDefaultSearchHitsPerText,
338 "Default search hits per text",
339 "The default number of search hits to return from each text");
340
341 searchHitsPerPage =
342 getSettingTable().ensure(
343 "SearchHitsPerPage",
344 defaultSearchHitsPerPage,
345 "Search hits per page",
346 "The number of search hits to return on each results page");
347
348 bookStockingsCheckIntervalDays =
349 getSettingTable().ensure(
350 "StockingsCheckIntervalDays",
351 defaultBookStockingsCheckIntervalDays,
352 "Bookshop search refresh days",
353 "The number of days for which a cached bookshop search "
354 + "remains valid");
355
356 bibBookBookshopSearchTimeoutSeconds =
357 getSettingTable().ensure(
358 "BibBookSearchTimeoutSecs",
359 defaultBibBookBookshopSearchTimeoutSeconds,
360 "Timeout for shop search for Bib book",
361 "The number of seconds which a bookshop search for a "
362 + "Bibliomania book is allowed to continue before being "
363 + "cut short");
364
365 bookStockingsInBackground =
366 getSettingTable().ensure(
367 "StockingsCheckInBackground",
368 false,
369 "Background bookshop search",
370 "Whether to run continual bookshop searches in the background");
371
372 bookStockingsOutputStartOffset =
373 getSettingTable().ensure(
374 "StockingsOutputStartOffset",
375 defaultBookStockingsOutputStartOffset,
376 "Start Offset for Shop DHTML",
377 "How far down the page to start DHTML output");
378
379 bookStockingsCacheDir =
380 getSettingTable().ensure(
381 "StockingsCacheDir",
382 root + "/stockings",
383 "Book stockings cache directory",
384 "Where the system should store cached results of bookshop "
385 + "searches");
386
387 bookStockingsCacheSizeMax =
388 getSettingTable().ensure(
389 "StockingsCacheMax",
390 defaultBookStockingsCacheSizeMax,
391 "Max cached book stockings",
392 "How many bookshop search results the system should cache");
393
394 bookStockingsCheckConcurrentMax =
395 getSettingTable().ensure(
396 "StockingsCheckConcurrentMax",
397 defaultBookStockingsCheckConcurrentMax,
398 "Max concurrent bookshop searches",
399 "The maximum number of bookshop searches to allow "
400 + "at once (including the background search)");
401
402 UKCurrency =
403 getCurrencyTable().ensure("UK Pound Sterling", 1d, Locale.UK);
404
405
406 if (openAuxDBs) {
407 try {
408 fti = new IndexOther(new File(getWorkspaceDir()), ftiConfig);
409 infoFTI = new IndexOther(new File(getInfoFTIDir()));
410 pagination = new Pagination(new File(getWorkspaceDir()));
411 } catch (Exception e) {
412 throw new FTIException(e);
413 }
414
415 paginationTexHeader =
416 (Setting)getSettingTable().getNameColumn().firstWhereEq(
417 "PaginationTexHeader");
418 if (paginationTexHeader == null) {
419 paginationTexHeader =
420 getSettingTable().ensure(
421 "PaginationTexHeader",
422 pagination.defaultTexHeader(),
423 "TeX header for pagination",
424 "The header to be prepended to the TeX version of a "
425 + "chapter when TeX is run to determine page breaks");
426 paginationTexHeader.setWidth(60);
427 paginationTexHeader.setHeight(5);
428 }
429 }
430
431 Vector bookshopsList = new Vector();
432
433 Bookshop bol = getBookshopTable().ensure("BOLUK", "BOL UK", "uk.gif");
434 if (!Boolean.TRUE.equals(bol.getDisabled())) {
435 bol.backend = bookshopBackendNamed("bol");
436 bookshopsList.addElement(bol);
437 }
438
439 Bookshop bob =
440 getBookshopTable().ensure(
441 "BOB",
442 "Blackwells Online Bookshop",
443 "uk.gif");
444 if (!Boolean.TRUE.equals(bob.getDisabled())) {
445 bob.backend = bookshopBackendNamed("bob");
446 bookshopsList.addElement(bob);
447 }
448
449 Bookshop amazon =
450 getBookshopTable().ensure("AMAZON", "amazon.com", "us.gif");
451 if (!Boolean.TRUE.equals(amazon.getDisabled())) {
452 amazon.backend = bookshopBackendNamed("amazon");
453 bookshopsList.addElement(amazon);
454 }
455
456 bookshops = new Bookshop[bookshopsList.size()];
457 bookshopsList.copyInto(bookshops);
458
459 String defaultSmtpServer;
460 try {
461 defaultSmtpServer = InetAddress.getLocalHost().toString();
462 } catch (Exception e) {
463 defaultSmtpServer = "www.bibliomania.com";
464 }
465
466 smtpServer =
467 getSettingTable().ensure(Email.SMTPSERVER,
468 defaultSmtpServer,
469 "SMTP server",
470 "The SMTP server through which auto-generated mail " + "should be sent");
471
472 passwordReminderMessage =
473 (Setting)getSettingTable().getNameColumn().firstWhereEq(
474 "PasswordReminderMessage");
475 if (passwordReminderMessage == null) {
476 String it;
477 try {
478 it =
479 new String(
480 IoUtils.slurp(
481 this.getClass().getResource(defaultPasswordReminderMessage),
482 4096));
483 } catch (Exception e) {
484 throw new UnexpectedExceptionException(
485 "Didn't find resource " + defaultPasswordReminderMessage,
486 e);
487 }
488
489 passwordReminderMessage =
490 getSettingTable().ensure(
491 "PasswordReminderMessage",
492 it,
493 "Password reminder message",
494 "The text of the message sent to users to remind them of "
495 + "their password (actually a WebMacro template)");
496 passwordReminderMessage.setWidth(80);
497 passwordReminderMessage.setHeight(30);
498 }
499
500 passwordReminderFrom =
501 getSettingTable().ensure(
502 "PasswordReminderFrom",
503 defaultPasswordReminderFrom,
504 "Password reminder `From:'",
505 "The address from whom the emailed password reminders "
506 + "should originate");
507
508 confirmationEmailFrom =
509 getSettingTable().ensure(
510 "ConfirmationEmailFrom",
511 defaultConfirmationEmailFrom,
512 "Confirmation Email `From:'",
513 "The address from whom the confirmation email that is sent on "
514 + "registration should originate");
515
516 orderEmailFrom =
517 getSettingTable().ensure(
518 "OrderEmailFrom",
519 defaultOrderEmailFrom,
520 "Order Email `From:'",
521 "The address from whom the emailed order acknowledgements "
522 + "should originate");
523
524 orderEmailTo =
525 getSettingTable().ensure(
526 "OrderEmailTo",
527 defaultOrderEmailTo,
528 "Order Email `To:'",
529 "The address to send email acknowledgements to (at bibliomania)");
530
531 stratusEmailTo =
532 getSettingTable().ensure(
533 "StratusEmailTo",
534 defaultStratusEmailTo,
535 "Stratus Email `To:'",
536 "The address to send email orders to (at Stratus)");
537
538 contentEncoding =
539 getSettingTable().ensure(
540 "Content encoding",
541 defaultContentEncoding,
542 "Encoding (character set) to use for content",
543 "The encoding, or character set, to use for the cached "
544 + "content; if you don't know why you need to set this, "
545 + "leave it as UTF8.");
546
547 uploadDir =
548 getSettingTable().ensure(
549 "UploadDir",
550 defaultUploadDir,
551 "Upload Directory for Product Images",
552 "Upload Directory for product images. This is prepended to the "
553 + "path used for the book to which this product relates.");
554
555 uploadURL =
556 getSettingTable().ensure(
557 "UploadURL",
558 defaultUploadURL,
559 "URL used when finding uploaded images",
560 "The URL used when finding uploaded images. This is prepended to the "
561 + "URL used for the book to which this product relates.");
562
563
564
565
566
567 setDefaultAccessPermissions(
568 getAdvertTable().getTableInfo(),
569 contentAdditionCapability,
570 administerCapability(),
571 contentModificationCapability,
572 null);
573
574
575
576 setDefaultAccessPermissions(
577 getAttachmentTable().getTableInfo(),
578 null,
579 administerCapability(),
580 administerCapability(),
581 null);
582
583
584 setDefaultAccessPermissions(
585 getAttachmentTypeTable().getTableInfo(),
586 administerCapability(),
587 administerCapability(),
588 administerCapability(),
589 null);
590
591
592 setDefaultAccessPermissions(
593 getAuthorTable().getTableInfo(),
594 contentAdditionCapability,
595 administerCapability(),
596 contentModificationCapability,
597 null);
598
599
600 setDefaultAccessPermissions(
601 getAuthorWebSiteTable().getTableInfo(),
602 contentAdditionCapability,
603 administerCapability(),
604 contentModificationCapability,
605 null);
606
607
608 setDefaultAccessPermissions(
609 getBoardTable().getTableInfo(),
610 contentAdditionCapability,
611 administerCapability(),
612 contentModificationCapability,
613 null);
614
615
616 setDefaultAccessPermissions(
617 getBoardTypeTable().getTableInfo(),
618 administerCapability(),
619 administerCapability(),
620 contentModificationCapability,
621 null);
622
623
624 setDefaultAccessPermissions(
625 getBookTable().getTableInfo(),
626 contentAdditionCapability,
627 administerCapability(),
628 contentModificationCapability,
629 null);
630
631
632 setDefaultAccessPermissions(
633 getBookFormatTable().getTableInfo(),
634 administerCapability(),
635 administerCapability(),
636 administerCapability(),
637 null);
638
639
640 setDefaultAccessPermissions(
641 getBookshopTable().getTableInfo(),
642 administerCapability(),
643 administerCapability(),
644 contentModificationCapability,
645 null);
646
647
648 setDefaultAccessPermissions(
649 getBookStockingTable().getTableInfo(),
650 administerCapability(),
651 administerCapability(),
652 administerCapability(),
653 null);
654
655
656 setDefaultAccessPermissions(
657 getChapterTable().getTableInfo(),
658 contentAdditionCapability,
659 administerCapability(),
660 contentModificationCapability,
661 null);
662
663
664 setDefaultAccessPermissions(
665 getCountryTable().getTableInfo(),
666 contentAdditionCapability,
667 administerCapability(),
668 contentModificationCapability,
669 null);
670
671
672 setDefaultAccessPermissions(
673 getCurrencyTable().getTableInfo(),
674 contentAdditionCapability,
675 administerCapability(),
676 contentModificationCapability,
677 null);
678
679
680 setDefaultAccessPermissions(
681 getDeliveryChargeTable().getTableInfo(),
682 contentAdditionCapability,
683 administerCapability(),
684 contentModificationCapability,
685 null);
686
687
688 setDefaultAccessPermissions(
689 getDeliveryChargeBandTable().getTableInfo(),
690 contentAdditionCapability,
691 administerCapability(),
692 contentModificationCapability,
693 null);
694
695
696 setDefaultAccessPermissions(
697 getDownloadTable().getTableInfo(),
698 contentAdditionCapability,
699 administerCapability(),
700 contentModificationCapability,
701 null);
702
703
704 setDefaultAccessPermissions(
705 getDownloadEventTable().getTableInfo(),
706 null,
707 administerCapability(),
708 administerCapability(),
709 administerCapability());
710
711
712 setDefaultAccessPermissions(
713 getLayoutTable().getTableInfo(),
714 contentAdditionCapability,
715 administerCapability(),
716 contentModificationCapability,
717 null);
718
719
720 setDefaultAccessPermissions(
721 getMembershipStatusTable().getTableInfo(),
722 contentAdditionCapability,
723 administerCapability(),
724 contentModificationCapability,
725 null);
726
727
728 setDefaultAccessPermissions(
729 getShopOrderTable().getTableInfo(),
730 null,
731 administerCapability(),
732 null,
733 null);
734
735
736 setDefaultAccessPermissions(
737 getShopOrderItemTable().getTableInfo(),
738 null,
739 administerCapability(),
740 null,
741 null);
742
743
744 setDefaultAccessPermissions(
745 getOrderStatusTable().getTableInfo(),
746 administerCapability(),
747 administerCapability(),
748 administerCapability(),
749 null);
750
751
752 setDefaultAccessPermissions(
753 getProductTable().getTableInfo(),
754 contentAdditionCapability,
755 administerCapability(),
756 contentModificationCapability,
757 null);
758
759
760 setDefaultAccessPermissions(
761 getProductAssociationTable().getTableInfo(),
762 contentAdditionCapability,
763 administerCapability(),
764 contentModificationCapability,
765 null);
766
767
768 setDefaultAccessPermissions(
769 getPublisherTable().getTableInfo(),
770 contentAdditionCapability,
771 administerCapability(),
772 contentModificationCapability,
773 null);
774
775
776 setDefaultAccessPermissions(
777 getPublisherTable().getTableInfo(),
778 contentAdditionCapability,
779 administerCapability(),
780 contentModificationCapability,
781 null);
782
783
784 setDefaultAccessPermissions(
785 getSectionTable().getTableInfo(),
786 contentAdditionCapability,
787 administerCapability(),
788 contentModificationCapability,
789 null);
790
791
792 setDefaultAccessPermissions(
793 getSectionTable().getTableInfo(),
794 administerCapability(),
795 administerCapability(),
796 administerCapability(),
797 null);
798
799
800 setDefaultAccessPermissions(
801 getSexTable().getTableInfo(),
802 administerCapability(),
803 administerCapability(),
804 administerCapability(),
805 null);
806
807
808 setDefaultAccessPermissions(
809 getStockingsSearchTable().getTableInfo(),
810 null,
811 administerCapability(),
812 administerCapability(),
813 null);
814
815
816 setDefaultAccessPermissions(
817 getSupplierTable().getTableInfo(),
818 contentAdditionCapability,
819 administerCapability(),
820 contentModificationCapability,
821 null);
822
823
824 setDefaultAccessPermissions(
825 getSupplierProductTable().getTableInfo(),
826 contentAdditionCapability,
827 administerCapability(),
828 contentModificationCapability,
829 null);
830
831
832 setDefaultAccessPermissions(
833 getUserTable().getTableInfo(),
834 administerCapability(),
835 administerCapability(),
836 administerCapability(),
837 administerCapability());
838 }
839 });
840
841 if (bookStockingsInBackgroundAppropriate)
842 new BackgroundStockingsChecker().start();
843
844
845
846
847
848 }
849
850 private void setDefaultAccessPermissions(
851 TableInfo info,
852 Capability add,
853 Capability delete,
854 Capability write,
855 Capability read) {
856 if (info.getCancreate() == null)
857 info.setCancreate(add);
858 if (info.getDefaultcandelete() == null)
859 info.setDefaultcandelete(delete);
860 if (info.getDefaultcanwrite() == null)
861 info.setDefaultcanwrite(write);
862 if (info.getDefaultcanread() == null)
863 info.setDefaultcanread(read);
864 }
865
866 public IndexOther fti() {
867 return fti;
868 }
869
870 public IndexOther infoFTI() {
871 return infoFTI;
872 }
873
874 Library infoLibrary = new Library() {
875 public Text text(long textID) {
876 int a =
877 (int) ((textID & ((1l << Chapter.ftiTextID_section_shift) - 1))
878 >> Chapter.ftiTextID_author_shift);
879
880 long aMask = (1l << Chapter.ftiTextID_author_shift) - 1;
881
882 if ((textID & aMask) == aMask) {
883
884
885 try {
886 return getAuthorTable().getAuthorObject(new Integer(a));
887 } catch (NoSuchRowPoemException e) {
888
889 e.printStackTrace(System.err);
890 return null;
891 }
892 } else {
893
894 int b = (int) ((textID & aMask) >> Chapter.ftiTextID_book_shift);
895 return (Book)getBookTable().firstSelection(
896 "author = " + a + " AND " + "authorsequence = " + b);
897 }
898 }
899 };
900
901 public Library infoLibrary() {
902 return infoLibrary;
903 }
904
905 public Pagination pagination() {
906 return pagination;
907 }
908
909 public String getWorkspaceDir() {
910 return workspaceDir.getStringCooked();
911 }
912
913 public String getUploadDir() {
914 return uploadDir.getStringCooked();
915 }
916
917 public String getUploadURL() {
918 return uploadURL.getStringCooked();
919 }
920
921 public String getInfoFTIDir() {
922 return getWorkspaceDir() + infoFTIDir.getStringCooked();
923 }
924
925 public String getContentRootDir() {
926 return contentRootDir.getStringCooked();
927 }
928
929 private WebMacro _wm = null;
930
931 public WebMacro getWebMacro() {
932 if (_wm == null) {
933 synchronized (this) {
934 if (_wm == null) {
935 try {
936 _wm = new WM();
937 } catch (InitException e) {
938 throw new UnexpectedExceptionException(
939 "Initialising WebMacro for the pagination subsystem",
940 e);
941 }
942 }
943 }
944 }
945
946 return _wm;
947 }
948
949 public void
950 setupContext( Melati melati, Context context, Unit it, Hashtable extras) {
951
952 SectionGroup sectiongroup = it == null ? null : it.getReadArea();
953 context.put("sectiongroup", sectiongroup);
954 if (sectiongroup != null) {
955 context.put("areaColour", sectiongroup.getThemecolour());
956 } else {
957 context.put("areaColour", "#FF0000");
958 }
959
960 Section section = null;
961 Author author = null;
962 Book book = null;
963 Chapter chapter = null;
964
965 if (it != null) {
966 context.put("meta_description", it.getMetatag_description());
967 context.put("meta_keywords", it.getMetatag_keywords());
968
969 if (it instanceof Chapter) {
970 chapter = (Chapter)it;
971 book = chapter.getBook();
972 author = book.getAuthor();
973 section = book.getSection();
974 } else if (it instanceof Book) {
975 book = (Book)it;
976 author = book.getAuthor();
977 section = book.getSection();
978 } else if (it instanceof Author) {
979 author = (Author)it;
980 } else if (it instanceof Section) {
981 section = (Section)it;
982 }
983 }
984
985 context.put("object", it);
986 context.put("section", section);
987 context.put("author", author);
988 context.put("book", book);
989 context.put("chapter", chapter);
990 context.put("bib", getBib());
991 context.put("db", this);
992 context.put("melati", melati);
993 if (extras != null)
994 for (Enumeration k = extras.keys(); k.hasMoreElements();) {
995 String key = (String)k.nextElement();
996 context.put(key, extras.get(key));
997 }
998 }
999
1000 public final void setupContext(Melati melati, Context context, Unit it) {
1001 setupContext(melati, context, it, null);
1002 }
1003
1004 public void templateExpand(
1005 org.webmacro.Template template,
1006 OutputStream o,
1007 Unit object,
1008 Hashtable extras)
1009 throws WebMacroException, IOException {
1010 WebMacro wm = getWebMacro();
1011
1012 MelatiFastWriter fmw =
1013 new MelatiFastWriter(wm.getBroker(), o, getContentEncoding());
1014 Melati m = new Melati(new MelatiConfig(), fmw);
1015 Context context = wm.getContext();
1016 setupContext(m, context, object, extras);
1017 template.write(fmw.getPeer().getOutputStream(), context);
1018 fmw.flush();
1019
1020
1021
1022
1023
1024
1025
1026
1027 }
1028
1029 public final void templateExpand(
1030 org.webmacro.Template template,
1031 OutputStream o,
1032 Unit object)
1033 throws WebMacroException, IOException {
1034 templateExpand(template, o, object, null);
1035 }
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048 public static void notifyNewContentFile(File file) throws IOException {
1049 FileUtils.makeExecutable(file);
1050 }
1051
1052 public void templateExpandNamed(
1053 String templateName,
1054 File to,
1055 Unit object,
1056 Hashtable extras)
1057 throws WebMacroException, IOException {
1058 org.webmacro.Template template = getWebMacro().getTemplate(templateName);
1059 OutputStream o = new BufferedOutputStream(new FileOutputStream(to));
1060 try {
1061 templateExpand(template, o, object, extras);
1062 } finally {
1063 try {
1064 o.close();
1065 } catch (Exception e) {
1066 }
1067 }
1068
1069 notifyNewContentFile(to);
1070 }
1071
1072 public final void templateExpandNamed(
1073 String templateName,
1074 File to,
1075 Unit object)
1076 throws WebMacroException, IOException {
1077 templateExpandNamed(templateName, to, object, null);
1078 }
1079
1080 public void macroexpand(Reader i, OutputStream o, Unit object)
1081 throws WebMacroException, IOException {
1082 templateExpand(new StreamTemplate(getWebMacro().getBroker(), i), o, object);
1083
1084
1085 }
1086
1087 public void macroexpand(File from, OutputStream o, Unit object)
1088 throws WebMacroException, IOException {
1089 Reader i = new BufferedReader(new FileReader(from));
1090 try {
1091 macroexpand(i, o, object);
1092 } finally {
1093 try {
1094 i.close();
1095 } catch (Exception e) {
1096 }
1097 }
1098 }
1099
1100 public void macroexpand(File from, File to, Unit object)
1101 throws WebMacroException, IOException {
1102 OutputStream o = new BufferedOutputStream(new FileOutputStream(to));
1103 try {
1104 macroexpand(from, o, object);
1105 } finally {
1106 try {
1107 o.close();
1108 } catch (Exception e) {
1109 }
1110 }
1111 }
1112
1113 public static class TemplateException extends MelatiRuntimeException {
1114
1115
1116
1117 private static final long serialVersionUID = 1L;
1118 public File file;
1119
1120 public TemplateException(File file, Exception trouble) {
1121 super(trouble);
1122 this.file = file;
1123 }
1124
1125 public String getMessage() {
1126 return "Couldn't load templet from " + file + "\n" + subException;
1127 }
1128 }
1129
1130 private org.webmacro.Template template(String source) {
1131 File file = new File(getContentRootDir(), source);
1132
1133 org.webmacro.Template it =
1134 new FileTemplate(getWebMacro().getBroker(), file, getContentEncoding());
1135 try {
1136 it.parse();
1137 } catch (Exception e) {
1138 throw new TemplateException(file, e);
1139 }
1140
1141 return it;
1142 }
1143
1144 public void writeContentHeader(OutputStream w, Unit object, String template)
1145 throws WebMacroException, IOException {
1146 templateExpand(
1147 template(template == null ? "header.wm" : template),
1148 w,
1149 object);
1150 }
1151
1152 public void writeContentFooter(OutputStream w, Unit object, String template)
1153 throws WebMacroException, IOException {
1154 templateExpand(
1155 template(template == null ? "footer.wm" : template),
1156 w,
1157 object);
1158 }
1159
1160 private org.webmacro.Template footnoteTemplate = null;
1161
1162 public org.webmacro.Template getFootnoteTemplate() {
1163 if (footnoteTemplate == null)
1164 footnoteTemplate = template("footnote.wm");
1165
1166 return footnoteTemplate;
1167 }
1168
1169 public String getPasswordReminderMessage() {
1170 return passwordReminderMessage.getStringCooked();
1171 }
1172
1173 public org.webmacro.Template getPasswordReminderTemplate() {
1174 return new StreamTemplate(
1175 getWebMacro().getBroker(),
1176 new StringReader(getPasswordReminderMessage().trim()));
1177
1178 }
1179
1180 public String getPasswordReminderFrom() {
1181 return passwordReminderFrom.getStringCooked();
1182 }
1183
1184 public String getOrderEmailFrom() {
1185 return orderEmailFrom.getStringCooked();
1186 }
1187
1188 public String getConfirmationEmailFrom() {
1189 return confirmationEmailFrom.getStringCooked();
1190 }
1191
1192 public String getOrderEmailTo() {
1193 return orderEmailTo.getStringCooked();
1194 }
1195
1196 public String getStratusEmailTo() {
1197 return stratusEmailTo.getStringCooked();
1198 }
1199
1200 public String getCachedContentRootDir() {
1201 return getWorkspaceDir() + "/cached";
1202 }
1203
1204 public String getStaticRootURL() {
1205 return staticRootURL.getStringCooked();
1206 }
1207
1208 public String getContentStaticRootURL() {
1209 return contentStaticRootURL.getStringCooked();
1210 }
1211
1212 public String getBannerURLPath() {
1213 return bannerURLPath.getStringCooked();
1214 }
1215
1216 public String getCacheRootURL() {
1217 String it = cacheRootURL.getStringCooked();
1218 return it == null ? "" : it;
1219 }
1220
1221 public String getHomepageURL() {
1222 return homepageURL.getStringCooked();
1223 }
1224
1225 public String getSmtpServer() {
1226 return smtpServer.getStringCooked();
1227 }
1228
1229 public int getDefaultSearchHitsPerText() {
1230 Integer it = defaultSearchHitsPerText.getIntegerCooked();
1231 return it == null ? defaultDefaultSearchHitsPerText : it.intValue();
1232 }
1233
1234 public int getSearchHitsPerPage() {
1235 Integer it = searchHitsPerPage.getIntegerCooked();
1236 return it == null ? defaultSearchHitsPerPage : it.intValue();
1237 }
1238
1239 public String getPaginationTexHeader() {
1240 return paginationTexHeader.getStringCooked();
1241 }
1242
1243 public int getBookStockingsCheckIntervalDays() {
1244 Integer it = bookStockingsCheckIntervalDays.getIntegerCooked();
1245 return it == null ? defaultBookStockingsCheckIntervalDays : it.intValue();
1246 }
1247
1248 public long getBookStockingsCheckIntervalMillis() {
1249 return (long)getBookStockingsCheckIntervalDays() * 1000L * 60L * 60L * 24L;
1250 }
1251
1252 public int getBibBookBookshopSearchTimeoutSeconds() {
1253 Integer it = bibBookBookshopSearchTimeoutSeconds.getIntegerCooked();
1254 return it == null
1255 ? defaultBibBookBookshopSearchTimeoutSeconds
1256 : it.intValue();
1257 }
1258
1259 public long getBibBookBookshopSearchTimeoutMillis() {
1260 return (long)getBibBookBookshopSearchTimeoutSeconds() * 1000L;
1261 }
1262
1263 public int getBookStockingsOutputStartOffset() {
1264 Integer it = bookStockingsOutputStartOffset.getIntegerCooked();
1265 return it == null ? defaultBookStockingsOutputStartOffset : it.intValue();
1266 }
1267
1268 public String getBookStockingsCacheDir() {
1269 return bookStockingsCacheDir.getStringCooked();
1270 }
1271
1272 public int getBookStockingsCacheSizeMax() {
1273 Integer it = bookStockingsCacheSizeMax.getIntegerCooked();
1274 return it == null ? defaultBookStockingsCacheSizeMax : it.intValue();
1275 }
1276
1277 public Integer pageFromAnchor(String anchor) {
1278 if (anchor.startsWith(Pagination.pageAnchorPrefix))
1279 try {
1280 return new Integer(
1281 Integer.parseInt(
1282 anchor.substring(Pagination.pageAnchorPrefix.length()))
1283 + 1);
1284 } catch (NumberFormatException e) {
1285 return null;
1286 } else
1287 return null;
1288 }
1289
1290 public static class BookshopException extends MelatiRuntimeException {
1291
1292
1293
1294 private static final long serialVersionUID = 1L;
1295 public String which;
1296
1297 public BookshopException(String which, Exception e) {
1298 super(e);
1299 this.which = which;
1300 }
1301
1302 public String getMessage() {
1303 return "Something went wrong starting the "
1304 + which
1305 + " subsystem\n"
1306 + subException.getMessage();
1307 }
1308 }
1309
1310 private BookshopBackend bookshopBackendNamed(String packageComp) {
1311 try {
1312 return (
1313 (BookshopBackendFactory)Class
1314 .forName(
1315 "org.paneris.bibliomania.metasearch." + packageComp + ".Factory")
1316 .newInstance())
1317 .instance(
1318 new File(getWorkspaceDir()),
1319 0);
1320 } catch (Exception e) {
1321 throw new BookshopException(packageComp, e);
1322 }
1323 }
1324
1325 public Bookshop[] bookshops() {
1326 return bookshops;
1327 }
1328
1329 public SectionGroup getReadSectionGroup() {
1330 return readSectionGroup;
1331 }
1332
1333 public Currency getUKCurrency() {
1334 return UKCurrency;
1335 }
1336
1337 public SectionGroup getStudySectionGroup() {
1338 return studySectionGroup;
1339 }
1340
1341 public SectionGroup getResearchSectionGroup() {
1342 return researchSectionGroup;
1343 }
1344
1345 public SectionGroup getShopSectionGroup() {
1346 return shopSectionGroup;
1347 }
1348
1349 public SectionGroup getSearchSectionGroup() {
1350 return searchSectionGroup;
1351 }
1352
1353 public Group getRegisteredUserGroup() {
1354 return registeredUserGroup;
1355 }
1356
1357 public Capability getRegisteredUserCapability() {
1358 return registeredUserCapability;
1359 }
1360 public Capability getContentModificationCapability() {
1361 return contentModificationCapability;
1362 }
1363 public Capability getContentAdditionCapability() {
1364 return contentAdditionCapability;
1365 }
1366
1367 public User getTemplateRegisterUser() {
1368 return templateRegisterUser;
1369 }
1370
1371 private Bib bib = null;
1372
1373 public Bib getBib() {
1374 if (bib == null)
1375 bib = new Bib(this);
1376 return bib;
1377 }
1378
1379 private int bookshopSearchesRunning = 0;
1380
1381 public void notifyBookshopSearchStart() {
1382 ++bookshopSearchesRunning;
1383 }
1384
1385 public void notifyBookshopSearchStop() {
1386 --bookshopSearchesRunning;
1387 }
1388
1389 public boolean okToRunABookshopSearch() {
1390 Integer maxO = bookStockingsCheckConcurrentMax.getIntegerCooked();
1391 int max =
1392 maxO == null ? defaultBookStockingsCheckConcurrentMax : maxO.intValue();
1393
1394 return bookshopSearchesRunning < max;
1395 }
1396
1397 public boolean getBookStockingsInBackground() {
1398 return bookStockingsInBackgroundAppropriate
1399 && Boolean.TRUE.equals(bookStockingsInBackground.getBooleanCooked());
1400 }
1401
1402 public String getContentEncoding() {
1403 return contentEncoding.getStringCooked();
1404 }
1405
1406 private class BackgroundStockingsChecker extends Thread {
1407
1408 private PreparedStatementFactory booksPS, booksPSNull;
1409
1410 BackgroundStockingsChecker() {
1411 booksPSNull =
1412 new PreparedStatementFactory(
1413 BibliomaniaDatabase.this,
1414 "SELECT book.id FROM book WHERE lastbookshopsearch IS NULL AND "
1415 + "(hasnofrontpage IS NULL OR NOT hasnofrontpage) AND "
1416 + "(author.id = book.author AND "
1417 + "(author.nonstandard IS NULL OR NOT author.nonstandard)) "
1418 + "LIMIT 1");
1419
1420 booksPS =
1421 new PreparedStatementFactory(
1422 BibliomaniaDatabase.this,
1423 "SELECT id FROM book WHERE lastbookshopsearch < ? AND "
1424 + "(hasnofrontpage IS NULL OR NOT hasnofrontpage) AND "
1425 + "(author.id = book.author AND "
1426 + "(author.nonstandard IS NULL OR NOT author.nonstandard)) "
1427 + "ORDER BY lastbookshopsearch "
1428 + "LIMIT 1");
1429 }
1430
1431 private void doNext() throws Exception {
1432 inSession(AccessToken.root, new PoemTask() {
1433 public void run() {
1434 Book nextToDo;
1435 PreparedStatement ps = null;
1436
1437 try {
1438 if (!getBookStockingsInBackground())
1439 nextToDo = null;
1440 else {
1441 Integer bookTroid = null;
1442
1443 ps = booksPSNull.preparedStatement(PoemThread.transaction());
1444 ResultSet rs = ps.executeQuery();
1445 try {
1446 if (rs.next())
1447 bookTroid = new Integer(rs.getInt(1));
1448 } finally {
1449 rs.close();
1450 }
1451
1452 if (bookTroid == null) {
1453 ps = booksPS.preparedStatement(PoemThread.transaction());
1454 ps.setTimestamp(
1455 1,
1456 new Time