Index: /trunk/gsdl/src/recpt/browsetools.cpp
===================================================================
--- /trunk/gsdl/src/recpt/browsetools.cpp	(revision 1940)
+++ /trunk/gsdl/src/recpt/browsetools.cpp	(revision 1941)
@@ -121,4 +121,35 @@
 }
 
+//this function outputs the related documents in the format specified 
+//in the collection configuration file if the collection preferences
+//indicate they wish related documents to be displayed.
+void output_related_docs (cgiargsclass &args, recptproto *collectproto, 
+			  formatinfo_t &formatinfo, displayclass &disp, 
+			  outconvertclass &outconvert, ostream &textout, 
+			  ostream &logout) {
+
+  if (args["d"].empty()) return; //if no OID
+  if (args["rd"] != "1") return; //if preferences say no related documents
+  
+  text_tset metadata;
+  bool getParents;
+  FilterResponse_t response;
+
+  //create new format pointer and parse the related doc format 
+  //string specified in the collection config file
+  format_t *formatlistptr = new format_t();
+  parse_formatstring (formatinfo.RelatedDocuments, formatlistptr, metadata, getParents);
+
+  if (!get_info (args["d"], args["c"], metadata, getParents, collectproto, response, logout))
+    return;
+  
+  //get the format string from formattools.cpp
+  text_t relateddocs =  get_formatted_string (args["c"],collectproto, response.docInfo[0], 
+					      disp, formatlistptr, logout);
+  //output the related documents
+  textout << outconvert << disp << relateddocs;
+}
+
+
 
 static void recurse_contents (ResultDocInfo_t &section, cgiargsclass &args, bool fulltoc,
@@ -542,5 +573,5 @@
     output_controls (args, formatinfo.DocumentButtons, collectproto, disp, 
 		     outconvert, textout, logout);
-    textout << "</td><td valign=top>\n";
+    textout << "</td><td valign=top>\n";  
     havecontrols = true;
   }
@@ -560,4 +591,14 @@
   }
 
-  if (havecontrols) textout << "</td></tr></table></center>\n";
-}
+  if (havecontrols) textout << "</td></tr></table></center>\n"; 
+
+  //if there is a format specified in the config file then
+  //try to display the related documents (may not be displayed
+  //if preference file does not indicate a wish to display
+  //related documents.
+  if (!formatinfo.RelatedDocuments.empty())
+    output_related_docs(args, collectproto, formatinfo, disp, outconvert, textout, logout);
+ 
+}
+
+
Index: /trunk/gsdl/src/recpt/documentaction.cpp
===================================================================
--- /trunk/gsdl/src/recpt/documentaction.cpp	(revision 1940)
+++ /trunk/gsdl/src/recpt/documentaction.cpp	(revision 1941)
@@ -144,4 +144,15 @@
   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);
+
 }
 
@@ -183,4 +194,13 @@
     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;
@@ -490,5 +510,7 @@
 		 '|', formatinfo.DocumentButtons);
     else if ((*format_here).first == "DocumentText") 
-      formatinfo.DocumentText = (*format_here).second;
+      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"))
@@ -565,13 +587,35 @@
       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 = ""; //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, 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, 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;
@@ -627,5 +671,9 @@
 	}
 
-      	// Add a macro to display the phind classifier (if appropriate)
+      	//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 a macro to display the phind classifier (if appropriate)
 	text_t &childtype = response.docInfo[0].metadata["childtype"].values[0];
 	if (childtype == "Phind") {
@@ -642,6 +690,5 @@
 				outconvertclass &outconvert, ostream &textout, 
 				ostream &logout) {
-
-
+ 
   // must have a valid collection server
   recptproto *collectproto = protos->getrecptproto (args["c"], logout);
@@ -660,4 +707,5 @@
     }
 
+  
     if (formatinfo.DocumentUseHTML) {
       
@@ -682,11 +730,17 @@
     }
 
+   
     textout << outconvert << disp << "_document:header_\n"
 	    << "_document:content_\n";
       
     // output the table of contents
+    logout << "formatinfo.RelatedDocs: " << formatinfo.RelatedDocuments.getcstr() << endl;
     output_toc (args, browsers, formatinfo, collectproto, 
 		disp, outconvert, textout, logout);
-    
+   
+    //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
     textout << "<p>\n";
@@ -774,4 +828,5 @@
   }
 
