/********************************************************************** * * documentaction.cpp -- * Copyright (C) 1999 The New Zealand Digital Library Project * * A component of the Greenstone digital library software * from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *********************************************************************/ #include #include "documentaction.h" #include "recptprototools.h" #include "OIDtools.h" #include "querytools.h" #include "unitool.h" #include "gsdltools.h" #include "highlighttext.h" #include "browsetoolsclass.h" documentaction::documentaction () { recpt = NULL; // this action uses cgi variables "a", "d", "cl", // "x", "gc", "gt", "gp", and "hl" cgiarginfo arg_ainfo; arg_ainfo.shortname = "a"; arg_ainfo.longname = "action"; arg_ainfo.multiplechar = true; arg_ainfo.defaultstatus = cgiarginfo::weak; arg_ainfo.argdefault = "p"; arg_ainfo.savedarginfo = cgiarginfo::must; argsinfo.addarginfo (NULL, arg_ainfo); arg_ainfo.shortname = "d"; arg_ainfo.longname = "document OID"; arg_ainfo.multiplechar = true; arg_ainfo.defaultstatus = cgiarginfo::none; arg_ainfo.argdefault = g_EmptyText; arg_ainfo.savedarginfo = cgiarginfo::can; argsinfo.addarginfo (NULL, arg_ainfo); // whether or not a document should be retrieved from the // library or the Web. arg_ainfo.shortname = "il"; arg_ainfo.longname = "internal link preference"; arg_ainfo.multiplechar = false; arg_ainfo.defaultstatus = cgiarginfo::weak; arg_ainfo.argdefault = "l"; arg_ainfo.savedarginfo = cgiarginfo::must; argsinfo.addarginfo (NULL, arg_ainfo); arg_ainfo.shortname = "cl"; arg_ainfo.longname = "classification OID"; arg_ainfo.multiplechar = true; arg_ainfo.defaultstatus = cgiarginfo::none; arg_ainfo.argdefault = g_EmptyText; arg_ainfo.savedarginfo = cgiarginfo::can; argsinfo.addarginfo (NULL, arg_ainfo); // in this action "gc" controls the expand/contract // contents function arg_ainfo.shortname = "gc"; arg_ainfo.longname = "expand contents"; arg_ainfo.multiplechar = false; arg_ainfo.defaultstatus = cgiarginfo::weak; arg_ainfo.argdefault = "0"; arg_ainfo.savedarginfo = cgiarginfo::can; argsinfo.addarginfo (NULL, arg_ainfo); // in this action "gt" controls the expand/contract // text function 0 = not expanded, 1 = expand unless // there are more than 10 sections containing text, // 2 = expand all arg_ainfo.shortname = "gt"; arg_ainfo.longname = "expand text"; arg_ainfo.multiplechar = false; arg_ainfo.defaultstatus = cgiarginfo::weak; arg_ainfo.argdefault = "0"; arg_ainfo.savedarginfo = cgiarginfo::can; argsinfo.addarginfo (NULL, arg_ainfo); // in this action "gp" is the "go to page" control // used by the Book type of toc arg_ainfo.shortname = "gp"; arg_ainfo.longname = "go to page"; arg_ainfo.multiplechar = true; arg_ainfo.defaultstatus = cgiarginfo::none; arg_ainfo.argdefault = g_EmptyText; arg_ainfo.savedarginfo = cgiarginfo::mustnot; argsinfo.addarginfo (NULL, arg_ainfo); // in this action "hl" is the "highlighting on/ // highlighting off control ("hl" == "2" is a // special case that forces phrase highlighting // to be used even if the query string doesn't // appear to be a phrase) arg_ainfo.shortname = "hl"; arg_ainfo.longname = "highlighting on/off"; arg_ainfo.multiplechar = false; arg_ainfo.defaultstatus = cgiarginfo::weak; arg_ainfo.argdefault = "1"; arg_ainfo.savedarginfo = cgiarginfo::must; argsinfo.addarginfo (NULL, arg_ainfo); // "x" is 0 normally or 1 if page // has been "detached" arg_ainfo.shortname = "x"; arg_ainfo.longname = "detached page"; arg_ainfo.multiplechar = false; arg_ainfo.defaultstatus = cgiarginfo::weak; arg_ainfo.argdefault = "0"; arg_ainfo.savedarginfo = cgiarginfo::must; argsinfo.addarginfo (NULL, arg_ainfo); // "xx" is 0 normally or 1 if documents should be detached by default arg_ainfo.shortname = "xx"; arg_ainfo.longname = "detach all doc pages"; arg_ainfo.multiplechar = false; arg_ainfo.defaultstatus = cgiarginfo::weak; arg_ainfo.argdefault = "0"; arg_ainfo.savedarginfo = cgiarginfo::must; argsinfo.addarginfo (NULL, arg_ainfo); // f arg is set to 1 if document is to // be displayed in a frame arg_ainfo.shortname = "f"; arg_ainfo.longname = "frame"; arg_ainfo.multiplechar = false; arg_ainfo.defaultstatus = cgiarginfo::weak; arg_ainfo.argdefault = "0"; arg_ainfo.savedarginfo = cgiarginfo::mustnot; argsinfo.addarginfo (NULL, arg_ainfo); // fc arg is "1" if search bar is to be included (i.e. if "fc" == 1 // the httpdocument macro will include "&f=1" arg_ainfo.shortname = "fc"; arg_ainfo.longname = "include search bar"; arg_ainfo.multiplechar = false; arg_ainfo.defaultstatus = cgiarginfo::weak; arg_ainfo.argdefault = "1"; arg_ainfo.savedarginfo = cgiarginfo::must; argsinfo.addarginfo (NULL, arg_ainfo); //rd is whether a document will be displayed //with a relevant document list arg_ainfo.shortname = "rd"; arg_ainfo.longname = "include relevant documents"; arg_ainfo.multiplechar = false; arg_ainfo.defaultstatus = cgiarginfo::weak; arg_ainfo.argdefault = "0"; arg_ainfo.savedarginfo = cgiarginfo::must; argsinfo.addarginfo (NULL, arg_ainfo); //dm is the metadata that has been used for the datelist arg_ainfo.shortname = "dm"; arg_ainfo.longname = "date metadata"; arg_ainfo.multiplechar = true; arg_ainfo.defaultstatus = cgiarginfo::weak; arg_ainfo.argdefault = g_EmptyText; arg_ainfo.savedarginfo = cgiarginfo::must; argsinfo.addarginfo (NULL, arg_ainfo); } documentaction::~documentaction() { } bool documentaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args, recptprotolistclass *protos, ostream &logout) { if(!args["d"].empty()) { text_t docTop; get_top(args["d"],docTop); recptproto* collectproto = protos->getrecptproto (args["c"], logout); if (collectproto != NULL) { ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, args["c"], logout); if(cinfo->authenticate == "document") { // both are either commented out or uncomment and are empty if (cinfo->public_documents.empty() && cinfo->private_documents.empty()) { //deny everything args["uan"] = "1"; args["ug"] = cinfo->auth_group; } // both public_documents and private_documents are turned on ! else if (!cinfo->public_documents.empty() && !cinfo->private_documents.empty()) { //deny everything args["uan"] = "1"; args["ug"] = cinfo->auth_group; } // if public_documents is set but this document isn't // listed in it we need to authenticate else if ((!cinfo->public_documents.empty()) && (cinfo->public_documents.find(docTop) == cinfo->public_documents.end())) { args["uan"] = "1"; args["ug"] = cinfo->auth_group; } // if private_documents is set and this document is // listed in it we need to authenticate else if ((!cinfo->private_documents.empty()) && (cinfo->private_documents.find(docTop) != cinfo->private_documents.end())) { args["uan"] = "1"; args["ug"] = cinfo->auth_group; } } } } // check gc argument int arg_gc = args.getintarg("gc"); if (arg_gc < 0 || arg_gc > 2) { logout << "Warning: \"gc\" argument out of range (" << arg_gc << ")\n"; cgiarginfo *gcinfo = argsinfo.getarginfo ("gc"); if (gcinfo != NULL) args["gc"] = gcinfo->argdefault; } // check gt argument (may be either 0, 1 or 2) int arg_gt = args.getintarg("gt"); if (arg_gt != 0 && arg_gt != 1 && arg_gt != 2) { logout << "Warning: \"gt\" argument out of range (" << arg_gt << ")\n"; cgiarginfo *gtinfo = argsinfo.getarginfo ("gt"); if (gtinfo != NULL) args["gt"] = gtinfo->argdefault; } // check hl argument int arg_hl = args.getintarg("hl"); if (arg_hl < 0 || arg_hl > 2) { logout << "Warning: \"hl\" argument out of range (" << arg_hl << ")\n"; cgiarginfo *hlinfo = argsinfo.getarginfo ("hl"); if (hlinfo != NULL) args["hl"] = hlinfo->argdefault; } // check x argument int arg_x = args.getintarg("x"); if (arg_x != 0 && arg_x != 1) { logout << "Warning: \"x\" argument out of range (" << arg_x << ")\n"; cgiarginfo *xinfo = argsinfo.getarginfo ("x"); if (xinfo != NULL) args["x"] = xinfo->argdefault; } //checks whether rd arg is valid int arg_rd = args.getintarg("rd"); if (arg_rd != 0 && arg_rd != 1) { logout << "Warning: \"rd\" argument out of range (" << arg_rd << ")\n"; cgiarginfo *rdinfo = argsinfo.getarginfo ("rd"); if (rdinfo != NULL) args["rd"] = rdinfo->argdefault; } return true; } void documentaction::get_cgihead_info (cgiargsclass &args, recptprotolistclass *protos, response_t &response,text_t &response_data, ostream &logout) { if ((args["il"] == "w") && (!args["d"].empty())) { recptproto* collectproto = protos->getrecptproto (args["c"], logout); if (collectproto != NULL) { text_tset metadata; FilterResponse_t filt_response; text_t top; metadata.insert ("URL"); // get metadata for parent document get_top (args["d"], top); if (get_info (top, args["c"], args["l"], metadata, false, collectproto, filt_response, logout)) { text_t url = filt_response.docInfo[0].metadata["URL"].values[0]; response = location; response_data = url; return; } else { // error, no URL logout << "Error: documentaction::get_cgihead_info failed on get_info" << endl; } } } response = content; response_data = "text/html"; } // set_widthtspace calculates how wide the spaces in the nav bar should // be and sets the appropriate macro void documentaction::set_spacemacro (displayclass &disp, FilterResponse_t &response, bool has_search_button) { text_t width; int twidth, swidth, iwidth = 0; int numc = response.docInfo.size(); ResultDocInfo_tarray::iterator dochere = response.docInfo.begin(); ResultDocInfo_tarray::iterator docend = response.docInfo.end(); disp.expandstring (displayclass::defaultpackage, "_pagewidth_", width); twidth = width.getint(); if (has_search_button) { disp.expandstring ("query", "_searchwidth_", width); iwidth += width.getint(); } else { numc -= 1; } while (dochere != docend) { const text_t &title = (*dochere).metadata["Title"].values[0]; disp.expandstring ("document", "_" + title + "width_", width); if (width == ("_" + title + "width_")) { disp.expandstring ("document", "_defaultwidth_", width); } iwidth += width.getint(); ++dochere; } if ((twidth - iwidth) < numc) swidth = 2; else { swidth = twidth - iwidth; if (numc > 0) swidth = swidth / numc; } disp.setmacro ("widthtspace", displayclass::defaultpackage, swidth); } // set_navbarmacros sets _navigationbar_ and _httpbrowseXXX_ macros // reponse contains 1 metadata field (Title) void documentaction::set_navbarmacros (displayclass &disp, FilterResponse_t &response, bool has_search_button, cgiargsclass &args, ColInfoResponse_t &cinfo) { bool use_pulldown = false; text_tmap::iterator it = cinfo.format.find("NavigationBar"); if (it != cinfo.format.end()) { if (it->second == "pulldown") { use_pulldown = true; } } text_t topparent; text_t &arg_d = args["d"]; text_t navigationbar = "\n"; get_top (args["cl"], topparent); int numc = response.docInfo.size(); ResultDocInfo_tarray::iterator dochere = response.docInfo.begin(); ResultDocInfo_tarray::iterator docend = response.docInfo.end(); if (!use_pulldown) { if (has_search_button) { // search uses its own navtab, as it may be suppressed based on other args navigationbar += "_navtabsearch_"; if (args["a"] == "q") { navigationbar += "(selected)"; } } if (has_search_button || numc == 0) navigationbar += "_navbarspacer_"; bool first = true; while (dochere != docend) { if (!first) navigationbar += "_navbarspacer_"; text_t title = (*dochere).metadata["Title"].values[0]; navigationbar += "_navtab_(_httpbrowse"+(*dochere).OID+"_,"+title; // if we are at this classifier if (arg_d.empty() && ((*dochere).OID == topparent)) { // don't link to this classifier as it is the current one navigationbar += ",selected"; } navigationbar += ")"; // set the _httpbrowseCLX_ macro for this classification disp.setmacro ("httpbrowse" + (*dochere).OID, displayclass::defaultpackage, "_httpdocument_&cl=" + (*dochere).OID); ++dochere; first = false; } navigationbar += "_dynamicclassifiernavbarentries_"; navigationbar += "\n\n"; } else { navigationbar = "
\n"; navigationbar += "\n"; navigationbar += "
\n"; } disp.setmacro ("navigationbar", displayclass::defaultpackage, navigationbar); } // define all the macros which might be used by other actions // to produce pages. void documentaction::define_external_macros (displayclass &disp, cgiargsclass &args, recptprotolistclass *protos, ostream &logout) { // define_external_macros sets the following macros: // _navigationbar_ this is the navigation bar containing the search button // and any classification buttons - it goes at the top of // most pages. for now we're assuming that there'll always // be a search button - we should probably check that there // is a query action before making this assumption // _httpbrowseXXX_ the http macros for each classification (i.e. if there // are Title and Creator classifications _httpbrowseTitle_ // and _httpbrowseCreator_ will be set // _widthtspace_ the width of the spacers between buttons in navigation // bar // _httpdocument_ has '&f=1' added if displaying document inside a frame // _gsdltop_ macro to replace _top targets with // _httppagehome_ overridden home url if html collections have own homepage // _usability_ macros for remote usability reporting: if // _usabinterface_ "format Usability [multi|textonly|stepwise]" is in // _usabilityscript_ collect.cfg // _versionnnum_ greenstone version number (hard-coded) // must have a valid collection server to continue text_t &collection = args["c"]; if (collection.empty()) return; recptproto *collectproto = protos->getrecptproto (collection, logout); if (collectproto == NULL) return; if (recpt == NULL) { logout << "ERROR (documentaction::define_external_macros): This action does not contain\n" << " information about any receptionists. The method set_receptionist was\n" << " probably not called from the module which instantiated this action.\n"; return; } outconvertclass text_t2ascii; comerror_t err; InfoFiltersResponse_t filterinfo; FilterResponse_t response; text_tset metadata; // get info on current collection and load up formatinfo // I'd prefer not to do this here as we're getting // collection info every time (and probably also getting // it in other places some of the time) - One day I'll // fix it ... maybe - Stefan. ColInfoResponse_t cinfo; collectproto->get_collectinfo (collection, cinfo, err, logout); load_formatinfo (cinfo.format, args.getintarg("gt")); // ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, collection, logout); // if (cinfo == NULL) { // logout << "ERROR (documentaction::define_external_macros): get_collectinfo_ptr returned NULL\n"; // return; // } //load_formatinfo (cinfo->format, args.getintarg("gt")); if (formatinfo.DocumentUseHTML) { // frame stuff if (args["fc"] == "1") { text_t httpdocument; disp.expandstring (displayclass::defaultpackage, "_httpdocument_", httpdocument); httpdocument += "&f=1"; disp.setmacro ("httpdocument", displayclass::defaultpackage, httpdocument); disp.setmacro ("gsdltop", displayclass::defaultpackage, "documenttop"); formatinfo.DocumentText = "[Text]"; } text_tmap::iterator it = cinfo.format.find ("homepage"); if (it != cinfo.format.end()) { text_t httppagehome; if (get_link (args, protos, (*it).second, httppagehome, logout)) disp.setmacro ("httppagehome", displayclass::defaultpackage, httppagehome); } } // don't want navigation bar if page is 'detached' if (!args.getintarg("x")) { collectproto->get_filterinfo (collection, filterinfo, err, logout); if (err == noError) { // check that there's a browse filter if (filterinfo.filterNames.find ("BrowseFilter") != filterinfo.filterNames.end()) { metadata.insert ("Title"); bool getParents = false; get_children ("", collection, args["l"], metadata, getParents, collectproto, response, logout); bool has_search_button = true; collectproto->is_searchable(collection, has_search_button, err, logout); if (err != noError) has_search_button = false; // calculate width of spacers and set _widthtspace_ macro if (args.getintarg("v") == 0) set_spacemacro (disp, response, has_search_button); // set _navigationbar_ macro set_navbarmacros (disp, response, has_search_button, args, cinfo); } } else { logout << text_t2ascii << "Error (documentaction::define_external_macros()) in call to get_filterinfo() " << get_comerror_string (err); } } // send feedback button and pages text_tmap::iterator usability = cinfo.format.find("Usability"); if(usability!=cinfo.format.end()){ disp.setmacro("usability",displayclass::defaultpackage,"_usablink_"); disp.setmacro("usabinterface",displayclass::defaultpackage,("_usab"+(*usability).second+"_")); disp.setmacro("usabilityscript", displayclass::defaultpackage, "_usabshowscript_"); } // version num disp.setmacro("versionnum", displayclass::defaultpackage, GSDL_VERSION); } bool documentaction::get_link (cgiargsclass &args, recptprotolistclass *protos, const text_t &inlink, text_t &outlink, ostream &logout) { FilterResponse_t response; text_tset metadata; metadata.insert ("section"); // check current collection first recptproto *collectproto = protos->getrecptproto (args["c"], logout); if (get_info (inlink, args["c"], args["l"], metadata, false, collectproto, response, logout)) { if (!response.docInfo[0].metadata["section"].values[0].empty()) { #ifndef DOCHANDLE outlink = "_httpdocument_&d=" + response.docInfo[0].metadata["section"].values[0]; #else outlink = "_httpdocumenthandle_("+args["c"]+","+response.docInfo[0].metadata["section"].values[0]+")"; #endif return true; } } // check all the other enabled collections if (args["ccs"] == "1" && !args["cc"].empty()) { text_tarray collections; splitchar (args["cc"].begin(), args["cc"].end(), ',', collections); text_tarray::const_iterator col_here = collections.begin(); text_tarray::const_iterator col_end = collections.end(); while (col_here != col_end) { // don't need to check current collection again if (*col_here == args["c"]) {++col_here; continue;} collectproto = protos->getrecptproto (*col_here, logout); if (get_info (inlink, *col_here, args["l"], metadata, false, collectproto, response, logout)) { if (!response.docInfo[0].metadata["section"].values[0].empty()) { #ifndef DOCHANDLE outlink = "_httpdocument_&c=" + *col_here + "&d=" + response.docInfo[0].metadata["section"].values[0]; #else outlink = "_httpdocumenthandle_("+*col_here+","+response.docInfo[0].metadata["section"].values[0]+")"; #endif return true; } } ++col_here; } } return false; } void documentaction::load_formatinfo (const text_tmap &colformat, int gt) { formatinfo.clear(); text_tmap::const_iterator format_here = colformat.begin(); text_tmap::const_iterator format_end = colformat.end(); while (format_here != format_end) { if (((*format_here).first == "DocumentImages") && ((*format_here).second == "true")) formatinfo.DocumentImages = true; else if (((*format_here).first == "DocumentTitles") && ((*format_here).second == "false")) formatinfo.DocumentTitles = false; else if ((*format_here).first == "DocumentHeading") formatinfo.DocumentHeading = (*format_here).second; else if (((*format_here).first == "DocumentContents") && ((*format_here).second == "false")) formatinfo.DocumentContents = false; else if (((*format_here).first == "DocumentArrowsBottom") && ((*format_here).second == "false")) formatinfo.DocumentArrowsBottom = false; else if (((*format_here).first == "DocumentArrowsTop") && ((*format_here).second == "true")) formatinfo.DocumentArrowsTop = true; else if ((*format_here).first == "DocumentButtons") splitchar ((*format_here).second.begin(), (*format_here).second.end(), '|', formatinfo.DocumentButtons); else if ((*format_here).first == "DocumentText") formatinfo.DocumentText = (*format_here).second; else if ((*format_here).first == "RelatedDocuments") formatinfo.RelatedDocuments = (*format_here).second; else if (((*format_here).first == "DocumentUseHTML") && ((*format_here).second == "true")) formatinfo.DocumentUseHTML = true; else if (((*format_here).first == "AllowExtendedOptions") && ((*format_here).second == "true")) formatinfo.AllowExtendedOptions = true; else formatinfo.formatstrings[(*format_here).first] = (*format_here).second; ++format_here; } // never want arrows when text is expanded if (gt) { formatinfo.DocumentArrowsBottom = false; formatinfo.DocumentArrowsTop = false; } } // define all the macros which are related to pages generated // by this action. we also load up the formatinfo structure // here (it's used in do_action as well as here) void documentaction::define_internal_macros (displayclass &disp, cgiargsclass &args, recptprotolistclass *protos, ostream &logout) { // define_internal_macros sets the following macros: // _pagetitle_ the title to be displayed at the top of the browser window // _imagethispage_ the title image to be displayed at top right of page // _navarrowsbottom_ this may be overridden to "" when format option // DocumentArrowsBottom is false // _navarrowstop_ likewise for DocumentArrowsTop // _httpprevarrow_ links to next and previous sections of document - used // _httpnextarrow_ by DocumentArrowsBottom // _header_ the header macro is overridden if we're not at a top level // classification to remove the title block // _thisOID_ the OID (directory) of the current document - this corresponds // to the archivedir metadata element // _documentheader_ Custom header info for the specified document // must have a valid collection server to continue text_t &collection = args["c"]; if (collection.empty()) return; recptproto *collectproto = protos->getrecptproto (collection, logout); if (collectproto == NULL) return; text_tset metadata; FilterResponse_t response; text_t &arg_d = args["d"]; text_t &arg_cl = args["cl"]; if (!formatinfo.DocumentArrowsBottom) { disp.setmacro("navarrowsbottom", "document", ""); } else if (!formatinfo.DocumentArrowsBottom) { disp.setmacro("navarrowstop", "document", ""); } if (!arg_d.empty() && (formatinfo.DocumentArrowsBottom || formatinfo.DocumentArrowsTop)) { // set _httpprevarrow_ and _httpnextarrow_ set_arrow_macros (args, collectproto, disp, logout); } metadata.insert ("Title"); metadata.insert ("gs.DocumentHeader"); metadata.insert ("DocumentHeader"); bool fulltoc = false; if (args["cl"] != "search") { // see if there's a FullTOC string text_t cl_top, full_toc; get_top (arg_cl, cl_top); if (get_formatstring (cl_top, "FullTOC", formatinfo.formatstrings, full_toc)) if (full_toc == "true") fulltoc = true; disp.setmacro("cltop", "document", cl_top); } if (!arg_d.empty() && !fulltoc) { // we're at document level metadata.insert ("archivedir"); comerror_t err; OptionValue_tarray options; // we need to do the query again for the z3950proto if (collectproto->get_protocol_name(err)=="z3950proto") { OptionValue_t opt; opt.name="Term";opt.value=args["q"];options.push_back(opt); opt.name="QueryType"; opt.value=(args.getintarg("t")) ? "ranked" : "boolean"; options.push_back(opt); opt.name="Index";opt.value=args["h"];options.push_back(opt); } //do not display relation metadata disp.setmacro ("relateddoc", "document", ""); //if preferences indicate relevant docs should be collected //and there is no particular format specified then display //this default format. if(args["rd"] == "1" && formatinfo.RelatedDocuments.empty()){ text_t relation = g_EmptyText; //string for displaying relation metadata //call function in formattools.cpp which will return the text of the //related documents in a vertical list. This is the default format. if (get_info (arg_d, collection, args["l"], metadata, options, false, collectproto, response, logout)) relation += get_related_docs(collection, collectproto, response.docInfo[0], logout); //set macro to be the related document string disp.setmacro ("relateddoc", "document", relation); } // get metadata for this document and it's parents if (get_info (arg_d, collection, args["l"], metadata, options, true, collectproto, response, logout)) { disp.setmacro ("header", "document", "_textheader_"); text_tarray pagetitlearray; if (!response.docInfo[0].metadata["Title"].values[0].empty()) pagetitlearray.push_back (response.docInfo[0].metadata["Title"].values[0]); if (args["gt"] != "1") { MetadataInfo_t *parenttitle = response.docInfo[0].metadata["Title"].parent; while (parenttitle != NULL) { if (!parenttitle->values[0].empty()) pagetitlearray.push_back (parenttitle->values[0]); parenttitle = parenttitle->parent; } } reverse (pagetitlearray.begin(), pagetitlearray.end()); text_t pagetitle; joinchar (pagetitlearray, ": ", pagetitle); // remove html tags from the title text_t::iterator open_tag=pagetitle.begin(); while (open_tag < pagetitle.end()) { if (*open_tag == '<') { text_t::iterator close_tag=open_tag+1; text_t::iterator donechar=pagetitle.end(); while (close_tag < donechar) { if (*close_tag == '>') break; ++close_tag; } if (close_tag < donechar) // remove this html tag, replace with space *close_tag=' '; pagetitle.erase(open_tag, close_tag); } ++open_tag; } disp.setmacro ("pagetitle", "document", pagetitle); // custom header info text_t custom_header; if (is_top (arg_d)) { custom_header = response.docInfo[0].metadata["gs.DocumentHeader"].values[0]; if (custom_header.empty()) { // try ex header custom_header = response.docInfo[0].metadata["DocumentHeader"].values[0]; } } else { MetadataInfo_t *parentch = response.docInfo[0].metadata["gs.DocumentHeader"].parent; while (parentch != NULL) { custom_header = parentch->values[0]; parentch = parentch->parent; } if (custom_header.empty()) { // try ex header MetadataInfo_t *parentch = response.docInfo[0].metadata["DocumentHeader"].parent; while (parentch != NULL) { custom_header = parentch->values[0]; parentch = parentch->parent; } } } if (!custom_header.empty()) { disp.setmacro("documentheader", "document", custom_header); } if (is_top (arg_d)) disp.setmacro ("thisOID", displayclass::defaultpackage, dm_safe(response.docInfo[0].metadata["archivedir"].values[0])); else { MetadataInfo_t *parentad = response.docInfo[0].metadata["archivedir"].parent; text_t thisOID; while (parentad != NULL) { thisOID = parentad->values[0]; parentad = parentad->parent; } disp.setmacro ("thisOID", displayclass::defaultpackage, dm_safe(thisOID)); } } } else { if (!arg_cl.empty()) { // create the currentnodevalue macro - this is the value of the node // in the hierarchy that is currently open. if (get_info (arg_cl, collection, args["l"], metadata, false, collectproto, response, logout)) { text_t &title = response.docInfo[0].metadata["Title"].values[0]; disp.setmacro ("currentnodevalue", "document", title); } // get metadata for top level classification text_t classtop; get_top (arg_cl, classtop); metadata.insert ("childtype"); metadata.insert ("parameters"); if (get_info (classtop, collection, args["l"], metadata, false, collectproto, response, logout)) { text_t &title = response.docInfo[0].metadata["Title"].values[0]; bool unknown = false; // test if we have macros defined for this classification's // metadata name - if not we'll just display the name text_t tmp; text_t macroname="_label" + title + "_"; disp.expandstring ("Global", macroname, tmp); if (tmp == macroname) unknown = true; // it wasn't expanded if (unknown) { disp.setmacro ("pagetitle", "document", title); } else { disp.setmacro ("pagetitle", "document", macroname); } /* for ns4/image layout compatibility. when this is no longer * needed, set imagethispage to _label+title+_ */ text_t titlemacroname="_titleimage" + title + "_"; disp.expandstring ("Global", titlemacroname, tmp); if (tmp == titlemacroname) { /* _titleimage$META_ isn't defined */ if (!unknown) /* _label$META_ is defined */ disp.setmacro ("imagethispage", "document", macroname); else disp.setmacro ("imagethispage", "document", title); } else { /* _titleimage$META_ is defined */ disp.setmacro ("imagethispage", "document", titlemacroname); } //if the document is not a document from a collection //we must set the macro to be an empty string disp.setmacro ("relateddoc", "document", ""); // Add macros specific to the Collage/Phind classifiers text_t &childtype = response.docInfo[0].metadata["childtype"].values[0]; if (childtype == "Collage") { text_t::iterator a = arg_cl.begin(); text_t::iterator b = arg_cl.end(); bool collage = true; while (a != b) { if (*a == 46) collage = false; ++a; } if (collage) { disp.setmacro ("collageclassifier", "document", "_collageapplet_"); // Next, macros that control the way the classifier is displayed text_t parameters = response.docInfo[0].metadata["parameters"].values[0]; // extract key=value pairs and set as macros text_t::iterator here = parameters.begin(); text_t::iterator end = parameters.end(); text_t key, value; while (here != end) { // get the next key and value pair here = getdelimitstr (here, end, '=', key); here = getdelimitstr (here, end, ';', value); // store this key=value pair if (!key.empty() && !value.empty()) { disp.setmacro ("collage"+key, "document", value); } } } } if (childtype == "Phind") { // First, a macro to display the phind classifier disp.setmacro ("phindclassifier", "document", "_phindapplet_"); // Next, macros that control the way the classifier is displayed text_t parameters = response.docInfo[0].metadata["parameters"].values[0]; // extract key=value pairs and set as macros text_t::iterator here = parameters.begin(); text_t::iterator end = parameters.end(); text_t key, value; while (here != end) { // get the next key and value pair here = getdelimitstr (here, end, '=', key); here = getdelimitstr (here, end, ';', value); // store this key=value pair if (!key.empty() && !value.empty()) { disp.setmacro (key, "document", value); } } } // end if (childtype == "Phind") } } // end if (!arg_cl.empty()) { } } bool documentaction::do_action(cgiargsclass &args, recptprotolistclass *protos, browsermapclass *browsers, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout) { if ((args["book"] == "flashxml") && (!args["d"].empty())) { return do_action_flashxml(args,protos,browsers,disp,outconvert,textout,logout); } else if ((args["book"] == "on") && (!args["d"].empty())) { return do_action_book(args,protos,browsers,disp,outconvert,textout,logout); } else { return do_action_html(args,protos,browsers,disp,outconvert,textout,logout); } } //displaying the normal display when bookswitch=0(html) bool documentaction::do_action_html(cgiargsclass &args, recptprotolistclass *protos, browsermapclass *browsers, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout) { // must have a valid collection server recptproto *collectproto = protos->getrecptproto (args["c"], logout); if (collectproto == NULL) { logout << "documentaction::do_action called with NULL collectproto\n"; textout << outconvert << disp << "_document:header_\n" << "Error: Attempt to get document without setting collection\n" << "_document:footer_\n"; } else { text_t OID = args["d"]; if (OID.empty()) OID = args["cl"]; if (OID.empty()) { textout << outconvert << disp << "Document contains no data_document:footer_\n"; return true; } if (formatinfo.DocumentUseHTML && !args["d"].empty()) { if (args["f"] == "1") { textout << outconvert << disp << "\n" << "\n" << "\n" #ifndef DOCHANDLE << "" #else << "" #endif << "\n" << "<p>You must have a frame enabled browser to view this.</p>\n" << "\n" << "\n" << "\n"; } else { output_document (OID, args, collectproto, browsers, disp, outconvert, textout, logout); } return true; } textout << outconvert << disp << "_document:header_\n" << "_document:content_\n"; // output the table of contents browsetoolsclass b; b.output_toc(args, browsers, formatinfo, collectproto, disp, outconvert, textout, logout); if (formatinfo.DocumentArrowsTop && !args["d"].empty()) { textout << outconvert << disp << "_document:navarrowstop_\n"; } //output the related documents (may be the empty string) //will not output the docs if a format string is specified textout << outconvert << disp << "_document:relateddoc_\n"; // output the document text if (!args["d"].empty()) { output_document (OID, args, collectproto, browsers, disp, outconvert, textout, logout); } textout << outconvert << disp << "_document:footer_\n"; } return true; } //displaying the book display when bookswitch=on bool documentaction::do_action_book(cgiargsclass &args, recptprotolistclass *protos, browsermapclass *browsers, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout) { // must have a valid collection server recptproto *collectproto = protos->getrecptproto (args["c"], logout); if (collectproto == NULL) { logout << "documentaction::do_action called with NULL collectproto\n"; textout << outconvert << disp << "_document:header_\n" << "Error: Attempt to get document without setting collection\n"; } else { text_t OID = args["d"]; if (OID.empty()) OID = args["cl"]; if (OID.empty()) { textout << outconvert << disp << "Document contains no data\n"; return true; } if (formatinfo.DocumentUseHTML && !args["d"].empty()) { if (args["f"] == "1") { textout << outconvert << disp << "\n" << "\n" << "\n" #ifndef DOCHANDLE << "" #else << "" #endif << "\n" << "<p>You must have a frame enabled browser to view this.</p>\n" << "\n" << "\n" << "\n"; } else { output_document (OID, args, collectproto, browsers, disp, outconvert, textout, logout); } return true; } //the header bit and the navigation button textout << outconvert << disp << "_document:header_\n" << "_document:content_\n"; //display the book textout << outconvert << disp << "_document:flashbook_\n"; //the footer bit without the document arrow textout << outconvert << disp << " \n"; textout << outconvert << disp << "_endspacer__htmlfooter_\n"; } return true; } //Displaying the xml when bookswitch=flashxml bool documentaction::do_action_flashxml(cgiargsclass &args, recptprotolistclass *protos, browsermapclass *browsers, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout) { // must have a valid collection server recptproto *collectproto = protos->getrecptproto (args["c"], logout); if (collectproto == NULL) { logout << "documentaction::do_action called with NULL collectproto\n"; textout << outconvert << disp << "\n" << " Error: Attempt to get document without setting collection\n" << "\n"; } else { text_t OID = args["d"]; if (OID.empty()) OID = args["cl"]; if (OID.empty()) { textout << outconvert << disp << "\n" << " Document contains no data\n" << "\n"; return true; } if (formatinfo.DocumentUseHTML && !args["d"].empty()) { if (args["f"] == "1") { textout << outconvert << disp << "\n" << " Using frames makes no sense for XML output\n" << "\n"; } else { output_document_flashxml (OID, args, collectproto, browsers, disp, outconvert, textout, logout); } return true; } // output the document text if (!args["d"].empty()) { output_document_flashxml (OID, args, collectproto, browsers, disp, outconvert, textout, logout); } } return true; } void documentaction::output_text (ResultDocInfo_t &docinfo, format_t *formatlistptr, const TermInfo_tarray &terminfo, const text_t &OID, bool highlight, int hastxt, int wanttext, text_t &collection, recptproto *collectproto, browsermapclass *browsers, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout, cgiargsclass &args) { DocumentRequest_t docrequest; DocumentResponse_t docresponse; comerror_t err; if (hastxt == 1) { if (wanttext) { // get the text docrequest.OID = OID; collectproto->get_document (collection, docrequest, docresponse, err, logout); // cut down on overhead by not using formattools if we only want the text // (wanttext will equal 2 if we want text and other stuff too) if (wanttext == 1) if (highlight) { highlighttext(docresponse.doc, args, terminfo, disp, outconvert, textout); } else { textout << outconvert << disp << docresponse.doc; } } if (wanttext != 1) { text_tmap options; options["text"] = docresponse.doc; if (formatinfo.AllowExtendedOptions) { browsetoolsclass b; b.load_extended_options(options, args, browsers, formatinfo, collectproto, disp, outconvert, logout); } text_t doctext = get_formatted_string (collection, collectproto, docinfo, disp, formatlistptr, options, logout); if (highlight) { highlighttext(doctext, args, terminfo, disp, outconvert, textout); } else { textout << outconvert << disp << doctext; } } } } void documentaction::output_document (const text_t &OID, cgiargsclass &args, recptproto *collectproto, browsermapclass *browsers, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout) { FilterResponse_t inforesponse; FilterResponse_t queryresponse; text_tset metadata; bool getParents = false; bool highlight = false; int wanttext = 0; int arg_gt = args.getintarg("gt"); text_t &collection = args["c"]; // if we have a query string and highlighting is turned on we need // to redo the query to get the terms for highlighting if (!args["q"].empty() && args.getintarg("hl")) { ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, collection, logout); bool segment = false; if (cinfo != NULL) { segment = cinfo->isSegmented; } FilterRequest_t request; comerror_t err; request.filterResultOptions = FRmatchTerms; text_t formattedstring = args["q"]; format_querystring (formattedstring, args.getintarg("b"), segment); set_queryfilter_options (request, formattedstring, args); args["q"] = formattedstring; // need to set this here for phrase // highlighting, where we look at the querystring collectproto->filter (args["c"], request, queryresponse, err, logout); if (err != noError) { outconvertclass text_t2ascii; logout << text_t2ascii << "documentaction::output_document: call to QueryFilter failed " << "for " << args["c"] << " collection (" << get_comerror_string (err) << ")\n"; highlight = false; } else { highlight = true; } } format_t *formatlistptr = new format_t(); parse_formatstring (formatinfo.DocumentText, formatlistptr, metadata, getParents); metadata.insert ("hastxt"); metadata.insert ("haschildren"); if (formatinfo.DocumentText == "[Text]") wanttext = 1; else { char *docformat = formatinfo.DocumentText.getcstr(); if (strstr (docformat, "[Text]") != NULL) wanttext = 2; delete []docformat; } textout << outconvert << "
\n"; if (get_info (OID, collection, args["l"], metadata, getParents, collectproto, inforesponse, logout)) { int hastxt = inforesponse.docInfo[0].metadata["hastxt"].values[0].getint(); int haschildren = inforesponse.docInfo[0].metadata["haschildren"].values[0].getint(); if (arg_gt == 0) { output_text (inforesponse.docInfo[0], formatlistptr, queryresponse.termInfo, OID, highlight, hastxt, wanttext, collection, collectproto, browsers, disp, outconvert, textout, logout, args); } else { ResultDocInfo_t thisdocinfo = inforesponse.docInfo[0]; // text is to be expanded text_t exOID = OID; if (haschildren != 1) exOID = get_parent (OID); if (exOID.empty()) exOID = OID; // if we're not in a document (i.e. we're in a top level classification) // we need to pass "is_classify = true" to get_contents so that it // doesn't recurse all the way through each document in the classification bool is_classify = false; if (args["d"].empty()) is_classify = true; get_contents (exOID, is_classify, metadata, collection, args["l"], collectproto, inforesponse, logout); ResultDocInfo_tarray::iterator sechere = inforesponse.docInfo.begin(); ResultDocInfo_tarray::iterator secend = inforesponse.docInfo.end(); if (arg_gt == 1) { // check if there are more than 10 sections containing text to be expanded - // if there are output warning message - this isn't a great way to do this // since the sections may be very large or very small - one day I'll fix it // -- Stefan. int seccount = 0; while (sechere != secend) { int shastxt = (*sechere).metadata["hastxt"].values[0].getint(); if (shastxt == 1) ++seccount; if (seccount > 10) break; ++sechere; } if (seccount > 10) { // more than 10 sections so output warning message and text // for current section only textout << outconvert << disp << "
_document:textltwarning_
\n"; output_text (thisdocinfo, formatlistptr, queryresponse.termInfo, OID, highlight, hastxt, wanttext, collection, collectproto, browsers, disp, outconvert, textout, logout, args); } else arg_gt = 2; } if (arg_gt == 2) { // get the text for each section sechere = inforesponse.docInfo.begin(); int count = 0; while (sechere != secend) { textout << outconvert << disp << "\n

