:- use_module(library(http/thread_httpd)). :- use_module(library(http/http_dispatch)). :- use_module(library(http/html_write)). :- use_module(library(http/http_parameters)). :- use_module(library(http/html_head)). % new :- use_module(library(semweb/rdf_db)). :- use_module(lod). :- http_handler(root(.), home, []). :- http_handler(root(search), search, []). :- http_handler(root(resource), resource, []). :- http_handler(css('ptable.css'), http_reply_file('ptable.css', []), []). :- http_handler(css('sindice.css'), http_reply_file('sindice.css', []), []). http:location(css, root(css), []). %% server(?Port) % % Start the HTTP server, listening to Port. server(Port) :- http_server(http_dispatch, [port(Port)]). %% home(+Request) % % Privides the initial page of the LOD-crawler with a form % to search on http://sindice.com home(_Request) :- reply_html_page(title('LOD Crawler'), [ h1(class(title), 'LOD Crawler'), p(class(banner), [ 'Welcome to the SWI-Prolog Linked Open Data ', 'crawler. To start your experience, enter a ', 'search term such as "Amsterdam".' ]), \search_form ]). search_form --> { http_link_to_id(search, [], Ref) }, html([ \html_requires(css('sindice.css')), form([id(search), action(Ref)], [ input(name(q)), input([type(submit), value('Search')]) ]) ]). %% search(+Request) % % Submit the query to sindice and show the result. Sindice almost % behaves as `dynamic linked open data' by providing the % search-results as properties of the query, but not quite. This % means we must pick-up the result-page URI from the returned % named graph. search(Request) :- http_parameters(Request, [q(Query, [])]), sindice_query(Query, 1, URL), lod_load(URL), ( rdf(URI, _, _, URL) % work around Sindice bug -> resource_page(URI) ). %% resource(+Request) % % Explore a linked open-data resource. We distinguish three cases: % % 1. If lod_load/1 succeeds, show the loaded triples % 2. If lod_load/1 indicates that the returned document % has an unknown content-type, it appearenly is not LOD % and we redirect the client to the web-page. % 3. Else, we re-throw the error, generating an error-page. resource(Request) :- http_parameters(Request, [r(URI, [])]), catch(lod_load(URI), E, true), ( var(E) -> resource_page(URI) ; subsumes_term(error(domain_error(content_type, _), _), E) -> throw(http_reply(moved_temporary(URI))) ; throw(E) ). resource_page(URL) :- uri_label(URL, Label), findall(P-O, rdf(URL, P, O), Pairs0), sort(Pairs0, Pairs), group_pairs_by_key(Pairs, Grouped), reply_html_page(title('LOD Crawler --- ~w'-[Label]), [ h1(class(title), a(href(URL), Label)), \property_table(Grouped) ]). property_table(Grouped) --> html([ \html_requires(css('ptable.css')), table(class(properties), [ \ptable_header | \ptable_rows(Grouped) ]) ]). ptable_header --> html(tr([th('Predicate'), th('Object')])). ptable_rows(Grouped) --> ptable_rows(Grouped, 1). ptable_rows([], _) --> []. ptable_rows([H|T], Row) --> { Row1 is Row + 1 }, ptable_row(H, Row), ptable_rows(T, Row1). ptable_row(P-VL, Row) --> { ( Row mod 2 =:= 0 -> Class = even ; Class = odd ) }, html(tr(class(Class), [ td(class(predicate), \rlink(P)), td(class(object), ul(\vlist(VL))) ])). vlist([]) --> []. vlist([H|T]) --> html(li(\vdiv(H))), vlist(T). vdiv(literal(L)) --> !, { text_of_literal(L, Text) }, html(div(class(lvalue), Text)). vdiv(R) --> html(div(class(rvalue), \rlink(R))). rlink(P) --> { uri_label(P, Label), uri_iri(URI, P), http_link_to_id(resource, [r=URI], HREF) }, html(a(href(HREF), Label)). %% body(+Content)// % % Define overall style. This hook into reply_html_page/2 is called % to translate the 2nd argument. It is searched for in the current % module as well as the user-module. % % Redefining head//1 or body//1 is a way to redefine the overall % page-style of all pages served. body(Content) --> % contents already provides a form { sub_term(\search_form, Content) }, !, html(Content). body(Content) --> % add header with search-form html([ div(class(top), \search_form) | Content ]).