mirror of
https://github.com/journeyman-cc/smeagol.git
synced 2026-04-12 18:05:06 +00:00
Merge remote-tracking branch 'origin/develop' into feature/47
This commit is contained in:
commit
ee46b0d545
9 changed files with 177 additions and 70 deletions
|
|
@ -131,6 +131,8 @@
|
||||||
"Smeagol has been unable to find some of the resources on which it depends,
|
"Smeagol has been unable to find some of the resources on which it depends,
|
||||||
possibly because of misconfiguration or missing environment variables."
|
possibly because of misconfiguration or missing environment variables."
|
||||||
;; used in sanity check report
|
;; used in sanity check report
|
||||||
|
:sortable "You can sort this table by selecting column headers"
|
||||||
|
;; used for sortable tables
|
||||||
:user-lacks-field "User record in the passwd file lacks a field"
|
:user-lacks-field "User record in the passwd file lacks a field"
|
||||||
;; used in sanity check report
|
;; used in sanity check report
|
||||||
:username-prompt "Username" ;; text of the username widget prompt on edit user page
|
:username-prompt "Username" ;; text of the username widget prompt on edit user page
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,16 @@
|
||||||
## html elements generally in alphabetic order
|
## html elements generally in alphabetic order
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: darkgray;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: darkgray;
|
||||||
|
background:rgba(200,200,200,0.8);
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
@ -52,14 +62,15 @@ dd {
|
||||||
|
|
||||||
/* footer of the page - not-editable, provided by Smeagol */
|
/* footer of the page - not-editable, provided by Smeagol */
|
||||||
footer {
|
footer {
|
||||||
border-top: thin solid gray;
|
border-top: thin solid silver;
|
||||||
|
color: gray;
|
||||||
|
background:rgba(200,200,200,0.8);
|
||||||
clear: both;
|
clear: both;
|
||||||
font-size: smaller;
|
font-size: smaller;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: gray;
|
|
||||||
background: rgba(224,224,224,0.95);
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
min-height: 4px;
|
||||||
padding: 0.25em 0;
|
padding: 0.25em 0;
|
||||||
bottom:0;
|
bottom:0;
|
||||||
position:fixed;
|
position:fixed;
|
||||||
|
|
@ -71,9 +82,14 @@ footer {
|
||||||
}
|
}
|
||||||
|
|
||||||
footer div {
|
footer div {
|
||||||
|
display: none;
|
||||||
padding: 0.1em;
|
padding: 0.1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
footer:hover div {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
form {
|
form {
|
||||||
border: thin solid silver;
|
border: thin solid silver;
|
||||||
}
|
}
|
||||||
|
|
@ -83,8 +99,6 @@ header {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
width:100%;
|
width:100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
background-color: gray;
|
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header h1 {
|
header h1 {
|
||||||
|
|
@ -93,7 +107,6 @@ header h1 {
|
||||||
|
|
||||||
header a {
|
header a {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header a:hover {
|
header a:hover {
|
||||||
|
|
@ -131,12 +144,12 @@ ins {
|
||||||
label {
|
label {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
min-width: 20em;
|
min-width: 20em;
|
||||||
border-right: thin solid gray;
|
border-right: thin solid silver;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
border: 2px solid black;
|
border: thin solid silver;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,7 +161,7 @@ th, td {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
padding: 0.15em 1.5em;
|
padding: 0.15em 1.5em;
|
||||||
border: 1px solid gray;
|
border: 1px solid silver;
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
th {
|
||||||
|
|
@ -166,6 +179,7 @@ th {
|
||||||
|
|
||||||
/* left bar for all pages in the Wiki - editable, provided by users. Within main-container */
|
/* left bar for all pages in the Wiki - editable, provided by users. Within main-container */
|
||||||
#side-bar {
|
#side-bar {
|
||||||
|
display: none;
|
||||||
width: 17%;
|
width: 17%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
float: left;
|
float: left;
|
||||||
|
|
@ -173,10 +187,10 @@ th {
|
||||||
|
|
||||||
/* cookies information box, fixed, in right margin, just above footer */
|
/* cookies information box, fixed, in right margin, just above footer */
|
||||||
#cookies {
|
#cookies {
|
||||||
width: 30%;
|
width: 20%;
|
||||||
float: right;
|
float: right;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 3.5em;
|
bottom: 8px;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 175;
|
z-index: 175;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
@ -190,8 +204,8 @@ th {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
padding: 0.25em 2em;
|
padding: 0.25em 2em;
|
||||||
border-radius: 0.25em;
|
border-radius: 0.25em;
|
||||||
color: white;
|
color: gray;
|
||||||
background:rgba(40,40,40,0.8);
|
background:rgba(200,200,200,0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* more-about-cookies box, normally hidden */
|
/* more-about-cookies box, normally hidden */
|
||||||
|
|
@ -199,9 +213,9 @@ th {
|
||||||
display: none;
|
display: none;
|
||||||
padding: 0.5em 2em;
|
padding: 0.5em 2em;
|
||||||
border-radius: 0.5em;
|
border-radius: 0.5em;
|
||||||
color: white;
|
color: gray;
|
||||||
background:rgba(40,40,40,0.8);
|
background:rgba(200,200,200,0.8);
|
||||||
border-bottom: thin solid white;
|
border-bottom: thin solid gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* but magically appears on mouseover */
|
/* but magically appears on mouseover */
|
||||||
|
|
@ -242,8 +256,8 @@ th {
|
||||||
right: 0;
|
right: 0;
|
||||||
padding: 0.25em 2em;
|
padding: 0.25em 2em;
|
||||||
border-radius: 0.25em;
|
border-radius: 0.25em;
|
||||||
color: white;
|
color: gray;
|
||||||
background:rgba(40,40,40,0.8);
|
background:rgba(200,200,200,0.8);
|
||||||
font-size: 66%;
|
font-size: 66%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,7 +268,11 @@ th {
|
||||||
.minor-controls a {
|
.minor-controls a {
|
||||||
float: right;
|
float: right;
|
||||||
padding: 0.25em 2em;
|
padding: 0.25em 2em;
|
||||||
color: white;
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minor-controls a:hover {
|
||||||
|
color: darkgray;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pseudo-input {
|
.pseudo-input {
|
||||||
|
|
@ -303,8 +321,7 @@ th {
|
||||||
/* content of the current page in the Wiki - editable, provided by users. Within main-container */
|
/* content of the current page in the Wiki - editable, provided by users. Within main-container */
|
||||||
#content {
|
#content {
|
||||||
border: thin solid silver;
|
border: thin solid silver;
|
||||||
width: 80%;
|
width: 100%;
|
||||||
float: right;
|
|
||||||
padding-bottom: 5em;
|
padding-bottom: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -312,16 +329,29 @@ th {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
|
|
||||||
/* top-of-page navigation, not editable, provided by Smeagol */
|
/* top-of-page navigation, not editable, provided by Smeagol */
|
||||||
#nav{
|
#nav{
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
min-height: 4px;
|
||||||
_position: absolute;
|
_position: absolute;
|
||||||
_top: expression(document.documentElement.scrollTop);
|
_top: expression(document.documentElement.scrollTop);
|
||||||
z-index: 149;
|
z-index: 149;
|
||||||
background:rgba(40,40,40,0.8);
|
color: gray;
|
||||||
|
background:rgba(200,200,200,0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav #nav-menu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav:hover #nav-menu {
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* only needed for fly-out menu effect on tablet and phone stylesheets */
|
/* only needed for fly-out menu effect on tablet and phone stylesheets */
|
||||||
|
|
@ -341,14 +371,14 @@ th {
|
||||||
}
|
}
|
||||||
|
|
||||||
#nav menu li a {
|
#nav menu li a {
|
||||||
color: white;
|
color: gray;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: 0.1em 0.75em;
|
padding: 0.1em 0.75em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nav menu li.active a { background: gray;}
|
#nav menu li.active a { background: gray; color: white;}
|
||||||
li.nav-item a:hover { background: rgb( 240, 240, 240) }
|
li.nav-item a:hover { background: rgb( 240, 240, 240) }
|
||||||
li.nav-item a:active { background: gray; color: white; }
|
li.nav-item a:active { background: gray; color: white; }
|
||||||
|
|
||||||
|
|
@ -379,17 +409,15 @@ th {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 149;
|
z-index: 149;
|
||||||
color: silver;
|
color: black;
|
||||||
background:rgba(40,40,40,0.9);
|
background:rgba(200,200,200,0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
#nav a {
|
#nav a {
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nav:hover #nav-menu {
|
#nav:hover #nav-menu, #nav:hover #phone-side-bar {
|
||||||
display: block;
|
display: block;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -455,18 +483,21 @@ th {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
#nav{
|
#nav{
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 149;
|
z-index: 149;
|
||||||
color: silver;
|
color: black;
|
||||||
background:rgba(40,40,40,0.9);
|
background:rgba(200,200,200,0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
#nav a {
|
#nav a {
|
||||||
color: white;
|
color: black;
|
||||||
text-decoration: none;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -491,6 +522,8 @@ th {
|
||||||
}
|
}
|
||||||
|
|
||||||
#nav menu li a {
|
#nav menu li a {
|
||||||
|
color: black;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nav ul li.active a { background: silver;}
|
#nav ul li.active a { background: silver;}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h1>{% i18n site-title %}: {{title}}</h1>
|
<h1>{% i18n site-title %}: {{title}}</h1>
|
||||||
|
<div id="header">
|
||||||
{{header|safe}}
|
{{header|safe}}
|
||||||
|
</div>
|
||||||
{% if message %}
|
{% if message %}
|
||||||
<div id="message">
|
<div id="message">
|
||||||
<p class="message">{{message}}</p>
|
<p class="message">{{message}}</p>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,18 @@
|
||||||
{% extends "templates/base.html" %}
|
{% extends "templates/base.html" %}
|
||||||
|
{% block extra-headers %}
|
||||||
|
{% script "/vendor/node_modules/tablesort/dist/tablesort.min.js" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<table>
|
<p>
|
||||||
<tr>
|
{% i18n sortable %}
|
||||||
<th/><th>{% i18n edit-col-hdr %}</th><th>{% i18n del-col-hdr %}</th>
|
</p>
|
||||||
|
<table id="userstable">
|
||||||
|
<tr data-sort-method='none'>
|
||||||
|
<th>{% i18n user-title-prefix %}</th>
|
||||||
|
<th data-sort-method='none'>{% i18n edit-col-hdr %}</th>
|
||||||
|
<th data-sort-method='none'>{% i18n del-col-hdr %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for user in users %}
|
{% for user in users %}
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -13,11 +21,12 @@
|
||||||
<td><a href="delete-user?target={{user}}">{% i18n del-col-hdr %} {{user}}</a></td>
|
<td><a href="delete-user?target={{user}}">{% i18n del-col-hdr %} {{user}}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tr>
|
<tr data-sort-method='none'>
|
||||||
<td><a href="edit-user">{% i18n add-user-label %}</a></td>
|
<td colspan="3"><a href="edit-user">{% i18n add-user-label %}</a></td>
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
new Tablesort(document.getElementById('userstable'));
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
{% extends "templates/base.html" %}
|
{% extends "templates/base.html" %}
|
||||||
{% block extra-headers %}
|
{% block extra-headers %}
|
||||||
{% ifequal js-from ":cloudflare" %}
|
{% ifequal js-from ":cdnjs" %}
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/simplemde/1.11.2/simplemde.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/simplemde/1.11.2/simplemde.min.js"></script>
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/simplemde/1.11.2/simplemde.min.css" rel="stylesheet" type="text/css" />
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/simplemde/1.11.2/simplemde.min.css" rel="stylesheet" type="text/css" />
|
||||||
{% else %}
|
{% else %}
|
||||||
{% style "/vendor/simplemde/dist/simplemde.min.css" %}
|
{% style "vendor/simplemde/dist/simplemde.min.css" %}
|
||||||
{% script "/vendor/simplemde/dist/simplemde.min.js" %}
|
{% script "vendor/simplemde/dist/simplemde.min.js" %}
|
||||||
{% endifequal %}
|
{% endifequal %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
{% extends "templates/base.html" %}
|
{% extends "templates/base.html" %}
|
||||||
|
{% block extra-headers %}
|
||||||
|
{% script "/vendor/node_modules/tablesort/dist/tablesort.min.js" %}
|
||||||
|
{% script "/vendor/node_modules/tablesort/dist/sorts/tablesort.number.min.js" %}
|
||||||
|
{% script "/vendor/node_modules/tablesort/dist/sorts/tablesort.date.min.js" %}
|
||||||
|
{% script "/vendor/node_modules/tablesort/dist/sorts/tablesort.monthname.min.js" %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="content" class="list-uploads">
|
<div id="content" class="list-uploads">
|
||||||
|
|
@ -9,12 +15,15 @@
|
||||||
<input name="search" id="search" type="text" value="{{search}}" required/>
|
<input name="search" id="search" type="text" value="{{search}}" required/>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
<table>
|
<p>
|
||||||
<tr>
|
{% i18n sortable %}
|
||||||
|
</p>
|
||||||
|
<table id="uploads">
|
||||||
|
<tr data-sort-method='none'>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Uploaded</th>
|
<th>Uploaded</th>
|
||||||
<th>Type this</th>
|
<th>Type this</th>
|
||||||
<th>To get this</th>
|
<th data-sort-method='none'>To get this</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for entry in files %}
|
{% for entry in files %}
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -26,9 +35,11 @@
|
||||||
<td>
|
<td>
|
||||||
{% if entry.is-image %} <img src="{{entry.resource}}" alt="{{entry.name|capitalize}}"/> {% else %} <a href="{{entry.resource}}">link</a> {% endif %}
|
{% if entry.is-image %} <img src="{{entry.resource}}" alt="{{entry.name|capitalize}}"/> {% else %} <a href="{{entry.resource}}">link</a> {% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
new Tablesort(document.getElementById('uploads'));
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -235,3 +235,4 @@
|
||||||
(reintegrate-inclusions (process-text md-src)))
|
(reintegrate-inclusions (process-text md-src)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
[noir.util.route :as route]
|
[noir.util.route :as route]
|
||||||
[noir.session :as session]
|
[noir.session :as session]
|
||||||
[smeagol.authenticate :as auth]
|
[smeagol.authenticate :as auth]
|
||||||
|
[smeagol.configuration :refer [config]]
|
||||||
[smeagol.diff2html :as d2h]
|
[smeagol.diff2html :as d2h]
|
||||||
[smeagol.formatting :refer [md->html]]
|
[smeagol.formatting :refer [md->html]]
|
||||||
[smeagol.history :as hist]
|
[smeagol.history :as hist]
|
||||||
|
|
@ -193,7 +194,8 @@
|
||||||
(log/info (format "Showing history of page '%s'" page))
|
(log/info (format "Showing history of page '%s'" page))
|
||||||
(layout/render "history.html"
|
(layout/render "history.html"
|
||||||
(merge (util/standard-params request)
|
(merge (util/standard-params request)
|
||||||
{:title (util/get-message :history-title-prefix request)
|
{:title (str (util/get-message :history-title-prefix request)
|
||||||
|
" " page)
|
||||||
:page page
|
:page page
|
||||||
:history (hist/find-history repo-path file-name)}))))
|
:history (hist/find-history repo-path file-name)}))))
|
||||||
|
|
||||||
|
|
@ -207,7 +209,7 @@
|
||||||
If `template` is supplied, use that as the formatting template as specified for
|
If `template` is supplied, use that as the formatting template as specified for
|
||||||
java.time.Formatter. Assumes system default timezone. Returns a string."
|
java.time.Formatter. Assumes system default timezone. Returns a string."
|
||||||
([^Long unix-time]
|
([^Long unix-time]
|
||||||
(format-instant unix-time "EEEE, dd MMMM YYYY"))
|
(format-instant unix-time "dd MMMM YYYY"))
|
||||||
([^Long unix-time ^String template]
|
([^Long unix-time ^String template]
|
||||||
(jt/format
|
(jt/format
|
||||||
(java-time/formatter template)
|
(java-time/formatter template)
|
||||||
|
|
@ -220,9 +222,9 @@
|
||||||
[request]
|
[request]
|
||||||
(let
|
(let
|
||||||
[params (keywordize-keys (:params request))
|
[params (keywordize-keys (:params request))
|
||||||
data-path (str util/content-dir "/uploads/")
|
|
||||||
cl (count (io/resource-path))
|
|
||||||
files
|
files
|
||||||
|
(sort-by
|
||||||
|
(juxt :name (fn [x] (- 0 (count (:resource x)))))
|
||||||
(map
|
(map
|
||||||
#(zipmap
|
#(zipmap
|
||||||
[:base-name :is-image :modified :name :resource]
|
[:base-name :is-image :modified :name :resource]
|
||||||
|
|
@ -235,11 +237,11 @@
|
||||||
(fs/mod-time %)
|
(fs/mod-time %)
|
||||||
(format-instant (fs/mod-time %)))
|
(format-instant (fs/mod-time %)))
|
||||||
(fs/name %)
|
(fs/name %)
|
||||||
(subs (str (fs/absolute %)) cl)])
|
(util/local-url %)])
|
||||||
(remove
|
(remove
|
||||||
#(or (cs/starts-with? (fs/name %) ".")
|
#(or (cs/starts-with? (fs/name %) ".")
|
||||||
(fs/directory? %))
|
(fs/directory? %))
|
||||||
(file-seq (clojure.java.io/file data-path))))]
|
(file-seq (clojure.java.io/file util/upload-dir)))))]
|
||||||
(log/info (with-out-str (pprint files)))
|
(log/info (with-out-str (pprint files)))
|
||||||
(layout/render
|
(layout/render
|
||||||
"list-uploads.html"
|
"list-uploads.html"
|
||||||
|
|
@ -261,6 +263,7 @@
|
||||||
files)
|
files)
|
||||||
}))))
|
}))))
|
||||||
|
|
||||||
|
|
||||||
;;;; end of list-uploads section ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;; end of list-uploads section ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(defn upload-page
|
(defn upload-page
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
:author "Simon Brooke"}
|
:author "Simon Brooke"}
|
||||||
smeagol.util
|
smeagol.util
|
||||||
(:require [clojure.java.io :as cjio]
|
(:require [clojure.java.io :as cjio]
|
||||||
|
[clojure.string :as cs]
|
||||||
[environ.core :refer [env]]
|
[environ.core :refer [env]]
|
||||||
[me.raynes.fs :as fs]
|
[me.raynes.fs :as fs]
|
||||||
[noir.io :as io]
|
[noir.io :as io]
|
||||||
|
|
@ -49,6 +50,51 @@
|
||||||
(def upload-dir
|
(def upload-dir
|
||||||
(str (cjio/file content-dir "uploads")))
|
(str (cjio/file content-dir "uploads")))
|
||||||
|
|
||||||
|
(def local-url-base
|
||||||
|
(let [a (str (fs/absolute content-dir))]
|
||||||
|
(subs a 0 (- (count a) (count "content")))))
|
||||||
|
|
||||||
|
(defn not-servable-reason
|
||||||
|
"As a string, the reason this `file-path` cannot safely be served, or `nil`
|
||||||
|
if it is safe to serve. This reason may be logged, but should *not* be
|
||||||
|
shown to remote users, as it would allow file system probing."
|
||||||
|
[file-path]
|
||||||
|
(let [path (fs/absolute file-path)]
|
||||||
|
(cond
|
||||||
|
(cs/includes? file-path "..")
|
||||||
|
(cs/join " " file-path
|
||||||
|
"Attempts to ascend the file hierarchy are disallowed.")
|
||||||
|
(not (cs/starts-with? path local-url-base))
|
||||||
|
(cs/join " " [path "is not servable"])
|
||||||
|
(not (fs/exists? path))
|
||||||
|
(cs/join " " [path "does not exist"])
|
||||||
|
(not (fs/readable? path))
|
||||||
|
(cs/join " " [path "is not readable"]))))
|
||||||
|
|
||||||
|
(defn local-url?
|
||||||
|
"True if this `file-path` can be served as a local URL, else false."
|
||||||
|
[file-path]
|
||||||
|
(empty? (not-servable-reason file-path)))
|
||||||
|
|
||||||
|
(defn local-url
|
||||||
|
"Return a local URL for this `file-path`, or a deliberate 404 if none
|
||||||
|
can be safely served."
|
||||||
|
[file-path]
|
||||||
|
(try
|
||||||
|
(let [path (fs/absolute file-path)
|
||||||
|
problem (not-servable-reason path)]
|
||||||
|
(if
|
||||||
|
(empty? problem)
|
||||||
|
(subs (str path) (count local-url-base))
|
||||||
|
(do
|
||||||
|
(log/error
|
||||||
|
"In `smeagol.util/local-url `" file-path "` is not a servable resource.")
|
||||||
|
(str "404-not-found?path=" file-path))))
|
||||||
|
(catch Exception any
|
||||||
|
(log/error
|
||||||
|
"In `smeagol.util/local-url `" file-path "` is not a servable resource:" any)
|
||||||
|
(str "404-not-found?path=" file-path))))
|
||||||
|
|
||||||
(defn standard-params
|
(defn standard-params
|
||||||
"Return a map of standard parameters to pass to the template renderer."
|
"Return a map of standard parameters to pass to the template renderer."
|
||||||
[request]
|
[request]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue