835c6c2001-06-17Martin Nilsson // This file is part of Roxen WebServer.
b655bf2004-06-30Martin Stjernholm // Copyright © 2000 - 2004, Roxen IS.
835c6c2001-06-17Martin Nilsson // // RXML Help by Martin Nilsson
cface32000-01-10Martin Nilsson //
3b194c2001-08-10Per Hedbor // inherited by configuration.pike
f5cadf2001-08-17Per Hedbor #define parse_rxml Roxen.parse_rxml
3b194c2001-08-10Per Hedbor 
2feec42000-01-24Martin Nilsson #ifdef RXMLHELP_DEBUG
91d3c32001-03-12Martin Nilsson # define RXMLHELP_WERR(X) report_debug("RXML help: %s\n", X);
2feec42000-01-24Martin Nilsson #else # define RXMLHELP_WERR(X) #endif
a104662000-01-25Martin Nilsson // --------------------- Layout help functions --------------------
e8ae642000-03-16Martin Nilsson #define TDBG "#d9dee7"
a104662000-01-25Martin Nilsson string mktable(array table) { string ret="<table boder=\"0\" cellpadding=\"0\" border=\"0\"><tr><td bgcolor=\"#000000\">\n" "<table border=\"0\" cellspacing=\"1\" cellpadding=\"5\">\n"; foreach(table, array row)
e8ae642000-03-16Martin Nilsson  ret+="<tr valign=\"top\"><td bgcolor=\""+TDBG+"\"><font color=\"#000000\">"+ row*("</font></td><td bgcolor=\""+TDBG+"\"><font color=\"#000000\">")+"</font></td></tr>\n";
a104662000-01-25Martin Nilsson  ret+="</table></tr></td></table>"; return ret; } string available_languages(object id) {
e40bf72000-01-28Martin Nilsson  string pl;
78be972001-04-23Martin Nilsson  if(id && id->misc->pref_languages && (pl=id->misc->pref_languages->get_language()))
b09a282000-03-21Martin Nilsson  if(!has_value(roxen->list_languages(),pl)) pl="en";
e40bf72000-01-28Martin Nilsson  else pl="en";
b09a282000-03-21Martin Nilsson  mapping languages=roxen->language_low(pl)->list_languages();
b0e8f92000-03-22Martin Nilsson  return mktable( map(sort(indices(languages) & roxen->list_languages()), lambda(string code) { return ({ code, languages[code] }); } ));
a104662000-01-25Martin Nilsson } // --------------------- Help layout functions --------------------
cface32000-01-10Martin Nilsson 
6cc5682002-08-13Martin Stjernholm class TagdocParser { inherit Parser.HTML; mapping misc = ([]); } static string desc_cont(TagdocParser parser, mapping m, string c, string rt)
cface32000-01-10Martin Nilsson {
9044642001-09-21Johan Sundström  string type; if(m->tag) type = "tag"; if(m->cont) type = "cont"; if(m->cont && m->tag) type = "both"; if(m->plugin) type = "plugin"; if(m->ent) type = "entity"; if(m->scope) type = "scope"; if(m->pi) type = "pi"; if(m->type) type = m->type; switch(type) { case "tag": rt = sprintf("&lt;%s/&gt;", rt); break;
b1b7e02001-12-04Martin Stjernholm  case "cont": rt = sprintf("&lt;%s&gt;&lt;/%s&gt;", rt, rt); break; case "both": rt = sprintf("&lt;%s/&gt; or "
9044642001-09-21Johan Sundström  "&lt;%s&gt;&lt;/%s&gt;", rt, rt, rt); break; case "plugin": rt = replace(rt, "#", " plugin "); break; //case "entity": rt = rt; break; case "scope": rt = rt[..sizeof(rt)-2] + " ... ;"; case "pi": rt = "&lt;" + rt + " ... ?&gt;";
49ee2d2000-03-07Martin Nilsson  }
9044642001-09-21Johan Sundström  return sprintf("<h2>%s</h2><p>%s</p>", rt, c);
cface32000-01-10Martin Nilsson }
6cc5682002-08-13Martin Stjernholm static string attr_cont(TagdocParser parser, mapping m, string c)
cface32000-01-10Martin Nilsson { string p=""; if(!m->name) m->name="(Not entered)";
ec8b312000-03-30Martin Nilsson  if(m->value) p=sprintf("<i>%s=%s</i>%s<br />", m->name, attr_vals(m->value), m->default?" ("+m->default+")":"" ); if(m->required) p+="<i>This attribute is required.</i><br />";
2430242003-05-20Anders Johansson  p = sprintf("<p><dl><dt><b>%s</b></dt><dd>%s%s</dd></dl></p>",m->name,p,c);
6cc5682002-08-13Martin Stjernholm  if (!parser->misc->got_attrs) { parser->misc->got_attrs = 1; p = "<h3>Attributes</h3>\n" + p; } return p;
cface32000-01-10Martin Nilsson }
5673a32000-07-23Martin Nilsson static string attr_vals(string v)
cface32000-01-10Martin Nilsson {
b09a282000-03-21Martin Nilsson  if(has_value(v,"|")) return "{"+(v/"|")*", "+"}"; // FIXME Use real config url // if(v=="langcodes") return "<a href=\"/help/langcodes.pike\">language code</a>";
cface32000-01-10Martin Nilsson  return v; }
6cc5682002-08-13Martin Stjernholm static string noex_cont(TagdocParser parser, mapping m, string c) {
4a45f12001-03-13Martin Nilsson  return Parser.HTML()->add_container("ex","")-> add_quote_tag("!--","","--")->feed(c)->read();
b0e8f92000-03-22Martin Nilsson }
4acd362001-05-18Martin Nilsson static string ex_quote(string in) {
94d8d02003-05-06Anders Johansson  string s = replace(in, "&lt;", "__LT__"); s = "<pre>"+replace(s, ({"<",">","&"}), ({"&lt;","&gt;","&amp;"}) )+"</pre>"; return replace(s, "__LT__", "&lt;");
4acd362001-05-18Martin Nilsson }
6cc5682002-08-13Martin Stjernholm static string ex_cont(TagdocParser parser, mapping m, string c, string rt, void|object id)
9dcf432000-01-23Martin Nilsson {
fa78ad2001-08-23Martin Nilsson  c=Parser.HTML()->add_container("ent", lambda(Parser.HTML parser, mapping m, string c) {
c7da172001-01-29Per Hedbor  return "&amp;"+c+";";
4a45f12001-03-13Martin Nilsson  } )-> add_quote_tag("!--","","--")->feed(c)->read();
4acd362001-05-18Martin Nilsson  string quoted = ex_quote(c);
69f7112000-05-30Martin Nilsson  if(m->type=="box")
0d1bb22000-05-30Martin Nilsson  return "<br />"+mktable( ({ ({ quoted }) }) );
69f7112000-05-30Martin Nilsson 
9dcf432000-01-23Martin Nilsson  if(!id) return "";
69f7112000-05-30Martin Nilsson 
e8ae642000-03-16Martin Nilsson  string parsed=
3b194c2001-08-10Per Hedbor  parse_rxml(m->type!="hr"? "<colorscope bgcolor="+TDBG+">"+c+"</colorscope>": c, id);
9dcf432000-01-23Martin Nilsson  switch(m->type) { case "hr":
0d1bb22000-05-30Martin Nilsson  return quoted+"<hr />"+parsed;
4acd362001-05-18Martin Nilsson  case "svert": return "<br />" + mktable( ({ ({ quoted }), ({ ex_quote(parsed) }) }) ); case "shor": return "<br />" + mktable( ({ ({ quoted, ex_quote(parsed) }) }) );
9dcf432000-01-23Martin Nilsson  case "vert":
7580012001-09-03Martin Nilsson  default:
0d1bb22000-05-30Martin Nilsson  return "<br />"+mktable( ({ ({ quoted }), ({ parsed }) }) );
9dcf432000-01-23Martin Nilsson  case "hor":
0d1bb22000-05-30Martin Nilsson  return "<br />"+mktable( ({ ({ quoted, parsed }) }) );
9dcf432000-01-23Martin Nilsson  } }
6cc5682002-08-13Martin Stjernholm static string ex_box_cont(TagdocParser parser, mapping m, string c, string rt) {
7580012001-09-03Martin Nilsson  return "<br />"+mktable( ({ ({ ex_quote(c) }) }) ); }
6cc5682002-08-13Martin Stjernholm static string ex_html_cont(TagdocParser parser, mapping m, string c, string rt) {
953f172001-09-10Martin Nilsson  return "<br />" + mktable( ({ ({ c }) }) ); }
6cc5682002-08-13Martin Stjernholm static string ex_src_cont(TagdocParser parser, mapping m, string c, string rt, void|object id) {
7580012001-09-03Martin Nilsson  string quoted = ex_quote(c);
9044642001-09-21Johan Sundström  string parsed = parse_rxml("<colorscope bgcolor="+TDBG+">"+c+"</colorscope>", id);
7580012001-09-03Martin Nilsson  return "<br />" + mktable( ({ ({ quoted }), ({ ex_quote(parsed) }) }) ); }
6cc5682002-08-13Martin Stjernholm static string list_cont( TagdocParser parser, mapping m, string c )
9a669a2000-09-19Per Hedbor {
1399572001-11-26Anders Johansson  string type = m->type || "ul"; return "<"+type+">"+ Parser.HTML()-> add_containers( ([ "item":lambda(Parser.HTML p, mapping m, string c) { return ({ "<li>"+ (m->name ? "<b>"+m->name+"</b><br />" : "")+ c+"</li>" }); } ]) )->finish(c)->read()+ "</"+type+">";
9a669a2000-09-19Per Hedbor } static string xtable_cont( mixed a, mixed b, string c ) {
9044642001-09-21Johan Sundström  return "<table border='1' cellpadding='2' cellspacing='0'>"+c+"</table>";
9a669a2000-09-19Per Hedbor } static string module_cont( mixed a, mixed b, string c ) { return "<i>"+c+"</i>"; } static string xtable_row_cont( mixed a, mixed b, string c ) { return "<tr>"+c+"</tr>"; } static string xtable_c_cont( mixed a, mixed b, string c ) { return "<td>"+c+"</td>"; }
9044642001-09-21Johan Sundström static string xtable_h_cont( mixed a, mixed b, string c ) { return "<th>"+c+"</th>"; }
9a669a2000-09-19Per Hedbor static string help_tag( mixed a, mapping m, string c ) { if( m["for"] )
cac8242001-08-17Per Hedbor  return find_tag_doc( m["for"], RXML.get_context()->id,0,1 );
9a669a2000-09-19Per Hedbor  return 0; // keep. }
93f7342002-11-26Anders Johansson static string webserver_tag( mixed a, mixed b, string c ) { return roxen_product_name; }
9a669a2000-09-19Per Hedbor  static string format_doc(string|mapping doc, string name, void|object id) {
9dcf432000-01-23Martin Nilsson  if(mappingp(doc)) {
a104662000-01-25Martin Nilsson  if(id && id->misc->pref_languages) {
9a669a2000-09-19Per Hedbor  foreach(id->misc->pref_languages->get_languages()+({"en"}), string code) {
e3b9b92000-02-23Martin Nilsson  object lang=roxen->language_low(code);
e40bf72000-01-28Martin Nilsson  if(lang) { array lang_id=lang->id(); if(doc[lang_id[2]]) { doc=doc[lang_id[2]]; break; } if(doc[lang_id[1]]) { doc=doc[lang_id[1]]; break; } }
dfe0642000-01-26Martin Nilsson  }
9dcf432000-01-23Martin Nilsson  } else doc=doc->standard; }
e40bf72000-01-28Martin Nilsson 
c7da172001-01-29Per Hedbor  name=replace(name, ({ "<", ">", "&" }), ({ "&lt;", "&gt;", "&amp;" }) );
b2401f2000-08-31Martin Nilsson 
6cc5682002-08-13Martin Stjernholm  return TagdocParser()->
9a669a2000-09-19Per Hedbor  add_tag( "lang",lambda() { return available_languages(id); } )-> add_tag( "help", help_tag )->
93f7342002-11-26Anders Johansson  add_tag( "webserver", webserver_tag )->
9a669a2000-09-19Per Hedbor  add_containers( ([ "list":list_cont, "xtable":xtable_cont, "row":xtable_row_cont, "c":xtable_c_cont,
9044642001-09-21Johan Sundström  "h":xtable_h_cont,
9a669a2000-09-19Per Hedbor  "module":module_cont, "desc":desc_cont, "attr":attr_cont, "ex":ex_cont,
7580012001-09-03Martin Nilsson  "ex-box":ex_box_cont, "ex-src":ex_src_cont,
953f172001-09-10Martin Nilsson  "ex-html":ex_html_cont,
9a669a2000-09-19Per Hedbor  "noex":noex_cont,
6cc5682002-08-13Martin Stjernholm  "tag":lambda(TagdocParser p, mapping m, string c) {
fa78ad2001-08-23Martin Nilsson  return ({ "&lt;"+c+"&gt;" });
9a669a2000-09-19Per Hedbor  },
6cc5682002-08-13Martin Stjernholm  "ent":lambda(TagdocParser p, mapping m, string c) {
fa78ad2001-08-23Martin Nilsson  return ({ "&amp;" + c + ";" }); },
6cc5682002-08-13Martin Stjernholm  "xref":lambda(TagdocParser p, mapping m, string c) {
46ae1a2001-08-28Martin Nilsson  if( (!c || !sizeof(c)) && m->href ) { c = m->href; sscanf(c, "%s.tag", c); return replace((c/"/")[-1], "_", " "); } return c; },
6cc5682002-08-13Martin Stjernholm  "short":lambda(TagdocParser p, mapping m, string c) {
9a669a2000-09-19Per Hedbor  return m->hide?"":c; },
6cc5682002-08-13Martin Stjernholm  "note":lambda(TagdocParser p, mapping m, string c) {
84154a2001-10-05Martin Nilsson  return c; },
9a669a2000-09-19Per Hedbor  ]) )->
4a45f12001-03-13Martin Nilsson  add_quote_tag("!--","","--")->
fa78ad2001-08-23Martin Nilsson  set_extra(name, id)->finish(doc)->read();
cface32000-01-10Martin Nilsson }
c284a02000-01-21Martin Nilsson 
cface32000-01-10Martin Nilsson // ------------------ Parse docs in mappings --------------
5673a32000-07-23Martin Nilsson static string parse_doc(string|mapping|array doc, string name, void|object id) {
cac8242001-08-17Per Hedbor  if(arrayp(doc) && (sizeof( doc ) == 2) )
9dcf432000-01-23Martin Nilsson  return format_doc(doc[0], name, id)+ "<dl><dd>"+parse_mapping(doc[1], id)+"</dd></dl>";
cac8242001-08-17Per Hedbor  if( arrayp( doc ) && sizeof(doc) ) return format_doc( doc[0], name, id );
9dcf432000-01-23Martin Nilsson  return format_doc(doc, name, id);
c284a02000-01-21Martin Nilsson }
5673a32000-07-23Martin Nilsson static string parse_mapping(mapping doc, void|object id) {
cface32000-01-10Martin Nilsson  string ret=""; if(!mappingp(doc)) return "";
e3b9b92000-02-23Martin Nilsson  foreach(sort(indices(doc)), string tmp) {
9dcf432000-01-23Martin Nilsson  ret+=parse_doc(doc[tmp], tmp, id);
cface32000-01-10Martin Nilsson  } return ret; }
6afce22001-01-16Martin Nilsson string parse_all_doc(RoxenModule o, void|RequestID id) { mapping doc = call_tagdocumentation(o); if(!doc) return 0; string ret = ""; foreach(sort(indices(doc)), string tagname) ret += parse_doc(doc[tagname], tagname, id); return ret; }
c284a02000-01-21Martin Nilsson 
cface32000-01-10Martin Nilsson // --------------------- Find documentation --------------
c284a02000-01-21Martin Nilsson mapping call_tagdocumentation(RoxenModule o) {
c8a9d12000-03-30Martin Nilsson  if(!o->tagdocumentation) return 0;
78be972001-04-23Martin Nilsson  string name = o->register_module()[1];
d95f6f2000-01-26Martin Nilsson 
c8a9d12000-03-30Martin Nilsson  mapping doc; if(!zero_type(doc=cache_lookup("tagdoc", name)))
d95f6f2000-01-26Martin Nilsson  return doc;
2feec42000-01-24Martin Nilsson  doc=o->tagdocumentation(); RXMLHELP_WERR(sprintf("tagdocumentation() returned %t.",doc));
d95f6f2000-01-26Martin Nilsson  if(!doc || !mappingp(doc)) {
c8a9d12000-03-30Martin Nilsson  cache_set("tagdoc", name, 0);
d95f6f2000-01-26Martin Nilsson  return 0; }
39e0362000-08-30Martin Nilsson  RXMLHELP_WERR("("+String.implode_nicely(indices(doc))+")");
c8a9d12000-03-30Martin Nilsson  cache_set("tagdoc", name, doc);
c284a02000-01-21Martin Nilsson  return doc; }
dbbfdf2000-01-12Martin Nilsson 
5673a32000-07-23Martin Nilsson static int generation;
9dcf432000-01-23Martin Nilsson multiset undocumented_tags=(<>);
cac8242001-08-17Per Hedbor string find_tag_doc(string name, RequestID id, int|void no_undoc, int|void reenter)
f5cadf2001-08-17Per Hedbor {
124c202000-01-24Martin Nilsson  RXMLHELP_WERR("Help for tag "+name+" requested.");
f5cadf2001-08-17Per Hedbor 
cac8242001-08-17Per Hedbor  object old_ctx = RXML.get_context();
f5cadf2001-08-17Per Hedbor  if( !id ) error("find_tag_doc called without ID-object\n");
cac8242001-08-17Per Hedbor  if( !reenter ) parse_rxml( "", id );
f5cadf2001-08-17Per Hedbor  RXML.TagSet tag_set = id->conf->rxml_tag_set;
3b194c2001-08-10Per Hedbor 
c284a02000-01-21Martin Nilsson  string doc; int new_gen=tag_set->generation;
f5cadf2001-08-17Per Hedbor  if(generation!=new_gen) {
9dcf432000-01-23Martin Nilsson  undocumented_tags=(<>);
c284a02000-01-21Martin Nilsson  generation=new_gen; }
39e0362000-08-30Martin Nilsson  array tags;
7b95522001-08-23Martin Nilsson  if(name[0]=='?') { RXMLHELP_WERR("<"+name+"?> is a processing instruction."); object tmp=tag_set->get_tag(name[1..], 1);
39e0362000-08-30Martin Nilsson  if(tmp) tags=({ tmp }); else tags=({}); } else tags=tag_set->get_overridden_tags(name);
3b194c2001-08-10Per Hedbor  if(!sizeof(tags)) {
cac8242001-08-17Per Hedbor  if( !reenter ) RXML.set_context( old_ctx );
3b194c2001-08-10Per Hedbor  return no_undoc ? "" : "<h4>That tag ("+name+") is not defined</h4>"; }
49ee2d2000-03-07Martin Nilsson  string plugindoc="";
c284a02000-01-21Martin Nilsson  foreach(tags, array|object|function tag) { if(objectp(tag)) { // FIXME: New style tag. Check for internal documentation.
49ee2d2000-03-07Martin Nilsson  mapping(string:RXML.Tag) plugins=tag_set->get_plugins(name); if(sizeof(plugins)) { plugindoc="<hr /><dl><dd>"; foreach(sort(indices(plugins)), string plugin)
cac8242001-08-17Per Hedbor  plugindoc+=find_tag_doc(name+"#"+plugin, id,no_undoc,1);
49ee2d2000-03-07Martin Nilsson  plugindoc+="</dd></dl>"; }
5e71002000-12-19Anders Johansson  if(tag->is_compat_tag) { RXMLHELP_WERR(sprintf("CompatTag %O", tag)); tag=tag->fn; } else if(tag->is_generic_tag) { RXMLHELP_WERR(sprintf("GenericTag %O", tag));
e8ae642000-03-16Martin Nilsson  tag=tag->_do_return;
5e71002000-12-19Anders Johansson  } else { RXMLHELP_WERR(sprintf("NormalTag %O", tag));
e8ae642000-03-16Martin Nilsson  tag=object_program(tag);
5e71002000-12-19Anders Johansson  }
c284a02000-01-21Martin Nilsson  }
e8ae642000-03-16Martin Nilsson  else if(arrayp(tag)) {
c284a02000-01-21Martin Nilsson  if(tag[0]) tag=tag[0][1]; else if(tag[1]) tag=tag[1][1]; }
e8ae642000-03-16Martin Nilsson  else continue;
b0e8f92000-03-22Martin Nilsson  if(!functionp(tag)) continue;
c284a02000-01-21Martin Nilsson  tag=function_object(tag); if(!objectp(tag)) continue;
2feec42000-01-24Martin Nilsson  RXMLHELP_WERR(sprintf("Tag defined in module %O", tag));
c284a02000-01-21Martin Nilsson  mapping tagdoc=call_tagdocumentation(tag);
7b95522001-08-23Martin Nilsson  if(!tagdoc || !tagdoc[name]) { RXMLHELP_WERR(name+" not present in result."); continue; }
3b194c2001-08-10Per Hedbor  string res = parse_doc(tagdoc[name], name, id)+plugindoc;
cac8242001-08-17Per Hedbor  if( !reenter ) RXML.set_context( old_ctx );
3b194c2001-08-10Per Hedbor  return res;
c284a02000-01-21Martin Nilsson  }
9dcf432000-01-23Martin Nilsson  undocumented_tags[name]=1;
49ee2d2000-03-07Martin Nilsson  if(has_value(name,"#")) { sscanf(name,"%*s#%s", name); name="plugin "+name; }
cac8242001-08-17Per Hedbor  if( !reenter ) RXML.set_context( old_ctx );
3b194c2001-08-10Per Hedbor  return (no_undoc ? "" : "<h4>No documentation available for \""+name+"\".</h4>\n");
cface32000-01-10Martin Nilsson }
2feec42000-01-24Martin Nilsson string find_module_doc( string cn, string mn, RequestID id )
cface32000-01-10Martin Nilsson {
124c202000-01-24Martin Nilsson  RXMLHELP_WERR("Help for module "+mn+" requested.");
cface32000-01-10Martin Nilsson  object c = roxen.find_configuration( cn );
2feec42000-01-24Martin Nilsson  if(!c) return "";
cface32000-01-10Martin Nilsson 
2feec42000-01-24Martin Nilsson  RoxenModule o = c->find_module( replace(mn,"!","#") );
124c202000-01-24Martin Nilsson  if(!o) return "";
cface32000-01-10Martin Nilsson 
2feec42000-01-24Martin Nilsson  return parse_mapping(o->tagdocumentation());
cface32000-01-10Martin Nilsson }