+
   format_t *formatlistptr = new format_t();
   parse_formatstring (formatinfo.DocumentText, formatlistptr, metadata, getParents);
@@ -779,5 +834,5 @@
   metadata.insert ("hastxt");
   metadata.insert ("haschildren");
-  
+ 
   if (formatinfo.DocumentText == "[Text]")
     wanttext = 1;
Index: /trunk/gsdl/src/recpt/formattools.cpp
===================================================================
--- /trunk/gsdl/src/recpt/formattools.cpp	(revision 1940)
+++ /trunk/gsdl/src/recpt/formattools.cpp	(revision 1941)
@@ -69,5 +69,5 @@
   DocumentHeading = "{Or}{[parent(Top):Title],[Title],untitled}<br>";
   DocumentContents = true;
-  DocumentArrowsBottom = true;
+  DocumentArrowsBottom = true; 
   DocumentButtons.erase (DocumentButtons.begin(), DocumentButtons.end());
   //  DocumentButtons.push_back ("Expand Text");
@@ -75,4 +75,5 @@
   DocumentButtons.push_back ("Detach");
   DocumentButtons.push_back ("Highlight");
+  RelatedDocuments = "";
   DocumentText = "<center><table width=_pagewidth_><tr><td>[Text]</td></tr></table></center>";
   formatstrings.erase (formatstrings.begin(), formatstrings.end());
@@ -171,4 +172,97 @@
   return day + " " + month + " " + year;
 }  
+
+
+//this function gets the information associated with the relation
+//metadata for the document associated with 'docinfo'. This relation
+//metadata consists of a line of pairs containing 'collection, document OID'
+//(this is the OID of the document related to the current document, and
+//the collection the related document belongs to). For each of these pairs
+//the title metadata is obtained and then an html link between the title
+//of the related doc and the document's position (the document will be
+//found in "<a href=\"_httpdocument_&c=collection&cl=search&d=OID">
+//(where collection is the related documents collection, and OID is the
+//related documents OID).  A list of these html links are made for as many
+//related documents as there are. This list is then returned. If there are
+//no related documents available for the current document then the string
+//'.. no related documents .. ' is returned.
+text_t get_related_docs(const text_t& collection, recptproto* collectproto,
+			   ResultDocInfo_t &docinfo, ostream& logout){
+  
+  text_tset metadata;
+
+  //insert the metadata we wish to collect
+  metadata.insert("relation"); 
+  metadata.insert("Title");  
+  metadata.insert("Subject"); //for emails, where title data doesn't apply
+ 
+  FilterResponse_t response;
+  text_t relation = ""; //string for displaying relation metadata
+  text_t relationTitle = ""; //the related documents Title (or subject)
+  text_t &relationOID = ""; //the related documents OID  
+
+  //get the information associated with the metadata for current doc
+  if (get_info (docinfo.OID, collection, metadata, 
+		false, collectproto, response, logout)) {
+    
+    //if the relation metadata exists, store for displaying
+    if(!response.docInfo[0].metadata["relation"].values.empty()){
+      relationOID += response.docInfo[0].metadata["relation"].values[0];
+
+      //split relation data into pairs of collectionname,ID number
+      text_tarray relationpairs;
+      splitchar (relationOID.begin(), relationOID.end(), ' ', relationpairs);
+      
+      text_tarray::const_iterator currDoc = relationpairs.begin();  
+      text_tarray::const_iterator lastDoc = relationpairs.end();
+
+      //iterate through the pairs to split and display
+      while(currDoc != lastDoc){
+	
+	//split pairs into collectionname and ID 
+	text_tarray relationdata;
+	splitchar ((*currDoc).begin(), (*currDoc).end(), ',', relationdata);
+	
+	//get first element in the array (collection)
+	text_tarray::const_iterator doc_data = relationdata.begin();
+	text_t document_collection = *doc_data;
+	doc_data++; //increment to get next item in array (oid)
+	text_t document_OID = *doc_data;
+	
+	//create html link to related document
+	relation += "<a href=\"_httpdocument_&c=" + document_collection;
+	relation += "&cl=search&d=" + document_OID;
+	   
+	//get the information associated with the metadata for related doc
+	if (get_info (document_OID, document_collection, metadata,
+		      false, collectproto, response, logout)) {
+	  
+	  //if title metadata doesn't exist, collect subject metadata
+	  //if that doesn't exist, just call it 'related document'
+	  if (!response.docInfo[0].metadata["Title"].values[0].empty())
+		relationTitle = response.docInfo[0].metadata["Title"].values[0];
+	  else if (!response.docInfo[0].metadata["Subject"].values.empty())
+	    relationTitle = response.docInfo[0].metadata["Subject"].values[0];
+	  else relationTitle =  "RELATED DOCUMENT";
+	  
+	}
+	
+	//link the related document's title to its page
+	relation += "\">" + relationTitle + "</a>"; 
+	relation += "  (" + document_collection + ")<br>";
+	
+	currDoc++;
+      }
+    }
+    
+  } 
+
+  if(relation.empty()) //no relation data for documnet
+    relation = ".. no related documents .. ";
+
+  return relation;
+}
+
+
 
 static void get_parent_options (text_t &instring, metadata_t &metaoption) {
@@ -238,4 +332,7 @@
   else if (meta == "Text")
     formatlistptr->command = comDoc;
+  
+  else if (meta == "RelatedDocuments")
+   formatlistptr->command = comRel;
 
   else if (meta == "highlight")
@@ -645,4 +742,6 @@
 }
 
