34
35:- module(http_dirindex,
36 [ http_reply_dirindex/3, 37 directory_index//2 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 ]). 52
62
71
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).
102
113
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]).
227
238
239mime_type_icon(Ext, Icon) :-
240 http:mime_type_icon(Ext, Icon),
241 !.
242mime_type_icon(_, 'generic.png').
243
251
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 265
266:- html_resource(http_dirindex,
267 [ virtual(true),
268 requires([ css('dirindex.css')
269 ])
270 ]).