\n"; int shastxt = (*sechere).metadata["hastxt"].values[0].getint(); output_text (*sechere, formatlistptr, queryresponse.termInfo, (*sechere).OID, highlight, shastxt, wanttext, collection, collectproto, browsers, disp, outconvert, textout, logout, args); ++count; ++sechere; } } } } textout << outconvert << "

\n"; delete formatlistptr; } void documentaction::output_document_flashxml(const text_t &OID, cgiargsclass &args, recptproto *collectproto, browsermapclass *browsermap, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout) { //if this is not at the document level --> do Nothing!! if (args["d"].empty()) return; //if we have a query string and highlighting is turned on we need //to redo the query to get the terms for highlighting FilterResponse_t queryresponse; bool highlight = false; if (!args["q"].empty() && args.getintarg("hl")) { text_t &collection = args["c"]; ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, collection, logout); bool segment = false; if (cinfo != NULL) { segment = cinfo->isSegmented; } FilterRequest_t request; comerror_t err; request.filterResultOptions = FRmatchTerms; text_t formattedstring = args["q"]; format_querystring (formattedstring, args.getintarg("b"), segment); set_queryfilter_options (request, formattedstring, args); args["q"] = formattedstring; //need to set this here for phrase //highlighting, where we look at the querystring collectproto->filter (args["c"], request, queryresponse, err, logout); if (err != noError) { outconvertclass text_t2ascii; logout << text_t2ascii << "documentaction::output_document: call to QueryFilter failed " << "for " << args["c"] << " collection (" << get_comerror_string (err) << ")\n"; highlight = false; } else { highlight = true; } } FilterResponse_t response; bool getParents = false; text_tset metadata; text_t classifytype, classification, formatstring; //metadata elements needed by recurse_contents metadata.insert ("Title"); metadata.insert ("allowPrint"); metadata.insert ("haschildren"); //protocol call if (!get_info (OID, args["c"], args["l"], metadata, getParents, collectproto, response, logout)) return; recurse_contents (response.docInfo[0], args, metadata, getParents, collectproto, disp, outconvert, textout, logout, highlight, queryresponse.termInfo); } void documentaction::recurse_contents(ResultDocInfo_t §ion, cgiargsclass &args, text_tset &metadata, bool &getParents, recptproto *collectproto, displayclass &disp, outconvertclass &outconvert, ostream &textout, ostream &logout, bool highlight, const TermInfo_tarray &terminfo) { text_t formatstring; bool is_classify = false; //output this section text_t classification = "Document"; textout << outconvert << disp << "
\n"; //get the title textout << outconvert << disp << "\n"; textout << outconvert << disp << " "; text_t t_title = section.metadata["Title"].values[0]; //if it is highlighted /*if (highlight == true) { highlighttext(t_title, args, terminfo, disp, outconvert, textout); } else {*/ textout << outconvert << disp << t_title; //} textout << outconvert << disp << "\n"; // if top-level document section, add in allowPrint metadata if (is_top(section.OID)) { textout << outconvert << disp << " "; text_t t_allowprint = "true"; if (section.metadata["allowPrint"].values.size()>0) { text_t opt_allowprint = section.metadata["allowPrint"].values[0]; if (!opt_allowprint.empty()) { t_allowprint = opt_allowprint; } } textout << outconvert << disp << t_allowprint; textout << outconvert << disp << "\n"; } textout << outconvert << disp << "\n"; //get the text DocumentRequest_t docrequest; DocumentResponse_t docresponse; comerror_t err; docrequest.OID = section.OID; text_t &collection = args["c"]; collectproto->get_document(collection, docrequest, docresponse, err, logout); //if it is highlighted if (highlight == true) { highlighttext(docresponse.doc, args, terminfo, disp, outconvert, textout); } else { textout << outconvert << disp << docresponse.doc; } textout << outconvert << disp << "\n"; int haschildren = section.metadata["haschildren"].values[0].getint(); // recurse through children if (haschildren == 1) { FilterResponse_t tmp; get_children (section.OID, args["c"], args["l"], metadata, getParents, collectproto, tmp, logout); ResultDocInfo_tarray::iterator thisdoc = tmp.docInfo.begin(); ResultDocInfo_tarray::iterator lastdoc = tmp.docInfo.end(); while (thisdoc != lastdoc) { recurse_contents (*thisdoc, args, metadata, getParents, collectproto, disp, outconvert, textout, logout, highlight, terminfo); ++thisdoc; } } textout << outconvert << disp << "
\n"; } void documentaction::set_arrow_macros (cgiargsclass &args, recptproto *collectproto, displayclass &disp, ostream &logout) { text_tset metadata; FilterResponse_t response; FilterResponse_t presponse; int haschildren = 0; text_tarray next_siblings; text_t previous_sibling; text_t &arg_d = args["d"]; // get info on current section metadata.insert("haschildren"); if (!get_info(arg_d, args["c"], args["l"], metadata, false, collectproto, response, logout)) { logout << "error 1 in documentaction::set_arrow_macros\n"; return; } haschildren = response.docInfo[0].metadata["haschildren"].values[0].getint(); // get OIDs of next and previous siblings of current section and // all it's parents int parentcount = countchar(arg_d.begin(), arg_d.end(), '.'); text_t thisoid = arg_d; while (parentcount > 0) { get_children (get_parent(thisoid), args["c"], args["l"], metadata, false, collectproto, response, logout); ResultDocInfo_tarray::iterator this_sibling = response.docInfo.begin(); ResultDocInfo_tarray::iterator last_sibling = response.docInfo.end(); bool first = true; while (this_sibling != last_sibling) { if ((*this_sibling).OID == thisoid) { if (!first && next_siblings.empty()) { previous_sibling = (*(this_sibling-1)).OID; int section_has_children = (*(this_sibling-1)).metadata["haschildren"].values[0].getint(); // if previous sibling has children we need to recurse // down to the last descendant while (section_has_children) { get_children (previous_sibling, args["c"], args["l"], metadata, false, collectproto, presponse, logout); if (!presponse.docInfo.empty()) { ResultDocInfo_tarray::iterator it = presponse.docInfo.end() - 1; previous_sibling = (*it).OID; section_has_children = (*it).metadata["haschildren"].values[0].getint(); } else { section_has_children = 0; // this should never happen } } } if ((this_sibling+1) != last_sibling) { next_siblings.push_back((*(this_sibling+1)).OID); } else { next_siblings.push_back(""); } break; } ++this_sibling; first = false; } thisoid = get_parent(thisoid); --parentcount; } // work out values for next link if (haschildren) { #ifndef DOCHANLE disp.setmacro ("httpnextarrow", "document", "_httpdocument_&cl=" + args["cl"] + "&d=" + arg_d + ".fc"); #else disp.setmacro ("httpnextarrow", "document", "_httpdocumenthandle_("+args["c"]+","+arg_d + ".fc)"; #endif } else { text_tarray::const_iterator h = next_siblings.begin(); text_tarray::const_iterator e = next_siblings.end(); while (h != e) { if (!(*h).empty()) { #ifndef DOCHANLE disp.setmacro ("httpnextarrow", "document", "_httpdocument_&cl=" + args["cl"] + "&d=" + *h); #else disp.setmacro ("httpnextarrow", "document", "_httpdocumenthandle_("+args["c"]+","+*h+")"; #endif break; } ++h; } } // work out value for previous link if (!previous_sibling.empty()) { #ifndef DOCHANDLE disp.setmacro ("httpprevarrow", "document", "_httpdocument_&cl=" + args["cl"] + "&d=" + previous_sibling); #else disp.setmacro ("httpprevarrow", "document", "_httpdocumenthandle_("+args["c"]+","+ previous_sibling+")"); #endif } else { if (countchar(arg_d.begin(), arg_d.end(), '.')) { #ifndef DOCHANDLE disp.setmacro ("httpprevarrow", "document", "_httpdocument_&cl=" + args["cl"] + "&d=" + get_parent(arg_d)); #else disp.setmacro ("httpprevarrow", "document", "_httpdocumenthandle_("+args["c"]+","+get_parent(arg_d)+")"); #endif } } }