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