View source with formatted comments or as raw
    1/*  Part of ClioPatria SeRQL and SPARQL server
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@cs.vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (C): 2004-2015, University of Amsterdam,
    7			      VU University Amsterdam
    8
    9    This program is free software; you can redistribute it and/or
   10    modify it under the terms of the GNU General Public License
   11    as published by the Free Software Foundation; either version 2
   12    of the License, or (at your option) any later version.
   13
   14    This program is distributed in the hope that it will be useful,
   15    but WITHOUT ANY WARRANTY; without even the implied warranty of
   16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17    GNU General Public License for more details.
   18
   19    You should have received a copy of the GNU General Public
   20    License along with this library; if not, write to the Free Software
   21    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   22
   23    As a special exception, if you link this library with other files,
   24    compiled with a Free Software compiler, to produce an executable, this
   25    library does not by itself cause the resulting executable to be covered
   26    by the GNU General Public License. This exception does not however
   27    invalidate any other reasons why the executable file might be covered by
   28    the GNU General Public License.
   29*/
   30
   31:- module(cpa_user, []).   32
   33:- use_module(rdfql(serql_xml_result)).   34:- use_module(library(http/http_open)).   35:- use_module(library(http/http_path)).   36:- use_module(library(http/html_head)).   37:- use_module(library(http/html_write)).   38:- use_module(library(http/js_write)).   39:- use_module(library(http/http_dispatch)).   40:- use_module(library(http/http_host)).   41:- use_module(library(http/cp_jquery)).   42:- use_module(api(rdflib)).   43:- use_module(user(user_db)).   44:- use_module(library(debug)).   45:- use_module(components(server_statistics)).   46:- use_module(components(query)).   47:- use_module(components(basics)).   48:- use_module(library(semweb/rdf_db)).   49:- use_module(library(semweb/rdf_library)).   50:- use_module(library(occurs)).   51
   52/** <Module> Basic user (developer) interaction
   53
   54This module contains the main front-end of ClioPatria. It notably
   55provides the HTTP-handlers for / and /home:
   56
   57    $ / :
   58    This handler, with id=root, redirects either to /home (id=home) or
   59    to id=create_admin. The latter is issued if there is no initialised
   60    user-db.
   61
   62    $ /home :
   63    Provides the default welcome page of ClioPatria.
   64
   65If one develops an application on top   of  ClioPatria, it is adviced to
   66redefine the handler for =home=, as in:
   67
   68    ==
   69    :- http_handler('/welcome', home, []).
   70
   71    home(Request) :-
   72	...
   73    ==
   74
   75If the application wants to provide  a   link  to the generic ClioPatria
   76administrative interface, it can do so by   linking  to the id=admin, as
   77in:
   78
   79    ==
   80	...,
   81	{ http_link_to_id(admin, [], AdminRef) },
   82	html(a(href(AdminRef), admin)),
   83	...
   84    ==
   85*/
   86
   87:- http_handler(root('.'),			     root,
   88		[ priority(-100) ]).   89:- http_handler(cliopatria(home),		     home,
   90		[ priority(-100) ]).   91:- http_handler(cliopatria(admin),		     home,
   92		[ id(admin) ]).   93:- http_handler(cliopatria('user/query'),	     query_form,
   94		[id(sparql_query_form)]).   95:- http_handler(cliopatria('user/statistics'),	     statistics,	      []).   96:- http_handler(cliopatria('user/loadFile'),	     load_file_form,	      []).   97:- http_handler(cliopatria('user/loadURL'),	     load_url_form,	      []).   98:- http_handler(cliopatria('user/loadLibraryRDF'),   load_library_rdf_form,   []).   99:- http_handler(cliopatria('user/clearRepository'),  clear_repository_form,   []).  100:- http_handler(cliopatria('user/removeStatements'), remove_statements_form,  []).  101
  102
  103%%	root(+Request)
  104%
  105%	Default ClioPatria handler for /.  The default handler redirects
  106%	to id=home, unless the use-info is not initialised. in that case
  107%	it redirects to id=create_admin.
  108
  109root(Request) :-
  110	redirect_create_admin(Request),
  111	http_redirect(moved_temporary,
  112		      location_by_id(home),
  113		      Request).
  114
  115redirect_create_admin(Request) :-
  116	\+ current_user(_), !,
  117	http_redirect(moved_temporary,
  118		      location_by_id(create_admin),
  119		      Request).
  120redirect_create_admin(_).
  121
  122%%	home(+Request)
  123%
  124%	Reply with the normal  welcome  page.   The  welcome  page  is a
  125%	decorated version of html('welcome.html').
  126
  127home(Request) :-
  128	redirect_create_admin(Request),
  129	reply_decorated_file(html('welcome.html'), Request).
  130
  131
  132%%	reply_decorated_file(+Alias, +Request) is det.
  133%
  134%	Present an HTML file embedded using  the server styling. This is
  135%	achieved by parsing the  HTML  and   passing  the  parsed DOM to
  136%	reply_html_page/3.
  137
  138reply_decorated_file(Alias, _Request) :-
  139	absolute_file_name(Alias, Page, [access(read)]),
  140	load_html_file(Page, DOM),
  141	contains_term(element(title, _, Title), DOM),
  142	contains_term(element(body, _, Body), DOM),
  143	Style = element(style, _, _),
  144	findall(Style, sub_term(Style, DOM), Styles),
  145	append(Styles, Body, Content),
  146	reply_html_page(cliopatria(html_file),
  147			title(Title), Content).
  148
  149
  150		 /*******************************
  151		 *	    STATISTICS		*
  152		 *******************************/
  153
  154%%	statistics(+Request)
  155%
  156%	Provide elementary statistics on the server.
  157
  158statistics(Request) :-
  159	http_current_host(Request, Host, _Port, [global(true)]),
  160	reply_html_page(cliopatria(default),
  161			title('RDF statistics'),
  162			[ div(id('rdf-statistics'),
  163			      [ h1([id(stattitle)], ['RDF statistics for ', Host]),
  164				ol([id(toc)],
  165				   [ li(a(href('#ntriples'),    'Triples in database')),
  166				     li(a(href('#callstats'),   'Call statistics')),
  167				     li(a(href('#sessions'),    'Active sessions')),
  168				     li(a(href('#serverstats'), 'Server statistics'))
  169				   ]),
  170				h2([id(ntriples)], 'Triples in database'),
  171				\triple_statistics,
  172				h2([id(callstats)],'Call statistics'),
  173				\rdf_call_statistics_table,
  174				h2([id(sessions)], 'Active sessions'),
  175				\http_session_table,
  176				h2([id(serverstats)], 'Server statistics'),
  177				h3('Static workers and statistics:'),
  178				\http_server_statistics,
  179				h3('Defined dynamic worker pools:'),
  180				\http_server_pool_table
  181			      ])
  182			]).
  183
  184
  185triple_statistics -->
  186	{ rdf_statistics(triples(Total)),
  187	  graph_count(Count),
  188	  http_link_to_id(list_graphs, [], ListGraphs)
  189	},
  190	html(p([ 'The RDF store contains ', \n(human, Total), ' triples in ',
  191		 \n(human, Count), ' ', a(href(ListGraphs), graphs),
  192		 \using_core])).
  193
  194:- if((rdf_version(V),V<30000)).  195using_core -->
  196	{ rdf_statistics(core(Core)) }, !,
  197	html([', using ', \n(human, Core), 'b memory']).
  198:- endif.  199using_core -->
  200	[].
  201
  202graph_count(Count) :-
  203	aggregate_all(count, rdf_graph(_), Count).
  204
  205%%	query_form(+Request)
  206%
  207%	Provide a page for issuing a =SELECT= query.
  208
  209query_form(_Request) :-
  210	reply_html_page(cliopatria(default),
  211			title('Specify a query'),
  212			[ \query_form([]),
  213			  \query_docs,
  214			  \warn_interactive
  215			]).
  216
  217
  218
  219warn_interactive -->
  220	{ http_location_by_id(sparql_query, HREF),
  221	  SparqlAPI = 'http://www.w3.org/TR/rdf-sparql-protocol/'
  222	},
  223	html([ br(clear(all)),
  224	       p(class(footnote),
  225		 [ 'This form is to test SPARQL queries ', i(interactively), '. ',
  226		   'Machines should use ', b([HREF,'?query=...']),
  227		   ', which provides a ',
  228		   a(href(SparqlAPI), 'SPARQL compliant HTTP API'), '.'
  229		 ])
  230	     ]).
  231
  232query_docs -->
  233	html(ul([ li(a(href('http://www.w3.org/TR/rdf-sparql-query/'),
  234		       'SPARQL Documentation')),
  235		  li(a(href('http://rdf4j.org/'),
  236		       'Sesame and SeRQL site'))
  237		])).
  238
  239%%	load_file_form(+Request)
  240%
  241%	Provide a form for uploading triples from a local file.
  242
  243load_file_form(Request) :-
  244	authorized(write(default, load(posted))),
  245	reply_html_page(cliopatria(default),
  246			title('Upload RDF'),
  247			[ h1('Upload an RDF document'),
  248
  249			  \explain_file_form,
  250
  251			  form([ action(location_by_id(upload_data)),
  252				 method('POST'),
  253				 enctype('multipart/form-data')
  254			       ],
  255			       [ \hidden(resultFormat, html),
  256				 table(class(form),
  257				       [tr([ th(class(label), 'File:'),
  258					     td(input([ name(data),
  259							id(filename),
  260							type(file),
  261							size(50)
  262						      ]))
  263					   ]),
  264					tr([ th(class(label), 'Graph:'),
  265					     td(input([ name(baseURI),
  266							id(namedgraph),
  267							size(50)
  268						      ]))
  269					   ]),
  270					tr(class(buttons),
  271					   [ th([align(right), colspan(2)],
  272						input([ type(submit),
  273							value('Upload now')
  274						      ]))
  275					   ])
  276				       ])
  277			       ]),
  278			  \graph_script(Request)
  279			]).
  280
  281explain_file_form -->
  282	html({|html||
  283<p>Upload RDF to the ClioPatria triple store. The uploaded file may
  284contain <a href="http://www.w3.org/TR/REC-rdf-syntax/">RDF/XML</a>, <a
  285href="http://www.w3.org/TR/turtle/">Turtle</a> or <a
  286href="http://www.w3.org/TR/n-triples/">ntriples</a>. The file is
  287processed using <a href="http://www.libarchive.org/"> libarchive</a>,
  288which implies it can be a (nested) archive and may optionally be
  289compressed. </p>
  290
  291<p>
  292Alternatively you can use <a href="loadURL">loadURL</a> to load data from a web server.
  293</p>
  294	     |}).
  295
  296
  297graph_script(Request) -->
  298	{ http_public_host_url(Request, Host),
  299	  http_absolute_location(root(data/uploaded), Location, []),
  300	  string_concat(Host, Location, URL)
  301	},
  302	html_requires(jquery),
  303	js_script({|javascript(URL)||
  304$(function() {
  305  if ( $("#filename").val() ) {
  306    $("#namedgraph").val(URL+"/"+$("#filename").val());
  307  }
  308
  309  $("#filename").on("change", function(ev) {
  310    var filename = $(ev.target).val();
  311    console.log("Changed file", filename);
  312    $("#namedgraph").val(URL+"/"+filename);
  313  });
  314});
  315		  |}).
  316
  317
  318%%	load_url_form(+Request)
  319%
  320%	Provide a form for uploading triples from a URL.
  321
  322load_url_form(_Request) :-
  323	reply_html_page(cliopatria(default),
  324			title('Load RDF from HTTP server'),
  325			[ h1('Load RDF from HTTP server'),
  326
  327			  \explain_url_form,
  328
  329			  form([ action(location_by_id(upload_url)),
  330				 method('GET')
  331			       ],
  332			       [ \hidden(resultFormat, html),
  333				 table(class(form),
  334				       [tr([ th(class(label), 'URL:'),
  335					     td(input([ name(url),
  336							id(url),
  337							value('http://'),
  338							size(50)
  339						      ]))
  340					   ]),
  341					tr([ th(class(label), 'Graph:'),
  342					     td(input([ name(baseURI),
  343							id(namedgraph),
  344							value('http://'),
  345							size(50)
  346						      ]))
  347					   ]),
  348					tr(class(buttons),
  349					   [ td([align(right), colspan(2)],
  350						input([ type(submit),
  351							value('Load RDF')
  352						      ]))
  353					   ])
  354				       ])
  355			       ]),
  356			  \url_graph_script
  357			]).
  358
  359
  360url_graph_script -->
  361	html_requires(jquery),
  362	js_script({|javascript||
  363$(function() {
  364  $("#url").on("change keyup", function(ev) {
  365    var url = $(ev.target).val();
  366    $("#namedgraph").val(url);
  367  });
  368});
  369		   |}).
  370
  371
  372explain_url_form -->
  373	html({|html||
  374
  375<p>Download RDF from an URL and insert it into the ClioPatria triple
  376store. The downloaded document may contain <a
  377href="http://www.w3.org/TR/REC-rdf-syntax/">RDF/XML</a>, <a
  378href="http://www.w3.org/TR/turtle/">Turtle</a> or <a
  379href="http://www.w3.org/TR/n-triples/">ntriples</a>. The file is
  380processed using <a href="http://www.libarchive.org/"> libarchive</a>,
  381which implies it can be a (nested) archive and may optionally be
  382compressed. </p>
  383
  384<p>
  385Alternatively you can use <a href="loadFile">loadFile</a> to upload
  386a file through your browser.
  387</p>
  388	     |}).
  389
  390%%	load_library_rdf_form(+Request)
  391%
  392%	Provide a form  for  loading  an   ontology  from  the  library.
  393%	Libraries are made  available  through   the  file  search  path
  394%	=ontology=. Directories found through this   alias  are searched
  395%	recursively for files named =|Manifest.ttl|=.
  396%
  397%	@see file_search_path/2
  398%	@see rdf_attach_library/1.
  399
  400load_library_rdf_form(Request) :-
  401	authorized(read(status, listBaseOntologies)),
  402	get_base_ontologies(Request, Ontologies),
  403	reply_html_page(cliopatria(default),
  404			title('Load server-side RDF library'),
  405			[ h1('Load a registered RDF library'),
  406			  p('Select a resource from the registered libraries'),
  407			  \load_base_ontology_form(Ontologies)
  408			]).
  409
  410
  411%%	load_base_ontology_form(+Ontologies)//
  412%
  413%	HTML component that emits a form to load a base-ontology and its
  414%	dependencies. Ontologies is a list   of  ontology-identifiers as
  415%	used by rdf_load_library/1.
  416
  417load_base_ontology_form(Ontologies) -->
  418	html(form([ action(location_by_id(load_library_ontology)),
  419		    method('GET')
  420		  ],
  421		  [ \hidden(resultFormat, html),
  422		    table(class(form),
  423			  [ tr([ th('Ontology:'),
  424				 td(select(name(ontology),
  425					   [ option([], '')
  426					   | \emit_base_ontologies(Ontologies)
  427					   ]))
  428			       ]),
  429			    tr(class(buttons),
  430			       td([colspan(2), align(right)],
  431				  input([ type(submit),
  432					  value('Load')
  433					])))
  434			  ])
  435		  ])).
  436
  437
  438emit_base_ontologies([]) -->
  439	[].
  440emit_base_ontologies([H|T]) -->
  441	(   { rdf_library_index(H, title(Title)) }
  442	->  html(option([value(H)], [H, ' -- ', Title]))
  443	;   html(option([value(H)], H))
  444	),
  445	emit_base_ontologies(T).
  446
  447
  448get_base_ontologies(_Request, List) :-
  449	catch(findall(O, library_ontology(O), List0), _, fail), !,
  450	sort(List0, List).
  451get_base_ontologies(Request, List) :-
  452	http_current_host(Request, Host, Port, []),
  453	http_location_by_id(list_base_ontologies, ListBaseOntos),
  454	debug(base_ontologies, 'Opening http://~w:~w~w',
  455	      [Host, Port, ListBaseOntos]),
  456	http_open([ protocol(http),
  457		    host(Host),
  458		    port(Port),
  459		    path(ListBaseOntos),
  460		    search([resultFormat(xml)])
  461		  ],
  462		  In,
  463		  [ % request_header('Cookie', Cookie)
  464		  ]),
  465	debug(base_ontologies, '--> Reading from ~w', [In]),
  466	xml_read_result_table(In, Rows, _VarNames),
  467	maplist(arg(1), Rows, List).
  468
  469%%	clear_repository_form(+Request)
  470%
  471%	HTTP handle presenting a form to clear the repository.
  472
  473clear_repository_form(_Request) :-
  474	reply_html_page(cliopatria(default),
  475			title('Clear triple store'),
  476			[ h1('Clear entire repository'),
  477
  478			  p(['This operation removes ', b(all), ' triples from \c
  479			  the RDF store.']),
  480
  481			  form([ action(location_by_id(clear_repository)),
  482				 method('GET')
  483			       ],
  484			       [ \hidden(repository, default),
  485				 \hidden(resultFormat, html),
  486				 input([ type(submit),
  487					 value('Clear repository now')
  488				       ])
  489			       ])
  490			]).
  491
  492
  493%%	remove_statements_form(+Request)
  494%
  495%	HTTP handler providing a form to remove RDF statements.
  496
  497remove_statements_form(_Request) :-
  498	reply_html_page(cliopatria(default),
  499			title('Remove triples from store'),
  500			[ h1('Remove statements'),
  501
  502			  p(['Remove matching triples from the database.  The three ',
  503			     'fields are in ntriples/Turtle notation.  Omitted fields ',
  504			     'match any value.'
  505			    ]),
  506
  507			  \remove_statements_form
  508			]).
  509
  510remove_statements_form -->
  511	html(form([ action(location_by_id(remove_statements)),
  512		    method('GET')
  513		  ],
  514		  [ \hidden(repository, default),
  515		    \hidden(resultFormat, html),
  516		    table([ class(form)
  517			  ],
  518			  [ tr([ th(class(label), 'Subject:'),
  519				 td(input([ name(subject),
  520					    size(50)
  521					  ]))
  522			       ]),
  523			    tr([ th(class(label), 'Predicate:'),
  524				 td(input([ name(predicate),
  525					    size(50)
  526					  ]))
  527			       ]),
  528			    tr([ th(class(label), 'Object:'),
  529				 td(input([ name(object),
  530					    size(50)
  531					  ]))
  532			       ]),
  533			    tr(class(buttons),
  534			       [ td([ align(right),
  535				      colspan(2)
  536				    ],
  537				    input([ type(submit),
  538					    value('Remove')
  539					  ]))
  540			       ])
  541			  ])
  542		  ]))