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, VU University Amsterdam
    7    All rights reserved.
    8
    9    Redistribution and use in source and binary forms, with or without
   10    modification, are permitted provided that the following conditions
   11    are met:
   12
   13    1. Redistributions of source code must retain the above copyright
   14       notice, this list of conditions and the following disclaimer.
   15
   16    2. Redistributions in binary form must reproduce the above copyright
   17       notice, this list of conditions and the following disclaimer in
   18       the documentation and/or other materials provided with the
   19       distribution.
   20
   21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   24    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   25    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   26    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   27    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   29    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   30    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   31    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   32    POSSIBILITY OF SUCH DAMAGE.
   33*/
   34
   35:- module(cp_messages,
   36          [ call_showing_messages/2,    % :Goal, +Options
   37            after_messages/1            % +HTML
   38          ]).   39:- use_module(library(http/html_write)).   40:- use_module(library(http/html_head)).   41:- use_module(library(http/js_write)).   42:- use_module(library(http/http_wrapper)).   43:- use_module(library(http/http_dispatch)).   44:- use_module(library(http/http_path)).   45:- use_module(library(http/cp_jquery)).   46:- use_module(library(option)).   47:- use_module(library(lists)).   48
   49/** <module> Run goals that produce messages
   50
   51This module allows executing (long  running)   Prolog  goals and see the
   52messages appear in the browser.
   53*/
   54
   55:- meta_predicate
   56    call_showing_messages(0, +).   57:- html_meta
   58    after_messages(html).   59
   60%!  call_showing_messages(:Goal, +Options) is det.
   61%
   62%   Execute  Goal,  showing  the  feedback   in  the  browser.  This
   63%   predicate builds a default application   page with a placeholder
   64%   for the messages. It then sends   all  HTML upto the placeholder
   65%   and flushes the output to  the   browser.  During execution, all
   66%   output from Goal emitted through   print_message/2  is caught in
   67%   the message-box. After completion of Goal the page is completed.
   68%
   69%   This predicate is intended for action such as loading RDF files,
   70%   while providing feedback on  files   loaded  and  possible error
   71%   messages. Note that this call creates a complete page.
   72%
   73%   @bug    This call uses =chunked= transfer encoding to send the
   74%           page in parts.  Not all browsers support this and not
   75%           all browsers update the page incrementally.
   76
   77:- create_prolog_flag(html_messages, false, [type(boolean)]).   78assert_message_hook :-
   79    Head = user:message_hook(_Term, Level, Lines),
   80    Body = send_message(Level, Lines),
   81    (   clause(Head, Body)
   82    ->  true
   83    ;   asserta((Head:-Body))
   84    ).
   85:- initialization
   86    assert_message_hook.   87
   88
   89call_showing_messages(Goal, Options) :-
   90    option(style(Style), Options, cliopatria(default)),
   91    option(head(Head), Options, title('ClioPatria')),
   92    option(header(Header), Options,
   93           div(class(msg_header),
   94               h4('Messages ...'))),
   95    (   option(footer(Footer), Options)
   96    ->  true
   97    ;   (   option(return_to(ReturnURI), Options)
   98        ->  FooterRest = [ p(['Go ', a(href(ReturnURI), 'back'),
   99                              ' to the previous page']) ]
  100        ;   FooterRest = []
  101        ),
  102        Footer = div(class(msg_footer), [ h4('Done') | FooterRest ])
  103    ),
  104    format('Content-Type: text/html~n'),
  105    format('Transfer-Encoding: chunked~n~n'),
  106    header(Style, Head, Header, Footer, FooterTokens),
  107    setup_call_cleanup(
  108        set_prolog_flag(html_messages, true),
  109        catch(once(Goal), E, print_message(error, E)),
  110        set_prolog_flag(html_messages, false)),
  111    footer(FooterTokens).
  112
  113send_message(Level, Lines) :-
  114    current_prolog_flag(html_messages, true),
  115    level_css_class(Level, Class),
  116    phrase(html(pre(class(Class), \html_message_lines(Lines))), Tokens),
  117    with_mutex(html_messages, print_html(Tokens)),
  118    flush_output,
  119    fail.
  120
  121level_css_class(informational, msg_informational).
  122level_css_class(warning,       msg_warning).
  123level_css_class(error,         msg_error).
  124
  125html_message_lines([]) -->
  126    [].
  127html_message_lines([nl|T]) -->
  128    !,
  129    html('\n'),                     % we are in a <pre> environment
  130    html_message_lines(T).
  131html_message_lines([flush]) -->
  132    [].
  133html_message_lines([H|T]) -->
  134    !,
  135    html(H),
  136    html_message_lines(T).
  137
  138
  139%!  after_messages(+HTML) is det.
  140%
  141%   Close the message window and emit   HTML.  This predicate may be
  142%   called from the Goal of call_showing_messages/2 to indicate that
  143%   all work has been done.
  144
  145after_messages(HTML) :-
  146    close_messages,
  147    phrase(html(HTML), Tokens),
  148    current_output(Out),
  149    html_write:write_html(Tokens, Out).
  150
  151
  152%!  header(+Style, +Head, +Header, +Footer, -FooterTokens)
  153%
  154%   Emit all tokens upto the placeholder for the actual messages and
  155%   return the remaining page-tokens in FooterTokens. Style and Head
  156%   are passed
  157
  158header(Style, Head, Header, Footer, FooterTokens) :-
  159    http_absolute_location(icons('smiley-thinking.gif'), Image, []),
  160    Magic = '$$$MAGIC$$$',
  161    make_list(Header, HList),
  162    make_list(Footer, FList),
  163    append([ HList,
  164             [ \(cp_messages:html_requires(jquery)),
  165               img([id('smiley-thinking'), src(Image)]),
  166               div(class(messages), Magic),
  167               \(cp_messages:js_script({|javascript||
  168                                        $("#smiley-thinking").hide(1000)|}))
  169             ],
  170             FList
  171           ], Body),
  172    phrase(html_write:page(Style, Head, Body), Tokens),
  173    html_write:mailman(Tokens),
  174    (   append(HeaderTokens, [Magic|FooterTokens0], Tokens)
  175    ->  append(CloseDiv0, [>|FooterTokens], FooterTokens0)
  176    ->  append(CloseDiv0, [>], CloseDiv)
  177    ->  true
  178    ),
  179    nb_setval(html_messages_close, CloseDiv),
  180    current_output(Out),
  181    html_write:write_html(HeaderTokens, Out),
  182    flush_output(Out).
  183
  184make_list(List, List) :-
  185    is_list(List),
  186    !.
  187make_list(Obj, [Obj]).
  188
  189close_messages :-
  190    nb_current(html_messages_close, Tokens),
  191    !,
  192    nb_delete(html_messages_close),
  193    current_output(Out),
  194    html_write:write_html(Tokens, Out).
  195close_messages.
  196
  197footer(FooterTokens) :-
  198    close_messages,
  199    current_output(Out),
  200    html_write:write_html(FooterTokens, Out)