001    /*
002    Copyright (c) 1996-2012, Damon Hart-Davis
003    All rights reserved.
004    
005    Redistribution and use in source and binary forms, with or without
006    modification, are permitted provided that the following conditions are
007    met:
008    
009      * Redistributions of source code must retain the above copyright
010        notice, this list of conditions and the following disclaimer.
011    
012      * Redistributions in binary form must reproduce the above copyright
013        notice, this list of conditions and the following disclaimer in the
014        documentation and/or other materials provided with the
015        distribution.
016    
017    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
018    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
019    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
020    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
021    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
022    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
023    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
024    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
025    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
026    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
027    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028    */
029    package org.hd.d.pg2k.webSvr.util;
030    
031    import org.hd.d.pg2k.svrCore.CoreConsts;
032    import org.hd.d.pg2k.svrCore.ExhibitThumbnails;
033    import org.hd.d.pg2k.svrCore.GenUtils;
034    import org.hd.d.pg2k.svrCore.Rnd;
035    
036    /**Web-server-related constants.
037     * This is for constants only of interest to Web apps.
038     */
039    public final class WebConsts
040        {
041        /**Prevent construction of an instance. */
042        private WebConsts() { }
043    
044        /**Web/Net standard line-end string; CRLF. */
045        public static final String WEB_LINE_END = "\r\n";
046    
047        /**If true then try very hard to push out the first page a user sees on our site(s) ASAP.
048         * <a href="http://news.bbc.co.uk/2/hi/technology/4616700.stm" target="_top">20050116: BBC News suggests</a>
049         * that visitors make up their minds about a new Web site within 50ms,
050         * so we will try to push out the first page
051         * as fast as possible, preferably within this time.
052         */
053        public static final boolean FIRST_PAGE_AFAP = true;
054    
055        /**Mount point for whole WAR tree on J2EE master servers.
056         * Note the leading ``/''.
057         * <p>
058         * On deployed front-ends the root should always be ``/'';
059         * this applies to a WAR deployed as part of a J2EE app,
060         * which cannot currently be deployed at the root of the Web
061         * server (due to bugs in, for example, Sun's 1.3 RI J2EE SDK.
062         */
063        public static final String MASTER_CONTEXT_ROOT = "/master";
064    
065        /**Name of base of exhibits area below the WAR root; no leading or trailing `/'. */
066        public static final String BASE_PATH_EXHIBITS = "_exhibits";
067        /**Name of base of features area below the WAR root; no leading or trailing `/'. */
068        public static final String BASE_PATH_FEATURES = "_features";
069        /**Name of base of admin area below the WAR root; no leading or trailing `/'. */
070        public static final String BASE_PATH_ADMIN = "_admin";
071        /**Name of base of catalogue-page area below the WAR root; no leading or trailing `/'. */
072        public static final String BASE_PATH_CATPAGE = "_c";
073        /**Name of base of thumbnail/sample area below the WAR root; no leading or trailing `/'. */
074        public static final String BASE_PATH_TN = "_tn";
075        /**Name of base of static/cacheable content below the WAR root; no leading or trailing `/'. */
076        public static final String BASE_PATH_STATIC = "_static";
077        /**Name of standard thumbnail/sample area below BASE_PATH_TN; no leading or trailing `/'. */
078        public static final String PATH_TN_STD = "std";
079        /**Name of small thumbnail/sample area below BASE_PATH_TN; no leading or trailing `/'. */
080        public static final String PATH_TN_SML = "sml";
081    
082        /**Root-based path from which latest available Javadoc bundle may be downloaded. */
083        public static final String PATH_JAVADOC_BUNDLE_LATEST = "/javadoc.zip";
084        /**Root-based path from which latest available code snapshot bundle may be downloaded. */
085        public static final String PATH_CODE_SNAPSHOT_LATEST = "/src.snapshot.tbz2";
086    
087        /**Prefix of Javadoc bundle in code section. */
088        public static final String PREFIX_JAVADOC_BUNDLE = "PG2K-javadoc";
089        /**Prefix of code snapshot in code section. */
090        public static final String PREFIX_CODE_SNAPSHOT = "PG2K-src-snapshot";
091    
092    
093        /**Suffix for secondary, generated HTML objects, including ``.''.*/
094        public static final String F_secondary_generated_HTML_suffix = ".html";
095    
096        /**Suffix for secondary, generated WML objects, including ``.''. */
097        public static final String F_secondary_generated_WML_suffix = ".wml";
098    
099        /**Suffix for secondary, generated XHTML objects, including ``.''. */
100        public static final String F_secondary_generated_XHTML_suffix = ".xhtml";
101    
102        /**Suffix for secondary, generated XML objects, including ``.''. */
103        public static final String F_secondary_generated_XML_suffix = ".xml";
104    
105        /**HTML welcome page for a directory. */
106        public static final String F_HTML_welcome_page = "index" + F_secondary_generated_HTML_suffix;
107    
108        /**Target limit for page-download time in ms; strictly positive.
109         * Less than 1s total, in part because users want a snappy response,
110         * and in part because our real-time I/O monitor regards an interval of 1s or more
111         * where at least one page delivery has remained blocked other than by network I/O
112         * as a 'failure' by the server and indicating that the server is too busy.
113         * <p>
114         * Eventual target for this is 100ms nominally including network RTT,
115         * which implies getting user to a geographically-close mirror
116         * which would further imply DNS support for the first hit.
117         */
118        public static final int MAX_PG_DOWNLOAD_MS = 512;
119    
120        /**General (client/proxy) page-cache time, including catalogue pages (ms); strictly positive.
121         * This can be used for positive and negative cacheing, and can be of
122         * the order of a few minutes (much, much shorter than the timeframe
123         * of changes in the exhibit collection [typically days]), and long
124         * enough to cover typical use of the BACK button.
125         * <p>
126         * This should be much shorter than (eg less than half) the system's
127         * default parallel slackness so as to ensure that the page gets rechecked
128         * often enough to catch important changes automatically.
129         * <p>
130         * A big-enough value will help caches/proxies and "Web accelerators",
131         * and generally improve perceived performance.
132         */
133        public static final int DEFAULT_PAGE_CACHE_MS = CoreConsts.DEFAULT_TEMPORAL_SLACKNESS_S * 456;
134    
135        /**Maximum non-spider/SE cache time for normal pages (ms); strictly positive.
136         * This can be used to build an extra component for ETags and so on
137         * to limit cache life to allow for changes in other parts of page content than the exhibit,
138         * such as page layout and ads.
139         * <p>
140         * Should be no lower than DEFAULT_PAGE_CACHE_MS and of the order of a day or so,
141         * but not necessarily exact multiples of a day to help spread refresh activity.
142         */
143        public static final int MAX_NORMAL_PAGE_CACHE_MS = Math.max(25*3600*1000, DEFAULT_PAGE_CACHE_MS);
144    
145        /**Power of two (bit shift) to get value close below MAX_NORMAL_PAGE_CACHE_MS. */
146        public static final int MAX_NORMAL_PAGE_CACHE_MS_SHIFT = GenUtils.log2Approx(MAX_NORMAL_PAGE_CACHE_MS);
147    
148        /**Cache/expiry time for pages we want captured in search engines (ms); strictly positive.
149         * We set a long expiry to encourage search engines to index the page;
150         * of the order of days to months is probably right.
151         * <p>
152         * This may force HTTP/1.0-browser users (esp IE5/IE6) to hit RELOAD
153         * more often than we'd like...
154         * <p>
155         * We may use this value only when the page/data looks like it is
156         * being retrieved by a search engine spider/robot.
157         * <p>
158         * A value of several days to several months is probably reasonable.
159         */
160        public static final long SPIDER_PAGE_EXPIRY_MS = 183 * (24 * 60 * 60 * 1000L); // ~6M.
161    
162        /**Spider revisit time in days; if zero it means not to attempt to use the "revisit-after" meta tag.
163         * This tag is reported to be obsolete.
164         */
165        public static final int SPIDER_REVISIT_DAYS = 0; // Math.max(1, (int) (SPIDER_PAGE_EXPIRY_MS / (24 * 60 * 60 * 1000L)));
166    
167        /**Default time to allow client to cache unchanging and heavily-used objects under /_static and elsewhere.
168         * These are usually static items of "page furniture",
169         * and allowing them to be served from cache for a long time
170         * improves perceived performance and reduces server load.
171         * <p>
172         * There is no point having this less than the default respidering time.
173         * <p>
174         * Note that the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21">HTTP spec</a> says that an expiry a year in the future
175         * is to be treated as 'never expires':
176         * <blockquote>
177         * To mark a response as "never expires," an origin server sends an Expires date approximately one year from the time the response is sent.
178         * HTTP/1.1 servers SHOULD NOT send Expires dates more than one year in the future.
179         * </blockquote>
180         * <p>
181         * Something of the order of many days up to a year is probably good.
182         */
183        public static final long DEFAULT_STATIC_WEBITEMS_CACHE_MS = 366 * 24 * 3600 * 1000L; // ~1Y.
184    
185        /**Minimum time to allow client to cache unchanging and heavily-used objects under /_static and elsewhere.
186         * These are usually static items of "page furniture",
187         * and allowing them to be served from cache for a long time
188         * improves perceived performance and reduces server load.
189         * <p>
190         * Something of the order of a month or more is probably good.
191         */
192        public static final long MIN_STATIC_WEBITEMS_CACHE_MS = 32 * 24 * 3600 * 1000L; // >1M.
193    
194        /**Name of application-scope Boolean attribute, if any, to indicate bandwidth/CPU overload.
195         * If present, we expect this to be set when the server is very busy by
196         * a throughput filter/monitor to FALSE when not busy and TRUE when busy.
197         */
198        public static final String BANDWIDTH_OVERLOAD_ATTR_NAME = "org.hd.d.pg2k.highTraffic";
199    
200        /**Name of application-scope Boolean attribute, if any, to indicate bandwidth/CPU light load.
201         * If present, we expect this to be set when the server is fairly quiet by
202         * a throughput filter/monitor to TRUE when quiet and FALSE otherwise.
203         */
204        public static final String BANDWIDTH_LIGHTLOAD_ATTR_NAME = "org.hd.d.pg2k.lowTraffic";
205    
206    
207        /**Name of request-scope Long attribute, if any, to suppress default Last-Modified header.
208         * Avoids automated generation of AEP-derived Last-Modified header
209         * for a page where it doesn't make sense,
210         * eg for dynamically-generated partly-randomised page content
211         * which should rotate for the user even when the AEP doesn't change.
212         * <p>
213         * Can also be used to adjust apparent page age to reduce spidering/SE load.
214         * <p>
215         * If not -1 then will be used as the Last-Modified value, subject to constraints.
216         */
217        public static final String CCLM_SUPPRESS_ATTR_NAME = "CCLM_SUPPRESS";
218    
219        /**Name of request-scope String attribute, if any, to suppress default ETag header.
220         * Avoids automated generation of AEP-derived ETag header
221         * for a page where it doesn't make sense,
222         * eg for dynamically-generated partly-randomised page content
223         * which should rotate for the user even when the AEP doesn't change.
224         * <p>
225         * If not "" (the empty string) then well be used as the ETag value.
226         */
227        public static final String CCET_SUPPRESS_ATTR_NAME = "CCET_SUPPRESS";
228    
229    
230        /**Default name of bandwidth-limit system property (bytes per second). */
231        public static final String BANDWITDH_LIMIT_BPS_DEFAULT_PNAME = "org.hd.d.pg2k.maxBytesPerSec";
232    
233        /**Default name of bandwidth-limit system property (gigabytes per 30 days). */
234        public static final String BANDWITDH_LIMIT_GBP30D_DEFAULT_PNAME = "org.hd.d.pg2k.maxGBytesPer30D";
235    
236    
237        /**Minimum percent-saved before we will consider sending an exhibit compressed.
238         * If (say) 5 then we must know a particular exhibit is compressible to
239         * no more than 95% of its original size before we will consider
240         * sending it compressed over the wire (eg when the site is busy).
241         * <p>
242         * Normally we don't bother trying to compress exhibits for download
243         * because:
244         * <ul>
245         * <li>Most of them are already very efficiently encoded,
246         *     ie hardly compressible,
247         *     and the extra CPU time at our end is probably unjustified.
248         * <li>We risk tripping up browsers with the non-standard encoding.
249         * <li>Any resend or byte-range fetch may be more difficult to handle.
250         * </ul>
251         */
252        public static final int MIN_PC_COMPRESSABLE_BEFORE_ENCODE = 5;
253    
254    
255        /**Maximum length of simple-text-query in bytes (pure-ASCII characters). */
256        public static final int MAX_SIMPLE_QUERY_LEN = 128;
257    
258    
259        /**In catalogue entries display spaces instead of hyphens.
260         * This is mainly needed because Netscape 4.x and 6.x refuses to
261         * correctly hyphenate inside nbr section seven when heavily
262         * prompted with wbr tags!
263         */
264        public static final boolean DISPLAY_SPACE_IN_EXHIBIT_NAMES = true;
265    
266    
267        /**If true, allow browsers to scale JPEG/GIF images when it saves us significant work. */
268        public static final boolean ALLOW_BROWSER_IMAGE_SCALE = true;
269    
270        /**If true, we force thumbnails to be made if we can use them in (catalogue) pages. */
271        public static final boolean EAGER_TN_USE = true;
272    
273    
274        /**Optional attributes for insertion at end of thumbnail img tags.
275         * Can be "", so that the img tag will have the basic minimal/default
276         * behaviour, or something like " border=0" to turn off
277         * a visible border if the image is clickable (note the leading space).
278         */
279        public static final String TN_IMG_TAG_EXTRA_ATTR = " border=\"0\"";
280    
281        /**Method by which search form is posted; can be "post" or "get". */
282        public static final String SEARCH_FORM_METHOD = "get";
283    
284        /**URL of page/JSP to handle 404 (not found) errors.
285         * Given as an absolute URI to work from any context (current URI).
286         */
287        public static final String NOT_FOUND_PAGE = "/notFound.jsp";
288    
289        /**URL of URI requested by spiders to check if spidering is allowed..
290         * Given as an absolute URI to work from any context (current URI).
291         */
292        public static final String ROBOTS_PAGE = "/robots.txt";
293    
294    
295        /**Name of (page-level) attribute in which last search time (ms) is stored (as a Long).
296         * If null or a negative value (or not a Long) then no search has been done
297         * through the standard mechanisms.
298         */
299        public static final String SEARCH_TIME_MS_ATTR_NAME = "SEARCH_TIME_MS";
300    
301        /**Name of (request-level) attribute in which JSP page start time is stored (as a Date). */
302        public static final String REQUEST_START_DATETIME_ATTR_NAME = "JSP_REQUEST_START_TIMEDATE";
303    
304    
305        /**When generating large chunks of paginatable output, how many to limit each page to where possible.
306         * A value of 10 to 20 is typical.
307         */
308        public static final int MAX_RESULTS_PER_PAGE = 10;
309    
310        /**Maximum number of pages to show, or maximum number of page buttons if there have to be more pages.
311         * Should maybe be a fraction of page width?
312         * <p>
313         * A value of 10 to 20 is typical.
314         */
315        public static final int MAX_RESULTS_PAGES = 15;
316    
317        /**Request-level attribute name to request no further redirection/forwarding (if attr non-null). */
318        public static final String ATTR_NOFWD = "nofwd";
319    
320        /**Request-level attribute name used to communicate breadcrumbs (if any) to standard page-header routine. */
321        public static final String STDHDR_ATTR_NAME_BREADCRUMBS = "breadcrumbs";
322        /**Request-level attribute name used to communicate title String to standard page-header routine.
323         * Title text must be short and 7-bit printable ASCII possibly containing HTML entity codes.
324         */
325        public static final String STDHDR_ATTR_NAME_TITLE = "title";
326        /**Request-level attribute name used to communicate sub-title String to standard page-header routine.
327         * Sub-title text must be short and 7-bit printable ASCII possibly containing HTML entity codes.
328         * <p>
329         * May be ignored if title is not set.
330         */
331        public static final String STDHDR_ATTR_NAME_SUBTITLE = "subTitle";
332        /**Request-level attribute name used to communicate keywords String to standard page-header routine.
333         * Sub-title text must be short and 7-bit printable ASCII possibly containing HTML entity codes.
334         * <p>
335         * May be ignored if title is not set.
336         */
337        public static final String STDHDR_ATTR_NAME_KEYWORDS = "keywords";
338    
339        /**Request-level attribute name used to communicate exhibit Location.Base to standard page-header routine.
340         */
341        public static final String STDHDR_ATTR_NAME_LOCATION = "location";
342    
343        /**Request-level attribute name used to indicate that this low-content page should not be SE food to standard page-header routine.
344         * If set non-null then we may wish to insert a robots noindex meta tag,
345         * and we may also want to suppress some or all ads on the page.
346         * <p>
347         * This may be the case for low-content pages, such as navigation pages.
348         * <p>
349         * The "safe" default is to let the SEs index everything
350         * and make the quality/relevance judgements!
351         */
352        public static final String STDHDR_ATTR_NAME_NOINDEX = "pg2k.page.noindex";
353    
354        /**If true then we make use of the STDHDR_ATTR_NAME_NOINDEX mechanism. */
355        public static final boolean USE_NOINDEX_ON_LOW_CONTENT_PAGES = true;
356    
357        /**Request-level attribute name used to indicate that this page should not be SE food to standard page-header routine.
358         * If set non-null then we may wish to insert a robots noindex meta tag,
359         * but leave normal ads (etc) on the page.
360         * <p>
361         * This may be the case for pages with good unique content,
362         * but where from the point of view of an SE that content might appear to be duplicate.
363         * This also applies where we'd prefer SE-driven traffic to arrive elsewhere.
364         * <p>
365         * The "safe" default is to let the SEs index everything
366         * and make the quality/relevance judgements!
367         */
368        public static final String STDHDR_ATTR_NAME_NOINDEX_ONLY = "pg2k.page.noindexonly";
369    
370        /**If true then we make use of the STDHDR_ATTR_NAME_NOINDEX_ONLY mechanism. */
371        public static final boolean USE_NOINDEX_ON_DUP_CONTENT_PAGES = true;
372    
373    
374        /**When paginating, name of parameter (HTML fieldname and GET/POST field name) to pass page number in.
375         * We keep this short because it gets used lots of times, sometimes many times per page.
376         */
377        public static final String PAGE_NUMBER_PARAMETER = "pg";
378    
379        /**Actual front-page URI / rooted URL (starting with '/'). */
380        public static final String HOME_URL_ACTUAL = "/index.jsp";
381        /**Canonical front-page URI / rooted URL (starting with '/'). */
382        public static final String HOME_URL_CANON = "/";
383    
384        /**URI of JSP page that handles display of browsable exhibit trees. */
385        public static final String BROWSABLE_EXHIBIT_TREE_DISPLAY_JSP = "/_cat/doHTMLTree.jsp";
386        /**Name of the (request-level) attribute used to pass the (unmodifiable, sorted) List of exhibits to BROWSABLE_EXHIBIT_TREE_DISPLAY_JSP. */
387        public static final String BETDJ_LIST_ATTR_NAME = "org.hd.d.pg2k.BETDJ_LIST_ATTR";
388        /**Name of the (request-level) attribute used to pass the boolean array of uninteresting parent nodes to BROWSABLE_EXHIBIT_TREE_DISPLAY_JSP. */
389        public static final String BETDJ_LIST_UNIN_NAME = "org.hd.d.pg2k.BETDJ_UNIN_ATTR";
390        /**Name of the (request-level) attribute used to pass (unmodifiable, CharSequence-to-Integer) Map of sub-nodes to exhibit counts BROWSABLE_EXHIBIT_TREE_DISPLAY_JSP. */
391        public static final String BETDJ_LIST_SNEXCOUNT_NAME = "org.hd.d.pg2k.BETDJ_SNEXCOUNT_ATTR";
392        /**Name of the (request-level) attribute used to pass the immutable CharSequence base absolute URI of the tree to BROWSABLE_EXHIBIT_TREE_DISPLAY_JSP. */
393        public static final String BETDJ_BASE_URI_ATTR_NAME = "org.hd.d.pg2k.BETDJ_BASE_URI_ATTR";
394        /**Name of the (request-level) attribute used to pass the immutable CharSequence title of the tree to BROWSABLE_EXHIBIT_TREE_DISPLAY_JSP. */
395        public static final String BETDJ_TITLE_ATTR_NAME = "org.hd.d.pg2k.BETDJ_TITLE_ATTR";
396        /**Name of the (request-level) attribute used to pass the immutable CharSequence prefix word sequence of the tree to BROWSABLE_EXHIBIT_TREE_DISPLAY_JSP. */
397        public static final String BETDJ_PRWSEQ_ATTR_NAME = "org.hd.d.pg2k.BETDJ_PRWSEQ_ATTR";
398        /**Name of the (request-level) attribute used to pass the String HTML general tree description to BROWSABLE_EXHIBIT_TREE_DISPLAY_JSP.
399         * This is allowed to contain structural elements such as headings for now,
400         * though in future might not be allowed to.
401         */
402        public static final String BETDJ_HTML_GENERAL_DESC_ATTR_NAME = "org.hd.d.pg2k.BETDJ_HTML_GENERAL_DESC_ATTR";
403        /**Name of common section/category dir (immutable CharSequence) for all exhibits in the view, or null if none or not known. */
404        public static final String BETDJ_COMMON_SECTION = "org.hd.d.pg2k.BETDJ_COMMON_SECTION";
405    
406        /**Root of "virtual" collections tree, starting and ending with "/". */
407        public static final String VIRTUAL_COLLECTIONS_ROOT = "/_virtual/";
408    
409        /**Root of "ByAuthor" collections forest, ending with "/". */
410        public static final String VIRTUAL_COLLECTIONS_BYAUTHOR_ROOT = VIRTUAL_COLLECTIONS_ROOT + "ByAuthor/";
411    
412        /**Root of "ByCategory" collections forest, ending with "/". */
413        public static final String VIRTUAL_COLLECTIONS_BYCATEGORY_ROOT = VIRTUAL_COLLECTIONS_ROOT + "ByCategory/";
414    
415        /**Root of custom icons collection, ending with "/". */
416        public static final String VIRTUAL_COLLECTIONS_CUST_ICONS_ROOT = VIRTUAL_COLLECTIONS_ROOT + "Cust_icons/";
417    
418        /**Root of trails, ending with "/". */
419        public static final String VIRTUAL_COLLECTIONS_TRAILS_ROOT = VIRTUAL_COLLECTIONS_ROOT + "trails/";
420    
421        /**Best-exhibits collection page. */
422        public static final String VIRTUAL_COLLECTIONS_BEST_PAGE = VIRTUAL_COLLECTIONS_ROOT + "best.jsp";
423    
424        /**New-exhibits collection page. */
425        public static final String VIRTUAL_COLLECTIONS_NEW_PAGE = VIRTUAL_COLLECTIONS_ROOT + "new.jsp";
426    
427        /**Most-downloaded-exhibits collection page. */
428        public static final String VIRTUAL_COLLECTIONS_MOST_DOWNLOADED_PAGE = VIRTUAL_COLLECTIONS_ROOT + "downloads.jsp";
429    
430        /**Base of custom sites tree, starting and ending with "/". */
431        public static final String VIRTUAL_SITES_ROOT = "/_site/";
432    
433        /**Root of custom LondonPix site, starting and ending with "/". */
434        public static final String VIRTUAL_SITE_CUST_LONDONPIX_ROOT = VIRTUAL_SITES_ROOT + "LondonPix/";
435    
436        /**Root of custom LondonPix site, starting and ending with "/". */
437        public static final String VIRTUAL_SITE_CUST_ZAPIX_ROOT = VIRTUAL_SITES_ROOT + "ZAPix/";
438    
439        /**Root of custom AlohaEarth site, starting and ending with "/". */
440        public static final String VIRTUAL_SITE_CUST_ALOHAEARTH_ROOT = VIRTUAL_SITES_ROOT + "AlohaEarth/";
441    
442        /**Root of custom SmlPx site, starting and ending with "/". */
443        public static final String VIRTUAL_SITE_CUST_SMLPX_ROOT = VIRTUAL_SITES_ROOT + "SmlPx/";
444    
445    
446        /**Actual root welcome page of custom SmlPx site. */
447        public static final String VIRTUAL_SITE_CUST_SMLPX_ROOT_ACTUAL = VIRTUAL_SITE_CUST_SMLPX_ROOT + "index.jsp";
448    
449    
450        /**Name of request parameter used to select trail (by trail exhibit short name). */
451        public static final String TRAIL_SELECTOR_PARAM_NAME = "trailname";
452    
453    
454        /**Root of "features" collections tree, starting and ending with "/". */
455        public static final String FEATURE_COLLECTIONS_ROOT = "/" + BASE_PATH_FEATURES + "/";
456    
457    
458        /**Stats-sink receiver page root-relative URL. */
459        public static final String STATS_SINK_PAGE_RRURL = "/statsSink.jsp";
460    
461    
462        /**Most thumbnails to show on one of the best/new/etc collections pages; strictly positive.
463         * The value chosen works nicely with the default page and lightbox layout,
464         * with a protective safety margin.
465         */
466        public static final int SINGLE_PAGE_CONTACT_SHEET_TN_COUNT = 80;
467    
468    
469        /**If true have the catalogue page generic handler use jsp:forward where possible to get to specific handler. */
470        public static final boolean CAT_HANDLER_USE_JSP_FORWARD = false;
471    
472    
473        /**Expected typical mean bandwidth per active/sticky user, bytes per second; strictly positive.
474         * We may override this default value from (say) GenProps
475         * or from collected stats.
476         * <p>
477         * Not expected to be terribly precise,
478         * but should err a little on the high side.
479         * <p>
480         * Used, amongst other things, for a server to estimate its own capacity.
481         * <p>
482         * As of 20050510:
483         * <ul>
484         * <li>about 4kBps per visitor that views more than one page;
485         * <li>about 1300Bps per distinct class-C IP block of all users of the site including hotlinkers.
486         * </ul>
487         * <p>
488         * As of 20060116: revising per-sticky-user estimate downwards,
489         * but all spiders and hotlinkers (etc) add significant per-server load,
490         * so a simple overall estimate of 2kBps is possibly more accurate.
491         */
492        public static final int DEFAULT_EXPECTED_USER_BW_BYTESPERSEC = 2048;
493    
494    
495        /**If true, attempt to break out of third-party HTML frames.
496         * We may want to do this to stop people accidentally
497         * (or deliberately, to steal revenue, for example)
498         * wrapping our pages,
499         * though this may break Google search's cacheing features for example.
500         */
501        public static final boolean DO_FRAME_BREAKOUT = true;
502    
503        /**If true, enable Urchin (ie Google) site stats tracking. */
504        public static final boolean ENABLE_URCHIN_STATS = true;
505    
506        /**Request-level attribute set non-null when Urchin code has already been set in the HEAD or page text. */
507        public static final String URCHIN_REQ_ATTR_NAME = "urchinOn";
508    
509        /**If true, enable recording (eg via Urchin) of when a user becomes 'sticky'. */
510        public static final boolean ENABLE_STICKY_HIT = false;
511    
512    
513        /**Unique prefix for data-point name for vote StatsSink IDs. */
514        public static final String VOTER_DATA_POINT_PREFIX = "VOTER-";
515    
516        /**Minimum time between requests to one user to vote, and also how long they get to vote, ms; non-negative.
517         * Small values may help shut out spiders and collect more votes,
518         * but at an increased risk of annoying users
519         * and of discarding votes from thoughtful/slow users.
520         * <p>
521         * A value of the order of minutes is usually suitable.
522         * <p>
523         * Once a user has voted we should generally wait many times as long
524         * before requesting another vote
525         * eg in order to suppress false votes from spiders
526         * that we otherwise fail to screen out.
527         */
528        public static final int VOTE_MIN_REQUEST_GAP_MS = 7 * 60 * 1000 +
529                Rnd.fastRnd.nextInt(3 * 60 * 1000);
530    
531        /**If true, use JavaScript to attempt to auto-hide vote buttons (roughly) when the vote permit expires. */
532        public static final boolean VOTE_BUTTONS_AUTO_HIDE_ON_EXPIRY = true;
533    
534    
535        /**Name of 15x15 "smile"/"pro"/"for" image within static-icon area. */
536        public static final String PRO_ICON_15x15_NAME = "smile.gif";
537        /**Name of 15x15 "frown"/"con"/"against" image within static-icon area. */
538        public static final String CON_ICON_15x15_NAME = "frown.gif";
539    
540        /**If true then allow users to be pushed to a local country mirror.
541         * To avoiding bouncing people around
542         * (eg in case of inconsistent algorithm versions)
543         * or making wrong guesses about which is the "best" machine for a user,
544         * and overly polluting the users' minds with the non-canonical name,
545         * any such "bouncing" has to be very conservative.
546         * <p>
547         * Typically only done if:
548         * <ul>
549         * <li>The user has no extant session (so no state to lose when bounced).
550         * <li>The user is on the generic URL, ie no explicit mirror URL.
551         * <li>The user is not already on a server "close" to them (eg same continent).
552         * <li>There is a same-country mirror "better" than the current server
553         *     for this user, and it is not too busy.
554         * <li>Possibly Only do this for text pages to avoid bouncing
555         *     (for example) thumbnail requests that must go to the same host.
556         * <li>Possibly done for only a fraction of visitors
557         *     and possibly only on selected pages so that
558         *     if the user cannot reach the "better" host
559         *     then they can "stick" to the generic one by doing a reload/refresh.
560         *     Users should always be able to get to the home page without bouncing.
561         * </ul>
562         * <p>
563         * Any such redirection would be temporary (302) and not done to spiders/bots.
564         */
565        public static final boolean ALLOW_BOUNCE_TO_LOCAL_MIRROR = true;
566    
567    
568        /**Recommended/widely-supported screen-width for .mobi devices/sites, in pixels. */
569        public static final int MOBI_REC_DEFAULT_SCREEN_WIDTH_PX = 120;
570    
571        /**Recommended maximum page weight for .mobi devices/sites, in bytes. */
572        public static final int MOBO_REC_MAX_PG_WEIGHT_BYTES = 20 * 1000;
573    
574    
575        /**Number of teaser thumbnails to show on front-page banner strip; strictly positive.
576         * This is constrained by the width of the main part of the page.
577         */
578        public static final int HOME_PAGE_TEASER_BANNER_IMAGE_COUNT = (PageSkinUtils.GENERIC_WIDTH_PX*2) / (ExhibitThumbnails.SML_STATIC_IMAGE_TN_LDIM_PX*3);
579    
580    
581        /**If true then we canonicalise tree-display URIs to try to reduce state-space with 301/302 redirects.
582         * We do this to reduce the number of different pages (and URI lengths)
583         * that SEs and end-users may need/try to fetch and cache,
584         * without reducing the actual information available.
585         * <p>
586         * This should reduce noise and server load!
587         * <p>
588         * We use 301 redirects to deal with non-standard URL forms,
589         * eg missing a trailing '/' or not monocased,
590         * since the duplicates that we might otherwise accept never have independent value.
591         * <p>
592         * We use 302 redirects to send the client to a higher node (shorter URI)
593         * that contains the same information.  This can only be a temporary redirect
594         * because the tree shape (and thus the/any redirection)
595         * may change at any time in principle.
596         */
597        public static final boolean CANON_TREE_URIS_WITH_REDIR = true;
598    
599    
600        /**If true then attempt to redirect mobile-phone browsers hitting front page to XHTML version. */
601        public static final boolean REDIR_MOBILES_AT_ROOT_TO_XHTML = true;
602    
603    
604        /**If true, support Hoverbox CSS-based large-thumbnail rollovers.
605         * Hoverbox c/o Nathan Smith http://host.sonspring.com/hoverbox/
606         */
607        public static final boolean SUPPORT_HOVERBOX = true;
608    
609        /**If true, support "Reddit It | Digg This | Add to del.icio.us" style links. */
610        public static final boolean SUPPORT_USER_LINK_POSTING = false;
611    
612        /**If true, insert teaser for trails on front page and possibly elsewhere. */
613        public static final boolean SHOW_FP_TRAILS_TEASER = false;
614    
615        /**Private CDN hostname for more valuable content.
616         * This may be an alias to one or more hosts.
617         * <p>
618         * This is rendered all lower-case to slightly improve compressibility
619         * in certain contexts, and is also prefix-less (eg no "www.")
620         * to be in canonical form.
621         */
622        public static final String CDN_HOST = "gcdn.hd.org";
623    
624    //    /**URL of (external, BETA) message board. */
625    //    public static final String EXT_MSG_BOARD_URL = "http://www.activeboard.com/forum.spark?forumID=56026";
626        }