+
+
 text_t format_string (const text_t& collection, recptproto* collectproto,
 		      ResultDocInfo_t &docinfo, displayclass &disp, 
@@ -666,4 +765,6 @@
   case comNum:
     return docinfo.result_num;
+  case comRel: //if [RelatedDocuments] appears in format string, collect relation data
+    return get_related_docs(collection, collectproto, docinfo, logout);
   case comMeta:
 
@@ -692,6 +793,5 @@
 		= get_formatted_string(collection, collectproto, 
 				       response.docInfo[0], disp, expanded_formatlistptr, 
-				       link,icon,highlight,
-				       logout);
+				       link, icon, highlight, logout);
 	      
 	      return expanded_metavalue;
Index: /trunk/gsdl/src/recpt/formattools.h
===================================================================
--- /trunk/gsdl/src/recpt/formattools.h	(revision 1940)
+++ /trunk/gsdl/src/recpt/formattools.h	(revision 1941)
@@ -34,5 +34,5 @@
 
 enum command_t {comIf, comOr, comMeta, comText, comLink, comEndLink, comNum, comIcon, 
-		comDoc, comHighlight, comEndHighlight};
+		comDoc, comHighlight, comEndHighlight, comRel};//changed
 enum pcommand_t {pNone, pImmediate, pTop, pAll};
 enum dcommand_t {dMeta, dText};
@@ -88,4 +88,7 @@
   text_tarray DocumentButtons;
   text_t DocumentText;
+  //-----------------
+  text_t RelatedDocuments;
+  //----------------
   text_tmap formatstrings;
   bool DocumentUseHTML;
@@ -102,4 +105,9 @@
 
 text_t format_date (const text_t &date);
+
+
+text_t get_related_docs(const text_t& collection, recptproto* collectproto,
+			  ResultDocInfo_t &docinfo, ostream& logout);
+
 
 bool parse_formatstring (const text_t &formatstring, format_t *formatlistptr, 
Index: /trunk/gsdl/src/recpt/zparse.tab.c
===================================================================
--- /trunk/gsdl/src/recpt/zparse.tab.c	(revision 1940)
+++ /trunk/gsdl/src/recpt/zparse.tab.c	(revision 1941)
@@ -1,6 +1,5 @@
 
 /*  A Bison parser, made from zparse.y
- by  GNU Bison version 1.27
-  */
+    by GNU Bison version 1.28  */
 
 #define YYBISON 1  /* Identify Bison output.  */
@@ -236,5 +235,5 @@
 /* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */
 #line 3 "/usr/share/bison.simple"
-/* This file comes from bison-1.27.  */
+/* This file comes from bison-1.28.  */
 
 /* Skeleton output parser for bison,
@@ -451,5 +450,5 @@
 
 
-#line 216 "/usr/share/bison.simple"
+#line 217 "/usr/share/bison.simple"
 
 /* The user can define YYPARSE_PARAM as the name of an argument to be passed
@@ -944,5 +943,5 @@
 }
    /* the action file gets copied in in place of this dollarsign */
-#line 542 "/usr/share/bison.simple"
+#line 543 "/usr/share/bison.simple"
 
 
