View source with raw comments or as raw
    1/*  Part of SWI-Prolog
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (c)  2009-2014, 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(http_dirindex,
   36          [ http_reply_dirindex/3,      % +PhysicalDir, +Options, +Request
   37            directory_index//2          % +PhysicalDir, +Options
   38          ]).   39:- use_module(library(http/html_write)).   40:- use_module(library(http/http_path)).   41:- use_module(library(http/http_dispatch)).   42:- use_module(library(http/http_server_files)).   43:- use_module(library(http/html_head)).   44:- use_module(library(http/mimetype)).   45:- use_module(library(apply)).   46:- use_module(library(option)).   47
   48:- predicate_options(http_reply_dirindex/3, 2,
   49                     [ title(any),
   50                       pass_to(http_dispatch:http_safe_file/2, 2)
   51                     ]).

HTTP directory listings

This module provides a simple API to generate an index for a physical directory. The index can be customised by overruling the dirindex.css CSS file and by defining additional rules for icons using the hook file_extension_icon/2.

To be done
- Provide more options (sorting, selecting columns, hiding files) */
 http_reply_dirindex(+DirSpec, +Options, +Request) is det
Provide a directory listing for Request, assuming it is an index for the physical directrory Dir. If the request-path does not end with /, first return a moved (301 Moved Permanently) reply.

The calling conventions allows for direct calling from http_handler/3.

   72http_reply_dirindex(DirSpec, Options, Request) :-
   73    http_safe_file(DirSpec, Options),
   74    absolute_file_name(DirSpec, Dir,
   75                       [ file_type(directory),
   76                         access(read)
   77                       ]),
   78    memberchk(path(Path), Request),
   79    (   atom_concat(PlainPath, /, Path),
   80        merge_options(Options,
   81                      [ title(['Index of ', PlainPath]) ],
   82                      Options1)
   83    ->  dir_index(Dir, Options1)
   84    ;   atom_concat(Path, /, NewLocation),
   85        throw(http_reply(moved(NewLocation)))
   86    ).
   87
   88dir_index(Dir, Options) :-
   89    directory_members(Dir, SubDirs, Files),
   90    option(title(Title), Options, Dir),
   91    reply_html_page(
   92        dir_index(Dir, Title),
   93        title(Title),
   94        [ h1(Title),
   95          \dirindex_table(SubDirs, Files, Options)
   96        ]).
   97
   98directory_members(Dir, Dirs, Files) :-
   99    atom_concat(Dir, '/*', Pattern),
  100    expand_file_name(Pattern, Matches),
  101    partition(exists_directory, Matches, Dirs, Files).
 directory_index(+Dir, +Options)// is det
