mirror of
https://github.com/journeyman-cc/smeagol.git
synced 2026-04-12 18:05:06 +00:00
Merge branch 'release/1.0.0rc1'
This commit is contained in:
commit
e54e7fac66
24 changed files with 1016 additions and 263 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -17,3 +17,4 @@ pom.xml.asc
|
|||
smeagol.log*
|
||||
/node_modules/
|
||||
.DS_Store
|
||||
|
||||
|
|
|
|||
79
README.md
79
README.md
|
|
@ -11,6 +11,78 @@ Smeagol is now a fully working small Wiki engine, and meets my own immediate nee
|
|||
## Markup syntax
|
||||
Smeagol uses the Markdown format as provided by [markdown-clj](https://github.com/yogthos/markdown-clj), with the addition that anything enclosed in double square brackets, \[\[like this\]\], will be treated as a link into the wiki itself.
|
||||
|
||||
### Pluggable extensible markup
|
||||
|
||||
A system of pluggable, extensible formatters is supported. In normal markdown, code blocks may be delimited by three backticks at start and end, and often the syntax of the code can be indicated by a token immediately following the opening three backticks. This has been extended to allow custom formatters to be provided for such code blocks. Two example formatters are provided:
|
||||
|
||||
#### The Vega formatter
|
||||
|
||||
Inspired by [visdown](http://visdown.amitkaps.com/) and [vega-lite](https://vega.github.io/vega-lite/docs/), the Vega formatter allows you to embed vega data visualisations into Smeagol pages. The graph description should start with a line comprising three back-ticks and then the word '`vega`', and end with a line comprising just three backticks.
|
||||
|
||||
Here's an example cribbed in its entirety from [here](http://visdown.amitkaps.com/london):
|
||||
|
||||
##### Flight punctuality at London airports
|
||||
|
||||
```vega
|
||||
data:
|
||||
url: "data/london.csv"
|
||||
transform:
|
||||
-
|
||||
filter: datum.year == 2016
|
||||
mark: rect
|
||||
encoding:
|
||||
x:
|
||||
type: nominal
|
||||
field: source
|
||||
y:
|
||||
type: nominal
|
||||
field: dest
|
||||
color:
|
||||
type: quantitative
|
||||
field: flights
|
||||
aggregate: sum
|
||||
```
|
||||
|
||||
Data files can be uploaded in the same way as images, by using the **upload a file** link.
|
||||
|
||||
Note that this visualisation will not be rendered in the GitHub wiki, as it doesn't have Smeagol's data visualisation magic. This is what it should look like:
|
||||
|
||||

|
||||
|
||||
#### The Mermaid formatter
|
||||
|
||||
Graphs can now be embedded in a page using the [Mermaid](http://knsv.github.io/mermaid/index.html) graph description language. The graph description should start with a line comprising three back-ticks and then the word `mermaid`, and end with a line comprising just three backticks.
|
||||
|
||||
Here's an example culled from the Mermaid documentation.
|
||||
|
||||
##### GANTT Chart
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
title Adding GANTT diagram functionality to mermaid
|
||||
section A section
|
||||
Completed task :done, des1, 2014-01-06,2014-01-08
|
||||
Active task :active, des2, 2014-01-09, 3d
|
||||
Future task : des3, after des2, 5d
|
||||
Future task2 : des4, after des3, 5d
|
||||
section Critical tasks
|
||||
Completed task in the critical line :crit, done, 2014-01-06,24h
|
||||
Implement parser and jison :crit, done, after des1, 2d
|
||||
Create tests for parser :crit, active, 3d
|
||||
Future task in critical line :crit, 5d
|
||||
Create tests for renderer :2d
|
||||
Add to mermaid :1d
|
||||
```
|
||||
|
||||
To add your own formatter, compile it into a jar file which is on the classpath - it does *not* have to be part of the Smeagol project directly, and then edit the value of the key `:formatters` in the file `config.edn`; whose standard definition is:
|
||||
|
||||
:formatters {"vega" smeagol.formatting/process-vega
|
||||
"vis" smeagol.formatting/process-vega
|
||||
"mermaid" smeagol.formatting/process-mermaid}
|
||||
|
||||
The added key should be the word which will follow the opening three backticks of your code block, and the value of that key should be a symbol which evaluates to a function which can format the code block as required.
|
||||
|
||||
## Security and authentication
|
||||
Security is now greatly improved. There is a file called *passwd* in the *resources* directory, which contains a clojure map which maps usernames to maps with plain-text passwords and emails thus:
|
||||
|
||||
|
|
@ -20,15 +92,12 @@ Security is now greatly improved. There is a file called *passwd* in the *resour
|
|||
that is to say, the username is a keyword and the corresponding password is a string. However, since version 0.5.0, users can now change their own passwords, and when the user changes their password their new password is encrypted using the [scrypt](http://www.tarsnap.com/scrypt.html) one-way encryption scheme. The password file is now no longer either in the *resources/public* directory so cannot be downloaded through the browser, nor in the git archive to which the Wiki content is stored, so that even if that git archive is remotely clonable an attacker cannot get the password file that way.
|
||||
|
||||
## Images
|
||||
Smeagol does not currently have any mechanism to upload images. You can, however, link to images already available on the web, like this:
|
||||
You can (if you're logged in) upload files, including images, using the **Upload a file** link on the top menu bar. You can link to an uploaded image, or other images already available on the web, like this:
|
||||
|
||||

|
||||
|
||||
## Todo
|
||||
* Mechanism to add users through the user interface;
|
||||
|
||||
## Advertisement
|
||||
If you like what you see here, I am available for work on open source Clojure projects. Contact me via [WEFT](http://www.weft.scot/).
|
||||
If you like what you see here, I am available for work on open source Clojure projects.
|
||||
|
||||
### Phoning home
|
||||
Smeagol currently requests the WEFT logo in the page footer from my home site. This is mainly so I can get a feel for how many people are using the product. If you object to this, edit the file
|
||||
|
|
|
|||
36
project.clj
36
project.clj
|
|
@ -1,8 +1,15 @@
|
|||
(defproject smeagol "0.5.0-rc3"
|
||||
(defproject smeagol "1.0.0-rc1"
|
||||
:description "A simple Git-backed Wiki inspired by Gollum"
|
||||
:url "https://github.com/simon-brooke/smeagol"
|
||||
:dependencies [[org.clojure/clojure "1.7.0"]
|
||||
:dependencies [[org.clojure/clojure "1.8.0"]
|
||||
[org.clojure/core.memoize "0.5.9"]
|
||||
[org.clojure/data.json "0.2.6"]
|
||||
[org.clojure/tools.logging "0.4.0"]
|
||||
[org.clojars.simon_brooke/internationalisation "1.0.0"]
|
||||
[clj-jgit "0.8.9"]
|
||||
[clj-yaml "0.4.0"]
|
||||
[com.cemerick/url "0.1.1"]
|
||||
[com.fzakaria/slf4j-timbre "0.3.7"]
|
||||
[com.taoensso/encore "2.91.1"]
|
||||
[lib-noir "0.9.9" :exclusions [org.clojure/tools.reader]]
|
||||
[com.cemerick/url "0.1.1"]
|
||||
|
|
@ -18,13 +25,25 @@
|
|||
[org.slf4j/jcl-over-slf4j "1.7.25"]
|
||||
|
||||
[com.taoensso/tower "3.0.2" :exclusions [com.taoensso/encore]]
|
||||
<<<<<<< Temporary merge branch 1
|
||||
[markdown-clj "0.9.99" :exclusions [com.keminglabs/cljx]]
|
||||
=======
|
||||
[markdown-clj "0.9.91" :exclusions [com.keminglabs/cljx]]
|
||||
>>>>>>> Temporary merge branch 2
|
||||
[crypto-password "0.2.0"]
|
||||
[clj-jgit "0.8.9"]
|
||||
[environ "1.1.0"]
|
||||
[im.chit/cronj "1.4.4"]
|
||||
[lib-noir "0.9.9" :exclusions [org.clojure/tools.reader]]
|
||||
[markdown-clj "0.9.99" :exclusions [com.keminglabs/cljx]]
|
||||
[noir-exception "0.2.5"]
|
||||
[prone "1.1.4"]]
|
||||
[org.slf4j/slf4j-api "1.7.25"]
|
||||
[org.slf4j/log4j-over-slf4j "1.7.25"]
|
||||
[org.slf4j/jul-to-slf4j "1.7.25"]
|
||||
[org.slf4j/jcl-over-slf4j "1.7.25"]
|
||||
[prone "1.1.4"]
|
||||
[ring-server "0.4.0"]
|
||||
[selmer "1.11.0"]]
|
||||
|
||||
:repl-options {:init-ns smeagol.repl}
|
||||
:jvm-opts ["-server"]
|
||||
|
|
@ -32,10 +51,13 @@
|
|||
[lein-environ "1.0.0"]
|
||||
[lein-bower "0.5.1"]
|
||||
[lein-ancient "0.5.5" :exclusions [org.clojure/clojure org.clojure/data.xml]]
|
||||
[lein-marginalia "0.7.1" :exclusions [org.clojure/clojure]]]
|
||||
:bower-dependencies [[simplemde "1.11.2"]]
|
||||
:docker {:image-name "simonbrooke/smeagol"
|
||||
:dockerfile "Dockerfile"}
|
||||
[lein-marginalia "0.7.1" :exclusions [org.clojure/clojure]]
|
||||
[lein-codox "0.10.3"]]
|
||||
:bower-dependencies [[simplemde "1.11.2"]
|
||||
;; [vega-embed "3.0.0-beta.19"] vega-embed currently not loaded from Bower because of
|
||||
;; dependency conflict which will hopefully be resolved soon.
|
||||
[vega-lite "2.0.0-beta.10"]
|
||||
[mermaid "6.0.0"]]
|
||||
:ring {:handler smeagol.handler/app
|
||||
:init smeagol.handler/init
|
||||
:destroy smeagol.handler/destroy}
|
||||
|
|
|
|||
|
|
@ -22,83 +22,13 @@
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;;; config.edn: a simple configuration map for Smeagol; inspired by Cryogen.
|
||||
;;; This is essentially all the text in the chrome - that which isn't editable
|
||||
;;; through the wiki itself
|
||||
;;; This is top-level configuration.
|
||||
|
||||
;; ; ; ; ; ; ; ; ; ;
|
||||
{:add-user-label "Add new user" ;; label for the add user link on edit users page
|
||||
:change-pass-label "Change password!"
|
||||
;; text of the change password widget itself on the
|
||||
;; change password page
|
||||
:change-pass-link "Change password"
|
||||
;; text of the change password link on the menu
|
||||
:change-pass-prompt "To change your password"
|
||||
;; text of the change password widget prompt on the
|
||||
;; change password page
|
||||
:change-col-hdr "Changes" ;; header for the changes column in history
|
||||
:chpass-bad-match "Your proposed passwords don't match"
|
||||
;; error text if proposed passwords don't match
|
||||
:chpass-fail "Your password was not changed"
|
||||
;; error text on fail other htan too short or bad match
|
||||
:chpass-success "Your password was changed"
|
||||
;; confirmation text on password change
|
||||
:chpass-too-short "You proposed password wasn't long enough: eight characters required"
|
||||
;; error text if proposed password is too short
|
||||
:chpass-title-prefix "Change password for"
|
||||
;; prefix for title of change password page
|
||||
:cookies-about "About cookies" ;; about cookies text
|
||||
:cookies-more "This website stores session information as a 'cookie' on your browser. This helps us show you the content you want to see. This cookie does not identify you, and cannot be read by other websites. It is deleted by your browser as soon as you leave this site. This website does not use any third party cookies, so your visit here cannot be tracked by other websites."
|
||||
;; more about cookies text
|
||||
:default-page-title "Introduction" ;; title of the default page in this wiki
|
||||
:del-col-hdr "Delete" ;; header for delete column on edit users page
|
||||
:del-user-fail "Could not delete user"
|
||||
;; error message on failure to delete user
|
||||
:del-user-success "Successfully deleted user"
|
||||
;; confirmation message on deletion of user
|
||||
:diff-title-prefix "Changes since version"
|
||||
;; prefix for the header of the changes page
|
||||
:edit-col-hdr "Edit" ;; header for edit column on edit users page
|
||||
:edit-page-link "Edit this page"
|
||||
;; text of the edit page link on the content frame
|
||||
:edit-title-prefix "Edit" ;; prefix for title of edit content page
|
||||
:edit-users-link "Edit users" ;; text of the edit users link on the menu
|
||||
:edit-users-title "Select user to edit"
|
||||
;; title of edit users page
|
||||
:email-prompt "Email address" ;; text of the email widget prompt on edit user page
|
||||
:is-admin-prompt "Is administrator?"
|
||||
:home-link "Home" ;; text of the home link on the menu
|
||||
:login-label "Log in!" ;; text of the login widget on the login page
|
||||
:login-link "Log in" ;; text of the login link on the menu
|
||||
:login-prompt "To edit this wiki"
|
||||
;; text of the action widget prompt on the login page
|
||||
:logout-label "Log out!" ;; text of the logout widget on the logout page
|
||||
:logout-link "Log out" ;; text of the logout link on the menu
|
||||
:logged-in-as "You are logged in as"
|
||||
;; text of the 'logged in as' label on the menu
|
||||
:history-link "History" ;; text of the history link on the content frame
|
||||
:history-title-prefix "History of" ;; prefix of the title on the history page
|
||||
:new-pass-prompt "New password" ;; text of the new password widget prompt on the change
|
||||
;; password and edit user pages
|
||||
:old-pass-prompt "Your password"
|
||||
;; text of the old password widget prompt on the change
|
||||
;; password page, and password widget on login page
|
||||
:rpt-pass-prompt "And again" ;; text of the new password widget prompt on the change
|
||||
;; password and edit user pages
|
||||
:save-prompt "When you have finished editing"
|
||||
;; text of the save widget label on edit content
|
||||
;; and edit user page
|
||||
:save-label "Save!" ;; text of the save widget itself
|
||||
:save-user-fail "Failed to store user"
|
||||
:save-user-success "Successfully stored user"
|
||||
{
|
||||
:site-title "Smeagol" ;; overall title of the site, used in page headings
|
||||
:username-prompt "Username" ;; text of the username widget prompt on edit user page
|
||||
;; text of the is admin widget prompt on edit user page
|
||||
:user-title-prefix "Edit user" ;; prefix for title of edit user page
|
||||
:vers-col-hdr "Version" ;; header for the version column in history
|
||||
:what-col-hdr "What" ;; header for the what column in history
|
||||
:what-changed-prompt "What have you changed?"
|
||||
;; text of the summary widget prompt on edit
|
||||
;; content page
|
||||
:when-col-hdr "When" ;; header for the when column in history
|
||||
:your-uname-prompt "Your username" ;; text of the username widget prompt on the login page
|
||||
:default-locale "en-GB" ;; default language used for messages
|
||||
:formatters {"vega" smeagol.formatting/process-vega
|
||||
"vis" smeagol.formatting/process-vega
|
||||
"mermaid" smeagol.formatting/process-mermaid}
|
||||
}
|
||||
|
|
|
|||
107
resources/i18n/en-GB.edn
Normal file
107
resources/i18n/en-GB.edn
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; Smeagol: a very simple Wiki engine.
|
||||
;;;;
|
||||
;;;; This program is free software; you can redistribute it and/or
|
||||
;;;; modify it under the terms of the GNU General Public License
|
||||
;;;; as published by the Free Software Foundation; either version 2
|
||||
;;;; of the License, or (at your option) any later version.
|
||||
;;;;
|
||||
;;;; This program is distributed in the hope that it will be useful,
|
||||
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;;; GNU General Public License for more details.
|
||||
;;;;
|
||||
;;;; You should have received a copy of the GNU General Public License
|
||||
;;;; along with this program; if not, write to the Free Software
|
||||
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
;;;; USA.
|
||||
;;;;
|
||||
;;;; Copyright (C) 2017 Simon Brooke
|
||||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;;; en-GB.edn: English-language messages.
|
||||
;;; This is essentially all the text in the chrome - that which isn't editable
|
||||
;;; through the wiki itself
|
||||
|
||||
;; ; ; ; ; ; ; ; ; ;
|
||||
{:add-user-label "Add new user" ;; label for the add user link on edit users page
|
||||
:change-pass-label "Change password!"
|
||||
;; text of the change password widget itself on the
|
||||
;; change password page
|
||||
:change-pass-link "Change password"
|
||||
;; text of the change password link on the menu
|
||||
:change-pass-prompt "To change your password"
|
||||
;; text of the change password widget prompt on the
|
||||
;; change password page
|
||||
:change-col-hdr "Changes" ;; header for the changes column in history
|
||||
:chpass-bad-match "Your proposed passwords don't match"
|
||||
;; error text if proposed passwords don't match
|
||||
:chpass-fail "Your password was not changed"
|
||||
;; error text on fail other htan too short or bad match
|
||||
:chpass-success "Your password was changed"
|
||||
;; confirmation text on password change
|
||||
:chpass-too-short "You proposed password wasn't long enough: eight characters required"
|
||||
;; error text if proposed password is too short
|
||||
:chpass-title-prefix "Change password for"
|
||||
;; prefix for title of change password page
|
||||
:cookies-about "About cookies" ;; about cookies text
|
||||
:cookies-more "This website stores session information as a 'cookie' on your browser. This helps us show you the content you want to see. This cookie does not identify you, and cannot be read by other websites. It is deleted by your browser as soon as you leave this site. This website does not use any third party cookies, so your visit here cannot be tracked by other websites."
|
||||
;; more about cookies text
|
||||
:default-page-title "Introduction" ;; title of the default page in this wiki
|
||||
:del-col-hdr "Delete" ;; header for delete column on edit users page
|
||||
:del-user-fail "Could not delete user"
|
||||
;; error message on failure to delete user
|
||||
:del-user-success "Successfully deleted user"
|
||||
;; confirmation message on deletion of user
|
||||
:diff-title-prefix "Changes since version"
|
||||
;; prefix for the header of the changes page
|
||||
:edit-col-hdr "Edit" ;; header for edit column on edit users page
|
||||
:edit-page-link "Edit this page"
|
||||
;; text of the edit page link on the content frame
|
||||
:edit-title-prefix "Edit" ;; prefix for title of edit content page
|
||||
:edit-users-link "Edit users" ;; text of the edit users link on the menu
|
||||
:edit-users-title "Select user to edit"
|
||||
;; title of edit users page
|
||||
:email-prompt "Email address" ;; text of the email widget prompt on edit user page
|
||||
:file-upload-link-text "You may link to this file using a link of the form"
|
||||
;; Text introducing the link to an uploaded file
|
||||
:file-upload-prompt "File to upload" ;; prompt string for the file upload widget
|
||||
:file-upload-title "Upload a file" ;; title for the file upload page
|
||||
:is-admin-prompt "Is administrator?"
|
||||
:home-link "Home" ;; text of the home link on the menu
|
||||
:login-label "Log in!" ;; text of the login widget on the login page
|
||||
:login-link "Log in" ;; text of the login link on the menu
|
||||
:login-prompt "To edit this wiki"
|
||||
;; text of the action widget prompt on the login page
|
||||
:logout-label "Log out!" ;; text of the logout widget on the logout page
|
||||
:logout-link "Log out" ;; text of the logout link on the menu
|
||||
:logged-in-as "You are logged in as"
|
||||
;; text of the 'logged in as' label on the menu
|
||||
:history-link "History" ;; text of the history link on the content frame
|
||||
:history-title-prefix "History of" ;; prefix of the title on the history page
|
||||
:new-pass-prompt "New password" ;; text of the new password widget prompt on the change
|
||||
;; password and edit user pages
|
||||
:old-pass-prompt "Your password"
|
||||
;; text of the old password widget prompt on the change
|
||||
;; password page, and password widget on login page
|
||||
:rpt-pass-prompt "And again" ;; text of the new password widget prompt on the change
|
||||
;; password and edit user pages
|
||||
:save-prompt "When you have finished editing"
|
||||
;; text of the save widget label on edit content
|
||||
;; and edit user page
|
||||
:save-label "Save!" ;; text of the save widget itself
|
||||
:save-user-fail "Failed to store user"
|
||||
:save-user-success "Successfully stored user"
|
||||
:username-prompt "Username" ;; text of the username widget prompt on edit user page
|
||||
;; text of the is admin widget prompt on edit user page
|
||||
:user-title-prefix "Edit user" ;; prefix for title of edit user page
|
||||
:vers-col-hdr "Version" ;; header for the version column in history
|
||||
:what-col-hdr "What" ;; header for the what column in history
|
||||
:what-changed-prompt "What have you changed?"
|
||||
;; text of the summary widget prompt on edit
|
||||
;; content page
|
||||
:when-col-hdr "When" ;; header for the when column in history
|
||||
:your-uname-prompt "Your username" ;; text of the username widget prompt on the login page
|
||||
}
|
||||
|
|
@ -11,6 +11,78 @@ Smeagol is now a fully working small Wiki engine, and meets my own immediate nee
|
|||
## Markup syntax
|
||||
Smeagol uses the Markdown format as provided by [markdown-clj](https://github.com/yogthos/markdown-clj), with the addition that anything enclosed in double square brackets, \[\[like this\]\], will be treated as a link into the wiki itself.
|
||||
|
||||
### Pluggable extensible markup
|
||||
|
||||
A system of pluggable, extensible formatters is supported. In normal markdown, code blocks may be delimited by three backticks at start and end, and often the syntax of the code can be indicated by a token immediately following the opening three backticks. This has been extended to allow custom formatters to be provided for such code blocks. Two example formatters are provided:
|
||||
|
||||
#### The Vega formatter
|
||||
|
||||
Inspired by [visdown](http://visdown.amitkaps.com/) and [vega-lite](https://vega.github.io/vega-lite/docs/), the Vega formatter allows you to embed vega data visualisations into Smeagol pages. The graph description should start with a line comprising three back-ticks and then the word '`vega`', and end with a line comprising just three backticks.
|
||||
|
||||
Here's an example cribbed in its entirety from [here](http://visdown.amitkaps.com/london):
|
||||
|
||||
##### Flight punctuality at London airports
|
||||
|
||||
```vega
|
||||
data:
|
||||
url: "data/london.csv"
|
||||
transform:
|
||||
-
|
||||
filter: datum.year == 2016
|
||||
mark: rect
|
||||
encoding:
|
||||
x:
|
||||
type: nominal
|
||||
field: source
|
||||
y:
|
||||
type: nominal
|
||||
field: dest
|
||||
color:
|
||||
type: quantitative
|
||||
field: flights
|
||||
aggregate: sum
|
||||
```
|
||||
|
||||
Data files can be uploaded in the same way as images, by using the **upload a file** link.
|
||||
|
||||
Note that this visualisation will not be rendered in the GitHub wiki, as it doesn't have Smeagol's data visualisation magic. This is what it should look like:
|
||||
|
||||

|
||||
|
||||
#### The Mermaid formatter
|
||||
|
||||
Graphs can now be embedded in a page using the [Mermaid](http://knsv.github.io/mermaid/index.html) graph description language. The graph description should start with a line comprising three back-ticks and then the word `mermaid`, and end with a line comprising just three backticks.
|
||||
|
||||
Here's an example culled from the Mermaid documentation.
|
||||
|
||||
##### GANTT Chart
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
title Adding GANTT diagram functionality to mermaid
|
||||
section A section
|
||||
Completed task :done, des1, 2014-01-06,2014-01-08
|
||||
Active task :active, des2, 2014-01-09, 3d
|
||||
Future task : des3, after des2, 5d
|
||||
Future task2 : des4, after des3, 5d
|
||||
section Critical tasks
|
||||
Completed task in the critical line :crit, done, 2014-01-06,24h
|
||||
Implement parser and jison :crit, done, after des1, 2d
|
||||
Create tests for parser :crit, active, 3d
|
||||
Future task in critical line :crit, 5d
|
||||
Create tests for renderer :2d
|
||||
Add to mermaid :1d
|
||||
```
|
||||
|
||||
To add your own formatter, compile it into a jar file which is on the classpath - it does *not* have to be part of the Smeagol project directly, and then edit the value of the key `:formatters` in the file `config.edn`; whose standard definition is:
|
||||
|
||||
:formatters {"vega" smeagol.formatting/process-vega
|
||||
"vis" smeagol.formatting/process-vega
|
||||
"mermaid" smeagol.formatting/process-mermaid}
|
||||
|
||||
The added key should be the word which will follow the opening three backticks of your code block, and the value of that key should be a symbol which evaluates to a function which can format the code block as required.
|
||||
|
||||
## Security and authentication
|
||||
Security is now greatly improved. There is a file called *passwd* in the *resources* directory, which contains a clojure map which maps usernames to maps with plain-text passwords and emails thus:
|
||||
|
||||
|
|
@ -20,12 +92,12 @@ Security is now greatly improved. There is a file called *passwd* in the *resour
|
|||
that is to say, the username is a keyword and the corresponding password is a string. However, since version 0.5.0, users can now change their own passwords, and when the user changes their password their new password is encrypted using the [scrypt](http://www.tarsnap.com/scrypt.html) one-way encryption scheme. The password file is now no longer either in the *resources/public* directory so cannot be downloaded through the browser, nor in the git archive to which the Wiki content is stored, so that even if that git archive is remotely clonable an attacker cannot get the password file that way.
|
||||
|
||||
## Images
|
||||
Smeagol does not currently have any mechanism to upload images. You can, however, link to images already available on the web, like this:
|
||||
You can (if you're logged in) upload files, including images, using the **Upload a file** link on the top menu bar. You can link to an uploaded image, or other images already available on the web, like this:
|
||||
|
||||

|
||||
|
||||
## Advertisement
|
||||
If you like what you see here, I am available for work on open source Clojure projects. Contact me via [WEFT](http://www.weft.scot/).
|
||||
If you like what you see here, I am available for work on open source Clojure projects.
|
||||
|
||||
### Phoning home
|
||||
Smeagol currently requests the WEFT logo in the page footer from my home site. This is mainly so I can get a feel for how many people are using the product. If you object to this, edit the file
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
This is the side bar. There's nothing in it yet. You could [edit](edit?page=_side-bar) it to provide internal navigation or branding.
|
||||
|
||||
If you don't like it on the left, float it to the right (or do something entirely different) by editing the [stylesheet](/edit-css?page=stylesheet).
|
||||
If you don't like it on the left, float it to the right (or do something entirely different) by editing the [stylesheet](edit-css?page=stylesheet).
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ form {
|
|||
header {
|
||||
margin-top: 0;
|
||||
width:100%;
|
||||
max-width: 100%;
|
||||
background-color: gray;
|
||||
color: white;
|
||||
}
|
||||
|
|
@ -196,6 +197,11 @@ th {
|
|||
border: thin solid silver;
|
||||
}
|
||||
|
||||
|
||||
.data-visualisation {
|
||||
padding: 0.25em 5%;
|
||||
}
|
||||
|
||||
.error {
|
||||
width: 100%;
|
||||
background-color: red;
|
||||
|
|
@ -230,6 +236,15 @@ th {
|
|||
color: white;
|
||||
}
|
||||
|
||||
.vega-bindings, .vega-actions {
|
||||
font-size: 66%;
|
||||
}
|
||||
|
||||
/* Add space between Vega-Embed links */
|
||||
.vega-actions a {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.warn {
|
||||
color: maroon;
|
||||
}
|
||||
|
|
|
|||
158
resources/public/data/london.csv
Normal file
158
resources/public/data/london.csv
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
"source","dest","airline","flights","onTimePerf","delayAverage","year"
|
||||
"LHR","ORD","AA",2490,66.33,21.11,2010
|
||||
"LHR","ORD","BA",1413,57.63,23.3,2010
|
||||
"LHR","ORD","UA",2105,73.24,14.57,2010
|
||||
"LHR","ORD","VS",218,77.06,11.1,2010
|
||||
"LHR","LAX","AA",706,66.38,17.66,2010
|
||||
"LHR","LAX","BA",1914,50.18,31.01,2010
|
||||
"LHR","LAX","UA",698,81.81,13.62,2010
|
||||
"LHR","LAX","VS",1285,56.86,21.92,2010
|
||||
"LHR","JFK","AA",3205,67.36,20.31,2010
|
||||
"LHR","JFK","BA",4181,66.59,20.84,2010
|
||||
"LHR","JFK","DL",1611,62.12,23.74,2010
|
||||
"LHR","JFK","KU",305,46.56,38.75,2010
|
||||
"LHR","JFK","VS",2067,59.62,23.69,2010
|
||||
"LHR","EWR","BA",1828,70.25,17.67,2010
|
||||
"LHR","EWR","CO",2697,69.28,18.87,2010
|
||||
"LHR","EWR","VS",1399,65.62,23.15,2010
|
||||
"LHR","IAD","BA",2011,62.11,21.71,2010
|
||||
"LHR","IAD","UA",2094,74.67,14.11,2010
|
||||
"LHR","IAD","VS",700,66.24,19.75,2010
|
||||
"LCY","JFK","BA",1012,89.12,5.67,2010
|
||||
"LHR","ORD","AA",2532,76.87,13.34,2011
|
||||
"LHR","ORD","BA",1440,76.32,14.42,2011
|
||||
"LHR","ORD","UA",2106,79.36,15.2,2011
|
||||
"LHR","ORD","VS",302,82.72,8.66,2011
|
||||
"LHR","LAX","AA",730,75.48,13.66,2011
|
||||
"LHR","LAX","BA",1984,63.46,18.47,2011
|
||||
"LHR","LAX","UA",698,83.52,11.7,2011
|
||||
"LHR","LAX","VS",1397,79,11.2,2011
|
||||
"LHR","JFK","AA",2917,75.8,14.77,2011
|
||||
"LHR","JFK","BA",4944,73.56,14.65,2011
|
||||
"LHR","JFK","DL",2144,80.91,13.05,2011
|
||||
"LHR","JFK","KU",302,52.65,30.02,2011
|
||||
"LHR","JFK","VS",2104,76.32,15.09,2011
|
||||
"LHR","EWR","BA",2129,80.7,11.24,2011
|
||||
"LHR","EWR","CO",3503,78.42,14.19,2011
|
||||
"LHR","EWR","VS",1410,77.52,13.39,2011
|
||||
"LHR","IAD","BA",2157,74.08,14.11,2011
|
||||
"LHR","IAD","UA",2516,80.79,14,2011
|
||||
"LHR","IAD","VS",711,82.42,9.73,2011
|
||||
"LCY","JFK","BA",1031,92.05,4.12,2011
|
||||
"LHR","ORD","AA",2523,72.07,20.03,2012
|
||||
"LHR","ORD","BA",1445,70.03,17.73,2012
|
||||
"LHR","ORD","UA",2102,76.08,15.35,2012
|
||||
"LHR","ORD","VS",308,83.77,8.31,2012
|
||||
"LHR","LAX","AA",714,75.35,12.03,2012
|
||||
"LHR","LAX","BA",1874,63.48,18.34,2012
|
||||
"LHR","LAX","UA",693,76.16,14.88,2012
|
||||
"LHR","LAX","VS",1324,76.13,14.08,2012
|
||||
"LHR","JFK","AA",3367,75.72,12.75,2012
|
||||
"LHR","JFK","BA",5129,69.28,17.68,2012
|
||||
"LHR","JFK","DL",2151,77.63,13.59,2012
|
||||
"LHR","JFK","KU",307,52.12,32.02,2012
|
||||
"LHR","JFK","VS",2227,81.9,11.32,2012
|
||||
"LHR","EWR","BA",1920,73.02,15.88,2012
|
||||
"LHR","EWR","CO",612,84.48,9.24,2012
|
||||
"LHR","EWR","UA",2872,69.76,17.91,2012
|
||||
"LHR","EWR","VS",1421,76.14,16.68,2012
|
||||
"LHR","IAD","BA",2014,71.45,16.23,2012
|
||||
"LHR","IAD","UA",2444,69.04,18.98,2012
|
||||
"LHR","IAD","VS",703,87.06,7.71,2012
|
||||
"LCY","JFK","BA",474,91.33,3.74,2012
|
||||
"LCY","JFK","BA",537,89.37,4.71,2012
|
||||
"LHR","ORD","AA",2513,74.69,15.29,2013
|
||||
"LHR","ORD","BA",1440,65.35,20.84,2013
|
||||
"LHR","ORD","UA",2110,78.39,14.13,2013
|
||||
"LHR","ORD","VS",324,83.02,9.05,2013
|
||||
"LHR","LAX","AA",728,76.48,12.56,2013
|
||||
"LHR","LAX","BA",1864,59.5,27.43,2013
|
||||
"LHR","LAX","UA",702,85.47,9.86,2013
|
||||
"LHR","LAX","VS",1288,79,13.76,2013
|
||||
"LHR","JFK","AA",2991,76.4,13.73,2013
|
||||
"LHR","JFK","BA",5350,66.97,18.95,2013
|
||||
"LHR","JFK","DL",2166,82.09,10.81,2013
|
||||
"LHR","JFK","KU",306,62.42,21.53,2013
|
||||
"LHR","JFK","VS",2825,81.91,10.53,2013
|
||||
"LHR","EWR","BA",1838,71.44,16.72,2013
|
||||
"LHR","EWR","UA",3494,80.59,12.36,2013
|
||||
"LHR","EWR","VS",1419,81.66,11.7,2013
|
||||
"LHR","IAD","BA",2012,71.02,18.67,2013
|
||||
"LHR","IAD","UA",2496,80.39,12.82,2013
|
||||
"LHR","IAD","VS",702,86.89,7.1,2013
|
||||
"LCY","JFK","BA",945,90.64,4.58,2013
|
||||
"LGW","LAX","DY",105,72.38,36.83,2014
|
||||
"LGW","JFK","DY",156,51.92,27.79,2014
|
||||
"LHR","ORD","AA",2476,73.5,22.09,2014
|
||||
"LHR","ORD","BA",1444,65.28,18.7,2014
|
||||
"LHR","ORD","UA",2110,77.56,13.68,2014
|
||||
"LHR","ORD","VS",348,81.32,8.86,2014
|
||||
"LHR","LAX","AA",728,76.79,18.56,2014
|
||||
"LHR","LAX","BA",1450,56.84,23.1,2014
|
||||
"LHR","LAX","DL",127,92.13,6.92,2014
|
||||
"LHR","LAX","UA",698,85.1,8.53,2014
|
||||
"LHR","LAX","VS",1217,80.99,12.58,2014
|
||||
"LHR","JFK","AA",2287,78.18,14.74,2014
|
||||
"LHR","JFK","BA",6018,71.29,16.59,2014
|
||||
"LHR","JFK","DL",2123,82.13,11.19,2014
|
||||
"LHR","JFK","KU",310,48.22,33.17,2014
|
||||
"LHR","JFK","VS",2820,79.77,12.45,2014
|
||||
"LHR","EWR","BA",1943,71.46,19.7,2014
|
||||
"LHR","EWR","UA",3485,78.3,15.19,2014
|
||||
"LHR","EWR","VS",1420,86.6,8.37,2014
|
||||
"LHR","IAD","BA",1807,74.58,15.31,2014
|
||||
"LHR","IAD","UA",2130,81.13,13.61,2014
|
||||
"LHR","IAD","VS",695,89.06,6.9,2014
|
||||
"LCY","JFK","BA",1003,91.23,4.43,2014
|
||||
"LGW","LAX","DY",328,66.46,21.41,2015
|
||||
"LGW","JFK","DY",543,64.09,31.78,2015
|
||||
"LHR","ORD","AA",2062,72.09,27.59,2015
|
||||
"LHR","ORD","BA",1442,71.4,16.24,2015
|
||||
"LHR","ORD","UA",2099,83.94,10.37,2015
|
||||
"LHR","ORD","VS",330,78.79,11.61,2015
|
||||
"LHR","LAX","AA",1265,75.42,14.5,2015
|
||||
"LHR","LAX","BA",1450,66.9,17.67,2015
|
||||
"LHR","LAX","DL",540,79.44,10.97,2015
|
||||
"LHR","LAX","UA",704,89.2,7.5,2015
|
||||
"LHR","LAX","VS",1230,80.81,12.74,2015
|
||||
"LHR","JFK","AA",2130,77.45,15.21,2015
|
||||
"LHR","JFK","BA",5994,77.84,13.61,2015
|
||||
"LHR","JFK","DL",2069,80.75,13.51,2015
|
||||
"LHR","JFK","KU",310,59.22,32.67,2015
|
||||
"LHR","JFK","VS",3363,76.35,13.82,2015
|
||||
"LHR","EWR","BA",1459,76.47,14.03,2015
|
||||
"LHR","EWR","DL",381,95.54,2.88,2015
|
||||
"LHR","EWR","UA",3493,79.23,16.31,2015
|
||||
"LHR","EWR","VS",871,81.75,10.7,2015
|
||||
"LHR","IAD","BA",1421,76.89,12.03,2015
|
||||
"LHR","IAD","UA",2126,81.74,14.02,2015
|
||||
"LHR","IAD","VS",703,85.49,12.59,2015
|
||||
"LCY","JFK","BA",937,92.74,4.52,2015
|
||||
"LTN","EWR","DJT",316,81.53,18.18,2015
|
||||
"LGW","LAX","DY",421,59.86,23.64,2016
|
||||
"LGW","LAX","DI",38,65.79,14.39,2016
|
||||
"LGW","JFK","BA",486,69.75,18.02,2016
|
||||
"LGW","JFK","DY",689,65.31,28.18,2016
|
||||
"LGW","JFK","DI",39,69.23,15.49,2016
|
||||
"LHR","ORD","AA",2453,71.82,26.15,2016
|
||||
"LHR","ORD","BA",1443,68.88,17.58,2016
|
||||
"LHR","ORD","UA",2085,82.25,14.2,2016
|
||||
"LHR","ORD","VS",333,89.79,5.07,2016
|
||||
"LHR","LAX","AA",1411,68.25,19.46,2016
|
||||
"LHR","LAX","BA",1452,54.34,24.89,2016
|
||||
"LHR","LAX","UA",700,87,8.48,2016
|
||||
"LHR","LAX","VS",1376,79.58,10.13,2016
|
||||
"LHR","JFK","AA",2623,76.82,21.41,2016
|
||||
"LHR","JFK","BA",5625,69.97,17.15,2016
|
||||
"LHR","JFK","DL",2081,80.37,13.42,2016
|
||||
"LHR","JFK","KU",253,19.76,71.96,2016
|
||||
"LHR","JFK","VS",3489,79.67,11.44,2016
|
||||
"LHR","EWR","AI",118,65.25,21.14,2016
|
||||
"LHR","EWR","BA",1444,76.11,13.49,2016
|
||||
"LHR","EWR","UA",3472,79.03,15.71,2016
|
||||
"LHR","EWR","VS",722,82.27,9.48,2016
|
||||
"LHR","IAD","BA",1415,71.59,17.44,2016
|
||||
"LHR","IAD","UA",2134,82.05,13.24,2016
|
||||
"LHR","IAD","VS",699,84.69,8.02,2016
|
||||
"LCY","JFK","BA",921,90.01,5.26,2016
|
||||
"LTN","EWR","DJT",333,87.05,8.44,2016
|
||||
|
BIN
resources/public/data/london.png
Normal file
BIN
resources/public/data/london.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
BIN
resources/public/uploads/water.png
Normal file
BIN
resources/public/uploads/water.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -5,8 +5,7 @@
|
|||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="{{servlet-context}}/content/stylesheet.css" media="screen" rel="stylesheet" type="text/css" />
|
||||
<link href="{{servlet-context}}/css/print.css" media="print" rel="stylesheet" type="text/css" />
|
||||
{% style "/content/stylesheet.css" %}
|
||||
{% block extra-headers %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
|
@ -21,6 +20,7 @@
|
|||
<li class="{{edit-users-selected}}"><a href="{{servlet-context}}/edit-users">{{config.edit-users-link}}</a></li>
|
||||
{% endif %}
|
||||
{% if user %}
|
||||
<li class="{{upload-selected}}"><a href="upload">{{config.file-upload-title}}</a></li>
|
||||
<li class="{{passwd-selected}}"><a href="passwd">{{config.change-pass-link}}</a></li>
|
||||
<li class="user" id="user">{{config.logged-in-as}} {{user}}</li>
|
||||
<li class="{{auth-selected}}"><a href="{{servlet-context}}/auth">
|
||||
|
|
@ -79,7 +79,7 @@
|
|||
<div>
|
||||
<img height="16" width="16" alt="one wiki to rule them all" src="img/smeagol.png"/>One Wiki to rule them all ||
|
||||
Smeagol wiki engine {{version}} ||
|
||||
<img height="16" width="16" alt="The Web Engineering Factory & Toolworks" src="http://www.weft.scot/images/weft.logo.64.png"> Developed by <a href="http://www.weft.scot/">WEFT</a>
|
||||
<img height="16" width="16" alt="The Web Engineering Factory & Toolworks" src="https://www.weft.scot/images/weft.logo.64.png"> Developed by <a href="http://www.weft.scot/">WEFT</a>
|
||||
</div>
|
||||
<div>
|
||||
Built with <a href="http://www.luminusweb.net/">LuminusWeb</a> ||
|
||||
|
|
@ -91,4 +91,3 @@
|
|||
<footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "templates/base.html" %}
|
||||
{% block extra-headers %}
|
||||
<link rel="stylesheet" href="{{servlet-context}}/vendor/simplemde/dist/simplemde.min.css">
|
||||
<script src="{{servlet-context}}/vendor/simplemde/dist/simplemde.min.js"></script>
|
||||
{% style "/vendor/simplemde/dist/simplemde.min.css" %}
|
||||
{% script "/vendor/simplemde/dist/simplemde.min.js" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
|
|
|||
33
resources/templates/upload.html
Normal file
33
resources/templates/upload.html
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{% extends "templates/base.html" %}
|
||||
{% block content %}
|
||||
<div id="content" class="auth">
|
||||
{% if uploaded %}
|
||||
{% if is-image %}
|
||||
<img alt="Uploaded image" src="uploads/{{uploaded}}"/>
|
||||
|
||||
<p>
|
||||
{{config.file-upload-link-text}}:
|
||||
|
||||
<code></code>
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
{{config.file-upload-link-text}}:
|
||||
|
||||
<code>[Uploaded file](uploads/{{uploaded}})</code>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<form action="{{servlet-context}}/upload" enctype="multipart/form-data" method="POST">
|
||||
<p class="widget">
|
||||
<label for="upload">{{config.file-upload-prompt}}</label>
|
||||
<input name="upload" id="upload" type="file" required/>
|
||||
</p>
|
||||
<p class="widget">
|
||||
<label for="submit">{{config.save-prompt}}</label>
|
||||
<input name="submit" id="submit" type="submit" class="action" value="{{config.save-label}}"/>
|
||||
</p>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -1,5 +1,17 @@
|
|||
{% extends "templates/base.html" %}
|
||||
|
||||
{% block extra-headers %}
|
||||
{% style "vendor/mermaid/dist/mermaid.css" %}
|
||||
<!-- there's at the time of writing (20170731) a problem with the dependencies of the Bower
|
||||
package for vega-embed, so we're currently not installing either it or Vega locally.
|
||||
TODO: fix -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/vega/3.0.0-rc2/vega.js"></script>
|
||||
{% script "vendor/vega-lite/build/vega-lite.js" %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/vega-embed/3.0.0-beta.19/vega-embed.js"></script>
|
||||
|
||||
{% script "vendor/mermaid/dist/mermaid.js" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="content" class="wiki">
|
||||
{% if editable %}
|
||||
|
|
@ -10,4 +22,14 @@
|
|||
{% endif %}
|
||||
{{content|safe}}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
//<![CDATA[
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
mermaid.initialize({startOnLoad:true});
|
||||
});
|
||||
}
|
||||
//]]
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
46
src/smeagol/configuration.clj
Normal file
46
src/smeagol/configuration.clj
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
(ns ^{:doc "Read and make available configuration."
|
||||
:author "Simon Brooke"}
|
||||
smeagol.configuration
|
||||
(:require [noir.io :as io]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; Smeagol: a very simple Wiki engine.
|
||||
;;;;
|
||||
;;;; This program is free software; you can redistribute it and/or
|
||||
;;;; modify it under the terms of the GNU General Public License
|
||||
;;;; as published by the Free Software Foundation; either version 2
|
||||
;;;; of the License, or (at your option) any later version.
|
||||
;;;;
|
||||
;;;; This program is distributed in the hope that it will be useful,
|
||||
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;;; GNU General Public License for more details.
|
||||
;;;;
|
||||
;;;; You should have received a copy of the GNU General Public License
|
||||
;;;; along with this program; if not, write to the Free Software
|
||||
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
;;;; USA.
|
||||
;;;;
|
||||
;;;; Copyright (C) 2017 Simon Brooke
|
||||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; Right, doing the data visualisation thing is tricky. Doing it in the
|
||||
;;;; pipeline doesn't work, because the md-to-html-string filter messes up
|
||||
;;;; both YAML and JSON notation. So we need to extract the visualisation
|
||||
;;;; fragments from the Markdown text and replace them with tokens we will
|
||||
;;;; recognise afterwards, perform md-to-html-string, and then replace our
|
||||
;;;; tokens with the transformed visualisation specification.
|
||||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
|
||||
(def config-file-path
|
||||
"The relative path to the config file."
|
||||
(str (io/resource-path) "../config.edn"))
|
||||
|
||||
|
||||
(def config
|
||||
"The actual configuration, as a map."
|
||||
(read-string (slurp config-file-path)))
|
||||
195
src/smeagol/formatting.clj
Normal file
195
src/smeagol/formatting.clj
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
(ns ^{:doc "Format Semagol's enhanced markdown format."
|
||||
:author "Simon Brooke"}
|
||||
smeagol.formatting
|
||||
(:require [clojure.data.json :as json]
|
||||
[clojure.string :as cs]
|
||||
[cemerick.url :refer (url url-encode url-decode)]
|
||||
[clj-yaml.core :as yaml]
|
||||
[noir.io :as io]
|
||||
[noir.session :as session]
|
||||
[markdown.core :as md]
|
||||
[taoensso.timbre :as timbre]
|
||||
[smeagol.authenticate :as auth]
|
||||
[smeagol.configuration :refer [config]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; Smeagol: a very simple Wiki engine.
|
||||
;;;;
|
||||
;;;; This program is free software; you can redistribute it and/or
|
||||
;;;; modify it under the terms of the GNU General Public License
|
||||
;;;; as published by the Free Software Foundation; either version 2
|
||||
;;;; of the License, or (at your option) any later version.
|
||||
;;;;
|
||||
;;;; This program is distributed in the hope that it will be useful,
|
||||
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;;; GNU General Public License for more details.
|
||||
;;;;
|
||||
;;;; You should have received a copy of the GNU General Public License
|
||||
;;;; along with this program; if not, write to the Free Software
|
||||
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
;;;; USA.
|
||||
;;;;
|
||||
;;;; Copyright (C) 2017 Simon Brooke
|
||||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; Right, doing the data visualisation thing is tricky. Doing it in the
|
||||
;;;; pipeline doesn't work, because the md-to-html-string filter messes up
|
||||
;;;; both YAML and JSON notation. So we need to extract the visualisation
|
||||
;;;; fragments from the Markdown text and replace them with tokens we will
|
||||
;;;; recognise afterwards, perform md-to-html-string, and then replace our
|
||||
;;;; tokens with the transformed visualisation specification.
|
||||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; Error to show if text to be rendered is nil.
|
||||
(def no-text-error "No text: does the file exist?")
|
||||
|
||||
|
||||
(defn local-links
|
||||
"Rewrite text in `html-src` surrounded by double square brackets as a local link into this wiki."
|
||||
[^String html-src]
|
||||
(if html-src
|
||||
(cs/replace html-src #"\[\[[^\[\]]*\]\]"
|
||||
#(let [text (clojure.string/replace %1 #"[\[\]]" "")
|
||||
encoded (url-encode text)
|
||||
;; I use '\_' to represent '_' in wiki markup, because
|
||||
;; '_' is meaningful in Markdown. However, this needs to
|
||||
;; be stripped out when interpreting local links.
|
||||
munged (cs/replace encoded #"%26%2395%3B" "_")]
|
||||
(format "<a href='wiki?page=%s'>%s</a>" munged text)))
|
||||
no-text-error))
|
||||
|
||||
|
||||
(defn yaml->json
|
||||
"Rewrite this string, assumed to be in YAML format, as JSON."
|
||||
[^String yaml-src]
|
||||
(json/write-str (yaml/parse-string yaml-src)))
|
||||
|
||||
|
||||
(declare process-text)
|
||||
|
||||
|
||||
(defn process-vega
|
||||
"Process this `vega-source` string, assumed to be in YAML format, into a specification
|
||||
of a Vega chart, and add the plumbing to render it."
|
||||
[^String vega-src ^Integer index]
|
||||
(str
|
||||
"<div class='data-visualisation' id='vis" index "'></div>\n"
|
||||
"<script>\n//<![CDATA[\nvar vl"
|
||||
index
|
||||
" = "
|
||||
(yaml->json (str "$schema: https://vega.github.io/schema/vega-lite/v2.json\n" vega-src))
|
||||
";\nvega.embed('#vis" index "', vl" index ");\n//]]\n</script>"))
|
||||
|
||||
|
||||
(defn process-mermaid
|
||||
"Lightly mung this `graph-spec`, assumed to be a mermaid specification."
|
||||
[^String graph-spec ^Integer index]
|
||||
(str "<div class=\"mermaid data-visualisation\">\n"
|
||||
graph-spec
|
||||
"\n</div>"))
|
||||
|
||||
|
||||
(defn get-first-token
|
||||
"Return the first space-separated token of this `string`."
|
||||
[^String string]
|
||||
(if string (first (cs/split string #"[^a-zA-Z0-9]+"))))
|
||||
|
||||
|
||||
(defn- process-markdown-fragment
|
||||
"Within the context of `process-text`, process a fragment believed to be markdown.
|
||||
|
||||
As with `process-text`, this function returns a map with two top-level keys:
|
||||
`:inclusions`, a map of constructed keywords to inclusion specifications,
|
||||
and `:text`, an HTML text string with the keywords present where the
|
||||
corresponding inclusion should be inserted."
|
||||
[index result fragments processed]
|
||||
(process-text
|
||||
(+ index 1)
|
||||
result
|
||||
(rest fragments)
|
||||
(cons (first fragments) processed)))
|
||||
|
||||
|
||||
(defn- apply-formatter
|
||||
"Within the context of `process-text`, process a fragment for which an explicit
|
||||
§formatter has been identified.
|
||||
|
||||
As with `process-text`, this function returns a map with two top-level keys:
|
||||
`:inclusions`, a map of constructed keywords to inclusion specifications,
|
||||
and `:text`, an HTML text string with the keywords present where the
|
||||
corresponding inclusion should be inserted."
|
||||
[index result fragments processed fragment token formatter]
|
||||
(let
|
||||
[kw (keyword (str "inclusion-" index))]
|
||||
(process-text
|
||||
(+ index 1)
|
||||
(assoc
|
||||
result
|
||||
:inclusions
|
||||
(assoc
|
||||
(:inclusions result)
|
||||
kw
|
||||
(apply
|
||||
formatter
|
||||
(list
|
||||
(subs fragment (count token))
|
||||
index))))
|
||||
(rest fragments)
|
||||
(cons kw processed))))
|
||||
|
||||
|
||||
(defn process-text
|
||||
"Process this `text`, assumed to be markdown potentially containing both local links
|
||||
and YAML visualisation specifications, and return a map comprising JSON visualisation
|
||||
specification, and HTML text with markers for where those should be reinserted.
|
||||
|
||||
The map has two top-level keys: `:inclusions`, a map of constructed keywords to
|
||||
inclusion specifications, and `:text`, an HTML text string with the keywords
|
||||
present where the corresponding inclusion should be inserted."
|
||||
([text]
|
||||
(process-text 0 {:inclusions {}} (cs/split text #"```") '()))
|
||||
([index result fragments processed]
|
||||
(let [fragment (first fragments)
|
||||
first-token (get-first-token fragment)
|
||||
formatter (eval ((:formatters config) first-token))]
|
||||
(cond
|
||||
(empty? fragments)
|
||||
(assoc result :text
|
||||
(local-links
|
||||
(md/md-to-html-string
|
||||
(cs/join "\n\n" (reverse processed))
|
||||
:heading-anchors true)))
|
||||
formatter
|
||||
(apply-formatter index result fragments processed fragment first-token formatter)
|
||||
true
|
||||
(process-markdown-fragment index result fragments processed)))))
|
||||
|
||||
|
||||
(defn reintegrate-inclusions
|
||||
"Given a map of the form produced by `process-text`, return a string of HTML text
|
||||
with the inclusions (if any) reintegrated."
|
||||
([processed-text]
|
||||
(reintegrate-inclusions (:inclusions processed-text) (:text processed-text)))
|
||||
([inclusions text]
|
||||
(let [ks (keys inclusions)]
|
||||
(if (empty? (keys inclusions))
|
||||
text
|
||||
(let [kw (first ks)]
|
||||
(reintegrate-inclusions
|
||||
(dissoc inclusions kw)
|
||||
(cs/replace
|
||||
text
|
||||
(str kw)
|
||||
(cs/replace (kw inclusions) "\\/" "/"))))))))
|
||||
|
||||
|
||||
(defn md->html
|
||||
"Take this markdown source, and return HTML."
|
||||
[md-src]
|
||||
(reintegrate-inclusions (process-text md-src)))
|
||||
|
||||
|
||||
|
|
@ -49,9 +49,10 @@
|
|||
log-entry))
|
||||
|
||||
|
||||
(defn find-history [^String git-directory-path ^String file-path]
|
||||
(defn find-history
|
||||
"Return the log entries in the repository at this `git-directory-path`
|
||||
which refer to changes to the file at this `file-path`."
|
||||
[^String git-directory-path ^String file-path]
|
||||
(let [repository (git/load-repo git-directory-path)]
|
||||
(filter
|
||||
#(entry-contains % file-path)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
[noir.io :as io]
|
||||
[ring.util.response :refer [content-type response]]
|
||||
[compojure.response :refer [Renderable]]
|
||||
[environ.core :refer [env]]))
|
||||
[environ.core :refer [env]]
|
||||
[smeagol.util :as util]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
|
|
@ -34,10 +35,6 @@
|
|||
|
||||
(def template-path "templates/")
|
||||
|
||||
;; the relative path to the config file.
|
||||
(def config-file-path (str (io/resource-path) "../config.edn"))
|
||||
|
||||
(def config (read-string (slurp config-file-path)))
|
||||
|
||||
(deftype RenderableTemplate [template params]
|
||||
Renderable
|
||||
|
|
@ -45,7 +42,7 @@
|
|||
(content-type
|
||||
(->> (assoc params
|
||||
(keyword (s/replace template #".html" "-selected")) "active"
|
||||
:config config
|
||||
:config (util/get-messages request)
|
||||
:dev (env :dev)
|
||||
:servlet-context
|
||||
(if-let [context (:servlet-context request)]
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
user (session/get :user)]
|
||||
(layout/render "edit-users.html"
|
||||
(merge (util/standard-params request)
|
||||
{:title (:edit-users-title layout/config)
|
||||
{:title (:edit-users-title (util/get-messages request))
|
||||
:users (auth/list-users)}))))
|
||||
|
||||
(defn delete-user
|
||||
|
|
@ -48,11 +48,11 @@
|
|||
(let [params (keywordize-keys (:params request))
|
||||
target (:target params)
|
||||
deleted (auth/delete-user target)
|
||||
message (if deleted (str (:del-user-success layout/config) " " target "."))
|
||||
error (if (not deleted) (str (:del-user-fail layout/config) " " target "."))]
|
||||
message (if deleted (str (:del-user-success (util/get-messages request)) " " target "."))
|
||||
error (if (not deleted) (str (:del-user-fail (util/get-messages request)) " " target "."))]
|
||||
(layout/render "edit-users.html"
|
||||
(merge (util/standard-params request)
|
||||
{:title (:edit-users-title layout/config)
|
||||
{:title (:edit-users-title (util/get-messages request))
|
||||
:message message
|
||||
:error error
|
||||
:users (auth/list-users)}))))
|
||||
|
|
@ -67,9 +67,9 @@
|
|||
password (if (and pass1 (auth/evaluate-password pass1 (:pass2 params))) pass1)
|
||||
stored (if (:email params)
|
||||
(auth/add-user target password (:email params) (:admin params)))
|
||||
message (if stored (str (:save-user-success layout/config) " " target "."))
|
||||
message (if stored (str (:save-user-success (util/get-messages request)) " " target "."))
|
||||
error (if (and (:email params) (not stored))
|
||||
(str (:save-user-fail layout/config) " " target "."))
|
||||
(str (:save-user-fail (util/get-messages request)) " " target "."))
|
||||
details (auth/fetch-user-details target)]
|
||||
(if message
|
||||
(timbre/info message))
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
(timbre/warn error))
|
||||
(layout/render "edit-user.html"
|
||||
(merge (util/standard-params request)
|
||||
{:title (str (:edit-title-prefix layout/config) " " target)
|
||||
{:title (str (:edit-title-prefix (util/get-messages request)) " " target)
|
||||
:message message
|
||||
:error error
|
||||
:target target
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
smeagol.routes.wiki
|
||||
(:require [clojure.walk :refer :all]
|
||||
[clojure.java.io :as cjio]
|
||||
[clojure.string :as cs]
|
||||
[cemerick.url :refer (url url-encode url-decode)]
|
||||
[compojure.core :refer :all]
|
||||
[clj-jgit.porcelain :as git]
|
||||
|
|
@ -14,8 +15,10 @@
|
|||
[taoensso.timbre :as timbre]
|
||||
[smeagol.authenticate :as auth]
|
||||
[smeagol.diff2html :as d2h]
|
||||
[smeagol.formatting :refer [md->html]]
|
||||
[smeagol.layout :as layout]
|
||||
[smeagol.util :as util]
|
||||
[smeagol.uploads :as ul]
|
||||
[smeagol.history :as hist]
|
||||
[smeagol.routes.admin :as admin]))
|
||||
|
||||
|
|
@ -54,7 +57,7 @@
|
|||
(defn process-source
|
||||
"Process `source-text` and save it to the specified `file-path`, committing it
|
||||
to Git and finally redirecting to wiki-page."
|
||||
[params suffix]
|
||||
[params suffix request]
|
||||
(let [source-text (:src params)
|
||||
page (:page params)
|
||||
file-name (str page suffix)
|
||||
|
|
@ -74,14 +77,14 @@
|
|||
(if
|
||||
(= suffix ".md")
|
||||
(url-encode page)
|
||||
(:default-page-title layout/config))))))
|
||||
(util/get-message :default-page-title request))))))
|
||||
|
||||
|
||||
(defn edit-page
|
||||
"Render a page in a text-area for editing. This could have been done in the same function as wiki-page,
|
||||
and that would have been neat, but I couldn't see how to establish security if that were done."
|
||||
([request]
|
||||
(edit-page request (:default-page-title layout/config) ".md" "edit.html" "/content/_edit-side-bar.md"))
|
||||
(edit-page request (util/get-message :default-page-title request) ".md" "edit.html" "/content/_edit-side-bar.md"))
|
||||
([request default suffix template side-bar]
|
||||
(let [params (keywordize-keys (:params request))
|
||||
src-text (:src params)
|
||||
|
|
@ -92,13 +95,13 @@
|
|||
(if (not exists?)
|
||||
(timbre/info (format "File '%s' not found; creating a new file" file-path))
|
||||
(timbre/info (format "Opening '%s' for editing" file-path)))
|
||||
(cond src-text (process-source params suffix)
|
||||
(cond src-text (process-source params suffix request)
|
||||
true
|
||||
(layout/render template
|
||||
(merge (util/standard-params request)
|
||||
{:title (str (:edit-title-prefix layout/config) " " page)
|
||||
{:title (str (util/get-message :edit-title-prefix request) " " page)
|
||||
:page page
|
||||
:side-bar (util/local-links (util/md->html side-bar))
|
||||
:side-bar (md->html (io/slurp-resource side-bar))
|
||||
:content (if exists? (io/slurp-resource (str "/content/" page suffix)) "")
|
||||
:exists exists?}))))))
|
||||
|
||||
|
|
@ -113,7 +116,7 @@
|
|||
"Render the markdown page specified in this `request`, if any. If none found, redirect to edit-page"
|
||||
[request]
|
||||
(let [params (keywordize-keys (:params request))
|
||||
page (or (:page params) (:default-page-title layout/config))
|
||||
page (or (:page params) (util/get-message :default-page-title request))
|
||||
file-name (str "/content/" page ".md")
|
||||
file-path (str (io/resource-path) file-name)
|
||||
exists? (.exists (clojure.java.io/as-file file-path))]
|
||||
|
|
@ -124,8 +127,7 @@
|
|||
(merge (util/standard-params request)
|
||||
{:title page
|
||||
:page page
|
||||
:side-bar (util/local-links (util/md->html "/content/_side-bar.md"))
|
||||
:content (util/local-links (util/md->html file-name))
|
||||
:content (md->html (io/slurp-resource file-name))
|
||||
:editable true})))
|
||||
true (response/redirect (str "/edit?page=" page)))))
|
||||
|
||||
|
|
@ -135,45 +137,68 @@
|
|||
if any. If none, error?"
|
||||
[request]
|
||||
(let [params (keywordize-keys (:params request))
|
||||
page (url-decode (or (:page params) (:default-page-title layout/config)))
|
||||
page (url-decode (or (:page params) (util/get-message :default-page-title request)))
|
||||
file-name (str page ".md")
|
||||
repo-path (str (io/resource-path) "/content/")]
|
||||
(timbre/info (format "Showing history of page '%s'" page))
|
||||
(layout/render "history.html"
|
||||
(merge (util/standard-params request)
|
||||
{:title (str "History of " page)
|
||||
:page page
|
||||
:history (hist/find-history repo-path file-name)}))))
|
||||
|
||||
(defn upload-page
|
||||
"Render a form to allow the upload of a file."
|
||||
[request]
|
||||
(let [params (keywordize-keys (:params request))
|
||||
data-path (str (io/resource-path) "/uploads/")
|
||||
upload (:upload params)
|
||||
uploaded (if upload (ul/store-upload params))]
|
||||
(layout/render "upload.html"
|
||||
(merge (util/standard-params request)
|
||||
{:title (util/get-message :file-upload-title request)
|
||||
:uploaded uploaded
|
||||
:is-image (and
|
||||
uploaded
|
||||
(or
|
||||
(cs/ends-with? uploaded ".gif")
|
||||
(cs/ends-with? uploaded ".jpg")
|
||||
(cs/ends-with? uploaded ".jpeg")
|
||||
(cs/ends-with? uploaded ".png")
|
||||
(cs/ends-with? uploaded ".GIF")
|
||||
(cs/ends-with? uploaded ".JPG")
|
||||
(cs/ends-with? uploaded ".PNG")))}))))
|
||||
|
||||
|
||||
(defn version-page
|
||||
"Render a specific historical version of a page"
|
||||
[request]
|
||||
(let [params (keywordize-keys (:params request))
|
||||
page (url-decode (or (:page params) (:default-page-title layout/config)))
|
||||
page (url-decode (or (:page params) (util/get-message :default-page-title request)))
|
||||
version (:version params)
|
||||
file-name (str page ".md")
|
||||
repo-path (str (io/resource-path) "/content/")]
|
||||
repo-path (str (io/resource-path) "/content/")
|
||||
content (hist/fetch-version repo-path file-name version)]
|
||||
(timbre/info (format "Showing version '%s' of page '%s'" version page))
|
||||
(layout/render "wiki.html"
|
||||
(merge (util/standard-params request)
|
||||
{:title (str (:vers-col-hdr layout/config) " " version " of " page)
|
||||
{:title (str (util/get-message :vers-col-hdr request) " " version " of " page)
|
||||
:page page
|
||||
:content (util/local-links
|
||||
(md/md-to-html-string
|
||||
(hist/fetch-version
|
||||
repo-path file-name version)))}))))
|
||||
:content (md->html content)}))))
|
||||
|
||||
|
||||
(defn diff-page
|
||||
"Render a diff between two versions of a page"
|
||||
[request]
|
||||
(let [params (keywordize-keys (:params request))
|
||||
page (url-decode (or (:page params) (:default-page-title layout/config)))
|
||||
page (url-decode (or (:page params) (util/get-message :default-page-title request)))
|
||||
version (:version params)
|
||||
file-name (str page ".md")
|
||||
repo-path (str (io/resource-path) "/content/")]
|
||||
(timbre/info (format "Showing diff between version '%s' of page '%s' and current" version page))
|
||||
(layout/render "wiki.html"
|
||||
(merge (util/standard-params request)
|
||||
{:title (str (:diff-title-prefix layout/config)" " version " of " page)
|
||||
{:title (str (util/get-message :diff-title-prefix request)" " version " of " page)
|
||||
:page page
|
||||
:content (d2h/diff2html (hist/diff repo-path file-name version))}))))
|
||||
|
||||
|
|
@ -188,7 +213,7 @@
|
|||
user (session/get :user)
|
||||
redirect-to (or (:redirect-to params) "/wiki")]
|
||||
(cond
|
||||
(= action (:logout-label layout/config))
|
||||
(= action (util/get-message :logout-label request))
|
||||
(do
|
||||
(timbre/info (str "User " user " logging out"))
|
||||
(session/remove! :user)
|
||||
|
|
@ -200,11 +225,8 @@
|
|||
true
|
||||
(layout/render "auth.html"
|
||||
(merge (util/standard-params request)
|
||||
{:title (if user (str (:logout-link layout/config) " " user) (:login-link layout/config))
|
||||
:redirect-to ((:headers request) "referer")
|
||||
:side-bar (util/local-links (util/md->html "/content/_side-bar.md"))
|
||||
:header (util/local-links (util/md->html "/content/_header.md"))
|
||||
:user user})))))
|
||||
{:title (if user (str (util/get-message :logout-link request) " " user) (util/get-message :login-link request))
|
||||
:redirect-to ((:headers request) "referer")})))))
|
||||
|
||||
|
||||
(defn passwd-page
|
||||
|
|
@ -220,16 +242,14 @@
|
|||
(auth/change-pass user oldpass pass2))]
|
||||
(layout/render "passwd.html"
|
||||
(merge (util/standard-params request)
|
||||
{:title (str (:chpass-title-prefix layout/config) " " user)
|
||||
:side-bar (util/local-links (util/md->html "/content/_side-bar.md"))
|
||||
:header (util/local-links (util/md->html "/content/_header.md"))
|
||||
:message (if changed? (:chpass-success layout/config))
|
||||
{:title (str (util/get-message :chpass-title-prefix request) " " user)
|
||||
:message (if changed? (util/get-message :chpass-success request))
|
||||
:error (cond
|
||||
(nil? oldpass) nil
|
||||
changed? nil
|
||||
(< (count pass1) 8) (:chpass-too-short layout/config)
|
||||
(not (= pass1 pass2)) (:chpass-bad-match layout/config)
|
||||
true (:chpass-fail layout/config))}))))
|
||||
(< (count pass1) 8) (util/get-message :chpass-too-short request)
|
||||
(not (= pass1 pass2)) (util/get-message :chpass-bad-match request)
|
||||
true (util/get-message :chpass-fail request))}))))
|
||||
|
||||
|
||||
(defroutes wiki-routes
|
||||
|
|
@ -250,4 +270,5 @@
|
|||
(POST "/auth" request (auth-page request))
|
||||
(GET "/passwd" request (passwd-page request))
|
||||
(POST "/passwd" request (passwd-page request))
|
||||
)
|
||||
(GET "/upload" request (route/restricted (upload-page request)))
|
||||
(POST "/upload" request (route/restricted (upload-page request))))
|
||||
|
|
|
|||
64
src/smeagol/uploads.clj
Normal file
64
src/smeagol/uploads.clj
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
(ns ^{:doc "Handle file uploads."
|
||||
:author "Simon Brooke"}
|
||||
smeagol.uploads
|
||||
(:import [java.io File])
|
||||
(:require [clojure.string :as cs]
|
||||
[noir.io :as io]
|
||||
[taoensso.timbre :as timbre]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; Smeagol: a very simple Wiki engine.
|
||||
;;;;
|
||||
;;;; This program is free software; you can redistribute it and/or
|
||||
;;;; modify it under the terms of the GNU General Public License
|
||||
;;;; as published by the Free Software Foundation; either version 2
|
||||
;;;; of the License, or (at your option) any later version.
|
||||
;;;;
|
||||
;;;; This program is distributed in the hope that it will be useful,
|
||||
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;;; GNU General Public License for more details.
|
||||
;;;;
|
||||
;;;; You should have received a copy of the GNU General Public License
|
||||
;;;; along with this program; if not, write to the Free Software
|
||||
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
;;;; USA.
|
||||
;;;;
|
||||
;;;; Copyright (C) 2017 Simon Brooke
|
||||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn avoid-name-collisions
|
||||
"Find a filename within this `path`, based on this `file-name`, that does not
|
||||
reference an existing file. It is assumed that `path` ends with a path separator.
|
||||
Returns a filename hwich does not currently reference a file within the path."
|
||||
[path file-name]
|
||||
(if (.exists (File. (str path file-name)))
|
||||
(let [parts (cs/split file-name #"\.")
|
||||
prefix (cs/join "." (butlast parts))
|
||||
suffix (last parts)]
|
||||
(first
|
||||
(filter #(not (.exists (File. (str path %))))
|
||||
(map #(str prefix "." % "." suffix) (range)))))
|
||||
file-name))
|
||||
|
||||
|
||||
(defn store-upload
|
||||
"Store an upload both to the file system and to the database.
|
||||
The issue with storing an upload is moving it into place.
|
||||
If `params` are passed as a map, it is expected that this is a map from
|
||||
an HTTP POST operation of a form with type `multipart/form-data`."
|
||||
[params]
|
||||
(let [upload (:upload params)
|
||||
tmp-file (:tempfile upload)
|
||||
path (str (io/resource-path) "uploads/")
|
||||
filename (avoid-name-collisions path (:filename upload))]
|
||||
(timbre/info
|
||||
(str "Storing upload file: " upload))
|
||||
(if tmp-file
|
||||
(do
|
||||
(.renameTo tmp-file
|
||||
(File. (str path filename)))
|
||||
filename)
|
||||
(throw (Exception. "No file found?")))))
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
(ns ^{:doc "Miscellaneous utility functions supporting Smeagol."
|
||||
:author "Simon Brooke"}
|
||||
smeagol.util
|
||||
(:require [clojure.string :as cs]
|
||||
[cemerick.url :refer (url url-encode url-decode)]
|
||||
(:require [noir.session :as session]
|
||||
[noir.io :as io]
|
||||
[noir.session :as session]
|
||||
[markdown.core :as md]
|
||||
[smeagol.authenticate :as auth]))
|
||||
[scot.weft.i18n.core :as i18n]
|
||||
[taoensso.timbre :as timbre]
|
||||
[smeagol.authenticate :as auth]
|
||||
[smeagol.configuration :refer [config]]
|
||||
[smeagol.formatting :refer [md->html]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
|
|
@ -31,30 +32,6 @@
|
|||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn md->html
|
||||
"reads a markdown file from public/md and returns an HTML string"
|
||||
[filename]
|
||||
(md/md-to-html-string (io/slurp-resource filename)))
|
||||
|
||||
|
||||
;; Error to show if text to be rendered is nil.
|
||||
(def no-text-error "No text: does the file exist?")
|
||||
|
||||
|
||||
(defn local-links
|
||||
"Rewrite text in `html-src` surrounded by double square brackets as a local link into this wiki."
|
||||
[^String html-src]
|
||||
(if html-src
|
||||
(cs/replace html-src #"\[\[[^\[\]]*\]\]"
|
||||
#(let [text (clojure.string/replace %1 #"[\[\]]" "")
|
||||
encoded (url-encode text)
|
||||
;; I use '\_' to represent '_' in wiki markup, because
|
||||
;; '_' is meaningful in Markdown. However, this needs to
|
||||
;; be stripped out when interpreting local links.
|
||||
munged (cs/replace encoded #"%26%2395%3B" "_")]
|
||||
(format "<a href='wiki?page=%s'>%s</a>" munged text)))
|
||||
no-text-error))
|
||||
|
||||
|
||||
(defn standard-params
|
||||
"Return a map of standard parameters to pass to the template renderer."
|
||||
|
|
@ -62,7 +39,31 @@
|
|||
(let [user (session/get :user)]
|
||||
{:user user
|
||||
:admin (auth/get-admin user)
|
||||
:side-bar (local-links (md->html "/content/_side-bar.md"))
|
||||
:header (local-links (md->html "/content/_header.md"))
|
||||
:side-bar (md->html (io/slurp-resource "/content/_side-bar.md"))
|
||||
:header (md->html (io/slurp-resource "/content/_header.md"))
|
||||
:version (System/getProperty "smeagol.version")}))
|
||||
|
||||
|
||||
(defn raw-get-messages
|
||||
"Return the most acceptable messages collection we have given the
|
||||
`Accept-Language` header in this `request`."
|
||||
[request]
|
||||
(merge
|
||||
(i18n/get-messages
|
||||
((:headers request) "accept-language")
|
||||
(str (io/resource-path) "../i18n")
|
||||
"en-GB")
|
||||
config))
|
||||
|
||||
|
||||
(def get-messages (memoize raw-get-messages))
|
||||
|
||||
|
||||
(defn get-message
|
||||
[message-key request]
|
||||
(let [messages (get-messages request)]
|
||||
(if
|
||||
(map? messages)
|
||||
(or (messages message-key) message-key)
|
||||
message-key)))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
(ns smeagol.test.util
|
||||
(:use clojure.test
|
||||
ring.mock.request
|
||||
smeagol.util))
|
||||
smeagol.formatting))
|
||||
|
||||
(deftest test-local-links
|
||||
(testing "Rewriting of local links"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue