123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451(*********************************************************************************)(* Stog *)(* *)(* Copyright (C) 2012-2015 INRIA All rights reserved. *)(* Author: Maxence Guesdon, INRIA Saclay *)(* *)(* 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, version 3 of the License. *)(* *)(* 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., 59 Temple Place, Suite 330, Boston, MA *)(* 02111-1307 USA *)(* *)(* As a special exception, you have permission to link this program *)(* with the OCaml compiler and distribute executables, as long as you *)(* follow the requirements of the GNU GPL in regard to all of the *)(* software in the executable aside from the OCaml compiler. *)(* *)(* Contact: Maxence.Guesdon@inria.fr *)(* *)(*********************************************************************************)(** *)moduleXR=Xtmpl.RewritemoduleXml=Xtmpl.Xmltypedate=Date.ttypebody=XR.treelisttypedef=XR.name*XR.attributes*bodyletget_def=letpname(s,_,_)=s=nameinfundefsname->trylet(_,args,body)=List.find(pname)defsinSome(args,body)withNot_found->None;;moduleStr_map=Map.Make(structtypet=stringletcompare=String.compareend);;moduleStr_set=Set.Make(structtypet=stringletcompare=String.compareend);;typedoc={doc_path:Path.path;doc_parent:Path.pathoption;doc_children:Path.pathlist;doc_type:string;doc_prolog:Xml.prologoption;doc_body:body;doc_date:dateoption;doc_title:string;doc_keywords:stringlist;doc_topics:stringlist;doc_defs:deflist;doc_src:string;doc_sets:stringlist;doc_lang_dep:bool;doc_out:bodyoption;doc_used_mods:Str_set.t;}anddoc_id=docTmap.keyletmake_doc?(typ="dummy")?(path=Path.path[]false)?prolog()={doc_path=path;doc_parent=None;doc_children=[];doc_type=typ;doc_prolog=prolog;doc_body=[];doc_date=None;doc_title="";doc_keywords=[];doc_topics=[];doc_defs=[];doc_src="/tmp";doc_sets=[];doc_lang_dep=true;doc_out=None;doc_used_mods=Str_set.empty;};;modulePath_trie=Trie.Make(structtypet=stringletcompare=compareend);;moduleDoc_set=Set.Make(structtypet=doc_idletcompare=Tmap.compare_keyend);;moduleDoc_map=Set.Make(structtypet=doc_idletcompare=Tmap.compare_keyend);;moduleInt_map=Map.Make(structtypet=intletcompare=compareend);;moduleInt_set=Set.Make(structtypet=intletcompare=compareend);;typeedge_type=Date|Topicofstring|Keywordofstring|Ref;;moduleGraph=Graph.Make_with_map(structtypet=doc_idletcompare=Tmap.compare_keyend)(structtypet=edge_typeletcompare=Stdlib.compareend);;typefile_tree={files:Str_set.t;dirs:file_treeStr_map.t;}typestog_mod={mod_requires:Str_set.t;mod_defs:deflist;}type'adependency=Fileofstring|Docof'a;;moduleDepset=Set.Make(structtypet=stringdependencyletcompare=Stdlib.compareend)typestog_dependencies=Depset.tStr_map.t;;typestog={stog_dir:string;stog_docs:(doc,doc)Tmap.t;stog_docs_by_path:doc_idPath_trie.t;stog_defs:deflist;stog_tmpl_dirs:stringlist;stog_mod_dirs:stringlist;stog_cache_dir:string;stog_title:string;stog_desc:body;stog_graph:Graph.t;stog_docs_by_kw:Doc_set.tStr_map.t;stog_docs_by_topic:Doc_set.tStr_map.t;stog_archives:Doc_set.tInt_map.tInt_map.t;(* year -> month -> article set *)stog_base_url:Url.t;stog_email:string;stog_rss_length:int;stog_lang:stringoption;stog_outdir:string;stog_main_doc:doc_idoption;stog_files:file_tree;stog_modules:stog_modStr_map.t;stog_used_mods:Str_set.t;stog_depcut:bool;stog_deps:stog_dependencies;stog_id_map:(Path.path*stringoption)Str_map.tPath.Map.t;stog_levels:(string*intlist)listStr_map.t;stog_publish_only:Filter_types.toption;stog_source:[`Dir|`File];}letcreate_stog?(source=`Dir)dir={stog_dir=dir;stog_docs=Tmap.create(make_doc());stog_docs_by_path=Path_trie.empty;stog_tmpl_dirs=[Config.tmpl_dirdir];stog_mod_dirs=[Config.modules_dirdir;List.hdInstall.Sites.modules];stog_cache_dir=Config.cache_dirdir;stog_title="";stog_desc=[];stog_graph=Graph.create();stog_docs_by_kw=Str_map.empty;stog_docs_by_topic=Str_map.empty;stog_archives=Int_map.empty;stog_base_url=Url.of_string"http://yoursite.net";stog_email="foo@bar.com";stog_rss_length=10;stog_defs=[];stog_lang=None;stog_outdir=".";stog_main_doc=None;stog_files={files=Str_set.empty;dirs=Str_map.empty};stog_modules=Str_map.empty;stog_used_mods=Str_set.empty;stog_depcut=false;stog_deps=Str_map.empty;stog_id_map=Path.Map.empty;stog_levels=Str_map.empty;stog_publish_only=Some(Filter_types.Not(Filter_types.Or(Filter_types.Pred(("","published"),"false"),Filter_types.Pred(("","published"),"0"))));stog_source=source;};;letstog_md5stog=letstog={stogwithstog_docs=Tmap.create(make_doc());stog_docs_by_path=Path_trie.empty;stog_graph=Graph.create();stog_docs_by_kw=Str_map.empty;stog_docs_by_topic=Str_map.empty;stog_archives=Int_map.empty;stog_files={files=Str_set.empty;dirs=Str_map.empty};stog_depcut=false;}inlets=Digest.string(Marshal.to_stringstog[Marshal.Closures;Marshal.No_sharing])inDigest.to_hexs;;letdocstogid=Tmap.getstog.stog_docsid;;letdocs_by_path?typstogh=letrev_path=List.revh.Path.pathin(*prerr_endline (Printf.sprintf "lookup rev_path=%s" (String.concat "/" rev_path));*)letids=Path_trie.findrev_pathstog.stog_docs_by_pathinletl=List.map(funid->(id,docstogid))idsinletpath_pred(_,doc)=doc.doc_path=h||(matchPath.chop_extensiondoc.doc_pathwithNone->true|Somep->p=h)inletpred=matchh.Path.path_absolute,typwithfalse,None->None|false,Sometyp->Some(fun(_,doc)->doc.doc_type=typ)|true,None->Somepath_pred|true,Sometyp->Some(fun(id,doc)->path_pred(id,doc)&&doc.doc_type=typ)inmatchpredwithNone->l|Somepred->List.filterpredl;;letdoc_by_path?typstogh=matchdocs_by_path?typstoghwith[]->(*prerr_endline (Path_trie.to_string (fun x -> x) stog.stog_docs_by_path);*)failwith(Printf.sprintf"Unknown document %S"(Path.to_stringh))|[x]->x|l->letmsg=Printf.sprintf"More than one document matches %S%s: %s"(Path.to_stringh)(matchtypwithNone->""|Somet->Printf.sprintf" of type %S"t)(String.concat", "(List.map(fun(id,doc)->Path.to_stringdoc.doc_path)l))infailwithmsg;;letdoc_childrenstog=letfpath=snd(doc_by_pathstogpath)infundoc->List.mapfdoc.doc_children;;letset_docstogiddoc=(*prerr_endline (Printf.sprintf "set_doc %d => %s" (Obj.magic id) (Path.to_string doc.doc_path));*){stogwithstog_docs=Tmap.modifystog.stog_docsiddoc};;letadd_path=letadd~failstogpathid=letrev_path=List.revpath.Path.pathinletmap=Path_trie.add~failrev_pathidstog.stog_docs_by_pathinletmap=(*prerr_endline (Printf.sprintf "rev_path=%s" (String.concat "/" rev_path));*)matchrev_pathwith|"index.html"::q|"index"::qwhennotfail->(* if [fail = false] then we already added the path with index.html,
so we do not add the path for index. *)(*prerr_endline (Printf.sprintf "add again %s" (String.concat "/" q));*)(* also make this document accessible without "index" *)Path_trie.add~failqidmap|_->mapin{stogwithstog_docs_by_path=map}infunstogpathid->letstog=add~fail:truestogpathidinmatchPath.chop_extensionpathwithNone->stog|Somepath->add~fail:falsestogpathid;;letadd_docstogdoc=let(id,docs)=Tmap.addstog.stog_docsdocinletstog=add_pathstogdoc.doc_pathidin{stogwithstog_docs=docs;};;letsort_docs_by_datedocs=List.sort(fune1e2->Stdlib.comparee1.doc_datee2.doc_date)docs;;letsort_ids_docs_by_datedocs=List.sort(fun(_,e1)(_,e2)->Stdlib.comparee1.doc_datee2.doc_date)docs;;letsort_ids_docs_by_rules=letapply_fieldenv(data,acc)field=letname=Xtmpl.Xml.name_of_stringfieldinletxml=[XR.nodename[]]inlet(data,xmls)=XR.apply_to_xmlsdataenvxmlin(data,xmls::acc)inletapply_fieldsfields(data,acc)(id,e,env)=let(data,xmls)=List.fold_left(apply_fieldenv)(data,[])fieldsinletxmls=List.flatten(List.revxmls)in(data,(id,e,xmls)::acc)inletcompare(_,e1,v1)(_,e2,v2)=Stdlib.comparev1v2infundatafieldsdocs->let(data,docs)=List.fold_left(apply_fieldsfields)(data,[])docsinletdocs=List.sortcomparedocsin(data,List.map(fun(id,e,_)->(id,e))docs);;letdoc_list?(by_date=false)?setstog=letpred=matchsetwithNone->(fun_->true)|Someset->(fundoc->List.memsetdoc.doc_sets)inletl=Tmap.fold(funiddocacc->ifpreddocthen(id,doc)::accelseacc)stog.stog_docs[]inifby_datethensort_ids_docs_by_datelelsel;;letmerge_stogsstogs=matchstogswith[]->assertfalse|stog::q->letfaccstog=Tmap.fold(fun_docacc->add_docaccdoc)stog.stog_docsaccinList.fold_leftfstogq;;letmake_pathstogstr=letstr=Stog_base.Misc.lowercasestrinletlen=String.lengthstrinletb=Buffer.createleninletreciterdashi=ifi>=lenthenBuffer.contentsbelsematchstr.[i]with'a'..'z'|'A'..'Z'|'0'..'9'|'_'|'-'->Buffer.add_charbstr.[i];iterfalse(i+1)|c->ifdashtheniterdash(i+1)else(Buffer.add_charb'-';itertrue(i+1))inletpath0=itertrue0inletrecitern=letpath=Printf.sprintf"%s%s"path0(ifn=1then""elsestring_of_intn)inletpath=[path]inmatchPath_trie.findpathstog.stog_docs_by_pathwith[]->path|_->iter(n+1)initer1;;exceptionBlock_foundofXR.treeletfind_block_by_id=letrecfind_in_listid=function[]->raiseNot_found|xml::q->tryfindidxmlwithNot_found->find_in_listidqandfindidxml=matchxmlwithXR.D_|XR.C_|XR.PI_->raiseNot_found|XR.E{XR.atts;subs}->matchXR.get_att_cdataatts("","id")withSomeswhens=id->raise(Block_foundxml)|_->find_in_listidsubsinfundocid->trymatchdoc.doc_outwithNone->find_in_listiddoc.doc_body|Somebody->find_in_listidbodywithNot_found->None|Block_foundxml->Somexml;;letid_map_addstogpathidtarget_pathtarget_id=assertpath.Path.path_absolute;asserttarget_path.Path.path_absolute;letmap=tryPath.Map.findpathstog.stog_id_mapwithNot_found->Str_map.emptyinletmap=Str_map.addid(target_path,target_id)mapin{stogwithstog_id_map=Path.Map.addpathmapstog.stog_id_map};;letrecmap_hrefstogpathid=tryletmap=Path.Map.findpathstog.stog_id_mapinmatchStr_map.findidmapwith(path,None)->(path,"")|(path,Someid)->map_hrefstogpathidwithNot_found->(path,id);;letmap_doc_refstogdocid=letpath=doc.doc_pathinlet(path,id)=map_hrefstogpathidinlet(_,doc)=doc_by_pathstogpathin(doc,id);;