View source with formatted comments or as raw
    1/*  Part of ClioPatria
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2010-2011 University of Amsterdam
    7		             CWI, Asterdam
    8		             VU University Amsterdam
    9    All rights reserved.
   10
   11    Redistribution and use in source and binary forms, with or without
   12    modification, are permitted provided that the following conditions
   13    are met:
   14
   15    1. Redistributions of source code must retain the above copyright
   16       notice, this list of conditions and the following disclaimer.
   17
   18    2. Redistributions in binary form must reproduce the above copyright
   19       notice, this list of conditions and the following disclaimer in
   20       the documentation and/or other materials provided with the
   21       distribution.
   22
   23    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   24    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   25    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   26    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   27    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   28    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   29    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   30    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   31    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   33    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   34    POSSIBILITY OF SUCH DAMAGE.
   35*/
   36
   37:- module(cpa_wiki,
   38	  [ serve_page/2
   39	  ]).   40:- use_module(library(http/http_dispatch)).   41:- use_module(library(http/http_parameters)).   42:- use_module(library(http/http_dirindex)).   43:- use_module(library(http/html_write)).   44:- use_module(library(pldoc/doc_wiki)).   45:- use_module(library(readutil)).   46:- use_module(library(option)).   47:- use_module(library(settings)).   48:- use_module(library(pldoc/doc_index)).   49:- use_module(library(pldoc/doc_html),
   50	      except([ file//2,
   51		       include//3
   52		     ])).   53
   54/** <module> ClioPatria wiki-page server
   55
   56This module serves wiki-pages from (by default) cliopatria(web/help). If
   57the user requests an X.html page, it runs SWI-Prolog's PlDoc wiki-engine
   58over the associated X.txt file.
   59
   60@see The file load.pl binds this functionality to cliopatria(web/help).
   61*/
   62
   63:- setting(http:index_files,
   64	   list(atom),
   65	   [ 'index.txt', 'index.html' ],
   66	   'List of files that provide a directory index').   67
   68%
   69%	serve_page(+Alias, +Request)
   70%
   71%	HTTP handler for files below the file-path Alias. .txt files are
   72%	served as Wiki-pages. All other files   are  served according to
   73%	the rules of http_reply_file/3. To serve   a directory, one must
   74%	create a file search path for it   and decide on the location in
   75%	the web-hierarchy. Here is an example that serves files from the
   76%	subdirectory =www= below  the  search-path   =myapp=  from  HTTP
   77%	locations below =|/web|=.
   78%
   79%	    ==
   80%	    user:file_search_path(web_files, myapp(www)).
   81%
   82%	    :- http_handler(root(web), serve_page(web_files), [prefix]).
   83%	    ==
   84
   85serve_page(Alias, Request) :-
   86	memberchk(path_info(Relative), Request),
   87	Spec =.. [ Alias, Relative ],
   88	http_safe_file(Spec, []),
   89	find_file(Spec, File), !,
   90	setup_call_cleanup(b_setval(doc_alias, Alias),
   91			   serve_file(File, Request),
   92			   nb_delete(Alias)).
   93serve_page(Alias, Request) :-
   94	\+ memberchk(path_info(_), Request), !,
   95	serve_page(Alias, [path_info('index.html')|Request]).
   96serve_page(_, Request) :-
   97	http_404([], Request).
   98
   99%%	find_file(+Spec, -File) is semidet.
  100%
  101%	Translate Spec into a File  in   the  document-root tree. If the
  102%	given extension is .html, also look for   .txt files that can be
  103%	translated into HTML.
  104
  105find_file(Spec, File) :-
  106	spec_replace_extension(Spec, html, txt, TxtSpec),
  107	absolute_file_name(TxtSpec,
  108			   File,
  109			   [ access(read),
  110			     file_errors(fail)
  111			   ]), !.
  112find_file(Spec, File) :-
  113	absolute_file_name(Spec,
  114			   File,
  115			   [ access(read),
  116			     file_errors(fail)
  117			   ]).
  118find_file(Spec, File) :-
  119	absolute_file_name(Spec,
  120			   File,
  121			   [ access(read),
  122			     file_errors(fail),
  123			     file_type(directory)
  124			   ]).
  125
  126spec_replace_extension(File0, Ext0, Ext, File) :-
  127	atomic(File0), !,
  128	file_name_extension(Base, Ext0, File0),
  129	file_name_extension(Base, Ext, File).
  130spec_replace_extension(Comp0, Ext0, Ext, Comp) :-
  131	Comp0 =.. [Alias,Inside0],
  132	spec_replace_extension(Inside0, Ext0, Ext, Inside),
  133	Comp =.. [Alias,Inside].
  134
  135%%	serve_file(+File, +Request) is det.
  136%%	serve_file(+Extension, +File, +Request) is det.
  137%
  138%	Serve the requested file.
  139
  140serve_file(File, Request) :-
  141	file_name_extension(_, Ext, File),
  142	debug(plweb, 'Serving ~q; ext=~q', [File, Ext]),
  143	serve_file(Ext, File, Request).
  144
  145serve_file('',  Dir, Request) :-
  146	exists_directory(Dir), !,
  147	(   sub_atom(Dir, _, _, 0, /),
  148	    serve_index_file(Dir, Request)
  149	->  true
  150	;   http_reply_dirindex(Dir, [unsafe(true)], Request)
  151	).
  152serve_file(txt, File, Request) :-
  153	http_parameters(Request,
  154			[ format(Format, [ oneof([raw,html]),
  155					   default(html)
  156					 ])
  157			]),
  158	Format == html, !,
  159	read_file_to_codes(File, String, []),
  160	setup_call_cleanup(b_setval(pldoc_file, File),
  161			   serve_wiki(String, File, Request),
  162			   nb_delete(pldoc_file)).
  163serve_file(_Ext, File, Request) :-	% serve plain files
  164	http_reply_file(File, [unsafe(true)], Request).
  165
  166%%	serve_index_file(+Dir, +Request) is semidet.
  167%
  168%	Serve index.txt or index.html, etc. if it exists.
  169
  170serve_index_file(Dir, Request) :-
  171        setting(http:index_files, Indices),
  172        member(Index, Indices),
  173	ensure_slash(Dir, DirSlash),
  174	atom_concat(DirSlash, Index, File),
  175        access_file(File, read), !,
  176        serve_file(File, Request).
  177
  178ensure_slash(Dir, Dir) :-
  179	sub_atom(Dir, _, _, 0, /), !.
  180ensure_slash(Dir0, Dir) :-
  181	atom_concat(Dir0, /, Dir).
  182
  183
  184%%	serve_wiki(+String, +File, +Request) is det.
  185%
  186%	Emit page from wiki content in String.
  187
  188serve_wiki(String, File, Request) :-
  189	wiki_codes_to_dom(String, [], DOM0),
  190	(   sub_term(h1(_, Title), DOM0)
  191	->  true
  192	;   Title = 'SWI-Prolog'
  193	),
  194	insert_edit_button(DOM0, File, Request, DOM),
  195	setup_call_cleanup(b_setval(pldoc_options,
  196				    [ prefer(manual)
  197				    ]),
  198			   serve_wiki_page(Title, DOM),
  199			   nb_delete(pldoc_options)).
  200
  201serve_wiki_page(Title, DOM) :-
  202	reply_html_page(pldoc(wiki),
  203			[ title(Title)
  204			],
  205			DOM).
  206
  207insert_edit_button(DOM, _, Request, DOM) :-
  208	\+ catch(http:authenticate(pldoc(edit), Request, _), _, fail), !.
  209insert_edit_button([h1(Attrs,Title)|DOM], File, _,
  210		   [h1(Attrs,[ span(style('float:right'),
  211				   \edit_button(File, [edit(true)]))
  212			     | Title
  213			     ])|DOM]) :- !.
  214insert_edit_button(DOM, File, _,
  215		   [ h1(class(wiki),
  216			[ span(style('float:right'),
  217			       \edit_button(File, [edit(true)]))
  218			])
  219		   | DOM
  220		   ]).
  221
  222
  223:- public				% Called through wiki \Term
  224	include//3,
  225	file//2.  226
  227		 /*******************************
  228		 *	     RENDERING		*
  229		 *******************************/
  230
  231%%	include(+Object, +Type, +Options)//
  232
  233include(Object, Type, Options) -->
  234	pldoc_html:include(Object, Type,
  235			   [ map_extension([txt-html])
  236			   | Options
  237			   ]).
  238
  239%%	file(+Path, Options)//
  240%
  241%	Trap translation of \file(+Path,  Options).   The  first  clause
  242%	reduces the label of the file to the plain file-name if the file
  243%	is inside the help-system.
  244
  245file(Path, Options) -->
  246	{ \+ option(label(_), Options),
  247	  file_name_extension(Base, txt, Path),
  248	  option(absolute_path(AbsPath), Options),
  249	  current_alias_root(DocRoot),
  250	  sub_atom(AbsPath, 0, _, _, DocRoot), !,
  251	  file_base_name(Base, Label),
  252	  file_href(Options, Options1)
  253	},
  254	pldoc_html:file(Path,
  255			[ label(Label),
  256			  map_extension([txt-html])
  257			| Options1
  258			]).
  259file(File, Options) -->
  260	{ file_href(Options, Options1)
  261	},
  262	pldoc_html:file(File,
  263			[ map_extension([txt-html])
  264			| Options1
  265			]).
  266
  267
  268file_href(Options0, Options) :-
  269	\+ ( nb_current(pldoc_file, CFile),
  270	     CFile \== []
  271	   ),
  272	option(absolute_path(Path), Options0),
  273	current_alias_root(DocRoot),
  274	atom_concat(DocRoot, DocLocal, Path), !,
  275	ensure_leading_slash(DocLocal, HREF),
  276	Options = [ href(HREF) | Options0 ].
  277file_href(Options0, Options) :-
  278	nb_current(pldoc_file, CFile),
  279	CFile \== [],
  280	option(absolute_path(Path), Options0),
  281	plfile_href(Path, HREF),
  282	Options = [ href(HREF)|Options0 ].
  283file_href(Options, Options).
  284
  285%%	plfile_href(+Path, -HREF) is det.
  286%
  287%	Create a link for a file to see  the (pretty) source if the file
  288%	is inside the help system. Otherwise create a normal PlDoc link.
  289
  290plfile_href(Path, HREF) :-
  291	file_name_extension(_, Ext, Path),
  292	prolog_file_type(Ext, prolog),
  293	current_alias_root(DocRoot),
  294	sub_atom(Path, 0, _, _, DocRoot), !,
  295	doc_file_href(Path, HREF0),
  296	atom_concat(HREF0, '?show=src', HREF).
  297plfile_href(Path, HREF) :-
  298	doc_file_href(Path, HREF).
  299
  300%%	current_alias_root(-Root)
  301%
  302%	Root is the root of the current file-alias we are served from.
  303
  304current_alias_root(DocRoot) :-
  305	(   nb_current(doc_alias, Val), Val \== []
  306	->  Alias = Val
  307	;   Alias = document_root
  308	),
  309	Term =.. [Alias,'.'],
  310	absolute_file_name(Term,
  311			   DocRoot,
  312			   [ file_type(directory),
  313			     access(read)
  314			   ]).
  315
  316
  317ensure_leading_slash(Path, SlashPath) :-
  318	(   sub_atom(Path, 0, _, _, /)
  319	->  SlashPath = Path
  320	;   atom_concat(/, Path, SlashPath)
  321	)