Show index for a directory. Options processed:
order_by(+Field)
Sort the files in the directory listing by Field. Field is one of name (default), size or time.
order(+AscentDescent)
Sorting order. Default is ascending. The altenative is descending
  114directory_index(Dir, Options) -->
  115    { directory_members(Dir, SubDirs, Files) },
  116    dirindex_table(SubDirs, Files, Options).
  117
  118dirindex_table(SubDirs, Files, Options) -->
  119    { option(order_by(By), Options, name),
  120      sort_files(By, Files, SortedFiles0),
  121      asc_desc(SortedFiles0, SortedFiles, Options)
  122    },
  123    html_requires(http_dirindex),
  124    html(table(class(dirindex),
  125               [ \dirindex_title,
  126                 \back
  127               | \dirmembers(SubDirs, SortedFiles)
  128               ])).
  129
  130sort_files(name, Files, Files) :- !.
  131sort_files(Order, Files, Sorted) :-
  132    map_list_to_pairs(key_file(Order), Files, Pairs),
  133    keysort(Pairs, SortedPairs),
  134    pairs_values(SortedPairs, Sorted).
  135
  136key_file(name, File, Base) :-
  137    file_base_name(File, Base).
  138key_file(size, File, Size) :-
  139    size_file(File, Size).
  140key_file(time, File, Time) :-
  141    time_file(File, Time).
  142
  143asc_desc(Files, Ordered, Options) :-
  144    (   option(order(ascending), Options, ascending)
  145    ->  Ordered = Files
  146    ;   reverse(Files, Ordered)
  147    ).
  148
  149dirindex_title -->
  150    html(tr(class(dirindex_header),
  151            [ th(class(icon),     ''),
  152              th(class(name),     'Name'),
  153              th(class(modified), 'Last modified'),
  154              th(class(size),     'Size')
  155            ])).
  156
  157back -->
  158    html(tr([ \icon_cell('back.png', '[UP]'),
  159              \name_cell(.., 'Up'),
  160              td(class(modified), -),
  161              td(class(size),     -)
  162            ])).
  163
  164dirmembers(Dirs, Files) -->
  165    dir_rows(Dirs, odd, End),
  166    file_rows(Files, End, _).
  167
  168dir_rows([], OE, OE) --> [].
  169dir_rows([H|T], OE0, OE) -->
  170    dir_row(H, OE0),
  171    { oe(OE0, OE1) },
  172    dir_rows(T, OE1, OE).
  173
  174file_rows([], OE, OE) --> [].
  175file_rows([H|T], OE0, OE) -->
  176    file_row(H, OE0),
  177    {oe(OE0, OE1)},
  178    file_rows(T, OE1, OE).
  179
  180oe(odd, even).
  181oe(even, odd).
  182
  183dir_row(Dir, OE) -->
  184    { file_base_name(Dir, Name)
  185    },
  186    html(tr(class(OE),
  187            [ \icon_cell('folder.png', '[DIR]'),
  188              \name_cell(Name, Name),
  189              \modified_cell(Dir),
  190              td(class(size), -)
  191            ])).
  192
  193
  194file_row(File, OE) -->
  195    { file_base_name(File, Name),
  196      file_mime_type(File, MimeType),
  197      mime_type_icon(MimeType, IconName),
  198      uri_encoded(path, Name, Ref)
  199    },
  200    html(tr(class(OE),
  201            [ \icon_cell(IconName, '[FILE]'),
  202              \name_cell(Ref, Name),
  203              \modified_cell(File),
  204              td(class(size), \size(File))
  205            ])).
  206
  207icon_cell(IconName, Alt) -->
  208    { http_absolute_location(icons(IconName), Icon, [])
  209    },
  210    html(td(class(icon), img([src(Icon), alt(Alt)]))).
  211
  212
  213name_cell(Ref, Name) -->
  214    html(td(class(name), a(href(Ref), Name))).
  215
  216
  217modified_cell(Name) -->
  218    { time_file(Name, Stamp),
  219      format_time(string(Date), '%+', Stamp)
  220    },
  221    html(td(class(modified), Date)).
  222
  223size(Name) -->
  224    { size_file(Name, Size)
  225    },
  226    html('~D'-[Size]).
 mime_type_icon(+MimeType, -Icon) is det
Determine the icon that is used to show a file of the given extension. This predicate can be hooked using the multifile http:mime_type_icon/2 hook with the same signature. Icon is the plain name of an image file that appears in the file-search-path icons.
Arguments:
MimeType- is a term Type/SubType as produced by file_mime_type/2.
  239mime_type_icon(Ext, Icon) :-
  240    http:mime_type_icon(Ext, Icon),
  241    !.
  242mime_type_icon(_, 'generic.png').
 http:mime_type_icon(+MimeType, -IconName) is nondet
Multi-file hook predicate that can be used to associate icons to files listed by http_reply_dirindex/3. The actual icon file is located by absolute_file_name(icons(IconName), Path, []).
See also
- serve_files_in_directory/2 serves the images.
  252:- multifile
  253    http:mime_type_icon/2.  254
  255http:mime_type_icon(application/pdf,      'layout.png').
  256http:mime_type_icon(text/csrc,            'c.png').
  257http:mime_type_icon(application/'x-gzip', 'compressed.png').
  258http:mime_type_icon(application/'x-gtar', 'compressed.png').
  259http:mime_type_icon(application/zip,      'compressed.png').
  260
  261
  262                 /*******************************
  263                 *            RESOURCES         *
  264                 *******************************/
  265
  266:- html_resource(http_dirindex,
  267                 [ virtual(true),
  268                   requires([ css('dirindex.css')
  269                            ])
  270                 ]).