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		  ])).