mirror of
https://github.com/journeyman-cc/smeagol.git
synced 2026-04-12 18:05:06 +00:00
Mainly documentation, but some reorganisation to support documentation.
This commit is contained in:
parent
256cf702cf
commit
b2ee34bd8e
17 changed files with 265 additions and 309 deletions
184
README.md
184
README.md
|
|
@ -1,187 +1,39 @@
|
|||

|
||||
|
||||
# Welcome to Smeagol!
|
||||
Smeagol is a simple Wiki engine inspired by [Gollum](https://github.com/gollum/gollum/wiki). Gollum is a Wiki engine written in Ruby, which uses a number of simple text formats including [Markdown](http://daringfireball.net/projects/markdown/), and which uses [Git](http://git-scm.com/) to provide versioning and backup. I needed a new Wiki for a project and thought Gollum would be ideal - but unfortunately it doesn't provide user authentication, which I needed, and it was simpler for me to reimplement the bits I did need in Clojure than to modify Gollum.
|
||||
Smeagol is a hackable, extensible Wiki engine which is reasonably user-friendly. It uses [Markdown](http://daringfireball.net/projects/markdown/) as its text format, and [Git](http://git-scm.com/) to provide versioning and backup.
|
||||
|
||||
So at this stage Smeagol is a Wiki engine written in Clojure which uses Markdown as its text format, which does have user authentication, and which uses Git as its versioning and backup system.
|
||||
|
||||
<a href="https://zenhub.com"><img src="https://raw.githubusercontent.com/ZenHubIO/support/master/zenhub-badge.png"></a>
|
||||
|
||||
## Status
|
||||
Smeagol is now a fully working small Wiki engine, and meets my own immediate needs.
|
||||
## Using Smeagol
|
||||
Read the [[User Documentation]] for an introduction to all Smeagol's features.
|
||||
|
||||
## 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.
|
||||
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. The markdown format is extensible, and has extensions already for inclusions, for data visualisations and for picture galleries. Read more about [[Extensible Markup]].
|
||||
|
||||
## 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:
|
||||
Smeagol now has good security and authentication. While the initial password supplied with the system is not encrypted, when it is changed it will be; and passwords for new users added through the user administration pages are encrypted. Read more about [[Security and authentication]].
|
||||
|
||||
{:admin {:password "admin" :email "admin@localhost" :admin true}
|
||||
:adam {:password "secret" :email "adam@localhost"}}
|
||||
|
||||
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.
|
||||
## Internationalisation
|
||||
Smeagol has built in internationalisation. Currently it has translation files for English, German, Lithuanian and Russian. We'd welcome volunteers to translate it into other languages.
|
||||
|
||||
## Images
|
||||
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:
|
||||
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 to other images already available on the web, like this:
|
||||
|
||||

|
||||
|
||||
## Includes
|
||||
You can include pages into the current page you're working on. To do so, you can add a include link:
|
||||
## Running Smeagol
|
||||
You can run Smeagol from the [[Docker Image]]; alternatively you can run it from an executable jar file or as a war file in a servlet container. Read how about [[Configuring Smeagol]] and [[Deploying Smeagol]].
|
||||
|
||||
`&[:indent-heading s/Num :indent-list s/Num](relative or absolute uri s/Str)`
|
||||
|
||||
Parameters semantics:
|
||||
* uri: The page to include. At the moment only pages from the current wiki are allowed. A page called "PageToBeIncluded" will result in a uri "PageToBeIncluded.md".
|
||||
* indent-heading: You can indent headings of included page to adjust the included content to your surrounding structure. Indents 0-9 are supported.
|
||||
* indent-list: In Same manner you can indent lists of included page to adjust the included content to your surrounding structure. Indents 0-9 are supported.
|
||||
|
||||
Security warning: At the moment there is no check against directory traversal attack. So include feature may expose files outside of your wiki content-dir.
|
||||
|
||||
## Prerequisites
|
||||
You will need [Leiningen](https://github.com/technomancy/leiningen) 2.0 or above installed.
|
||||
|
||||
You will need [node](https://nodejs.org/en/) and [bower](https://bower.io/) installed.
|
||||
|
||||
## Development
|
||||
To start a development web server for the application, run:
|
||||
|
||||
lein bower install
|
||||
lein repl
|
||||
|
||||
And then, when the repl starts up,
|
||||
|
||||
(start-server)
|
||||
|
||||
## Running
|
||||
|
||||
To build a standalone Smeagol jar file, run:
|
||||
|
||||
lein bower install
|
||||
lein ring uberjar
|
||||
|
||||
**HOWEVER**, this will not run without [configuration](https://github.com/journeyman-cc/smeagol/blob/develop/resources/public/content/Environment%20Variables.md).
|
||||
|
||||
Alternatively, if you want to deploy to a servlet container (which I would strongly recommend), the simplest thing is to run:
|
||||
|
||||
lein bower install
|
||||
lein ring uberwar
|
||||
|
||||
(a command which I'm sure Smeagol would entirely appreciate) and deploy the resulting war file.
|
||||
|
||||
## Experimental Docker image
|
||||
|
||||
You can now run Smeagol as a [Docker](http://www.docker.com) image. To run my Docker image, use
|
||||
|
||||
docker run simonbrooke/smeagol
|
||||
|
||||
Smeagol will run, obviously, on the IP address of your Docker image, on port 8080. To find the IP address, start the image using the command above and then use
|
||||
|
||||
docker inspect --format '{{ .NetworkSettings.IPAddress }}' $(docker ps -q)
|
||||
|
||||
Suppose this prints '10.10.10.10', then the URL to browse to will be http://10.10.10.10:8080/smeagol/
|
||||
|
||||
This image is _experimental_, but it does seem to work fairly well. What it does **not** yet do, however, is push the git repository to a remote location, so when you tear the Docker image down your edits will be lost. My next objective for this image is for it to have a cammand line parameter being the git address of a repository from which it can initialise the Wiki content, and to which it will periodically push local changes to the Wiki content.
|
||||
|
||||
To build your own Docker image, run:
|
||||
|
||||
lein clean
|
||||
lein bower install
|
||||
lein ring uberwar
|
||||
lein docker build
|
||||
|
||||
This will build a new Docker image locally; you can, obviously, push it to your own Docker repository if you wish.
|
||||
|
||||
## Advertisement
|
||||
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
|
||||
|
||||
resources/templates/base.html
|
||||
|
||||
and replace the line
|
||||
|
||||
<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>
|
||||
|
||||
with the line
|
||||
|
||||
<img height="16" width="16" alt="The Web Engineering Factory & Toolworks" src="img/weft.logo.64.png"> Developed by <a href="http://www.weft.scot/">WEFT</a>
|
||||
## Developing Smeagol
|
||||
Smeagol is an open source project; you're entitled to make changes yourself. Read more about [[Developing Smeagol]].
|
||||
|
||||
## License
|
||||
Copyright © 2014-2020 Simon Brooke. Licensed under the GNU General Public License,
|
||||
version 2.0 or (at your option) any later version. If you wish to incorporate
|
||||
parts of Smeagol into another open source project which uses a less restrictive
|
||||
license, please contact me; I'm open to dual licensing it.
|
||||
|
||||
## Phoning home
|
||||
Smeagol does currently fetch one image from my home site. Read more about [[Phoning Home]], and how to prevent it (if you want to).
|
||||
|
||||
## Advertisement
|
||||
If you like what you see here, I am available for work on open source Clojure projects.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
# 1.0.4
|
||||
|
||||
* New gallery extension `pswp` using [Photoswipe](https://photoswipe.com/);
|
||||
* Automatic thumbnailing of uploaded images;
|
||||
* Javascript supporting extensions is only loaded for those pages on which the extensions are actually used;
|
||||
* Many minor bug fixes.
|
||||
|
||||
# 1.0.2
|
||||
Two fixes contributed by a user:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,102 +1,112 @@
|
|||
Smeagol's core configuration comes from a configuration file, `config.edn`, which may be overridden by [[Environment Variables]]. The default file is at `resources/config.edn`; this default can be overridden by providing an environment variable, `SMEAGOL_CONFIG`, whose value is the full or relative pathname of a suitable file.
|
||||
|
||||
|
||||
The default configuration file is as follows:
|
||||
|
||||
```
|
||||
|
||||
{
|
||||
|
||||
:content-dir "resources/public/content"
|
||||
|
||||
;; where content is served from.
|
||||
|
||||
:default-locale "en-GB" ;; default language used for messages
|
||||
|
||||
:extensions-from :local ;; where to load JavaScript libraries
|
||||
;; from: options are :local, :remote.
|
||||
:formatters ;; formatters for processing markdown
|
||||
|
||||
;; extensions.
|
||||
|
||||
{"vega" smeagol.formatting/process-vega
|
||||
|
||||
"vis" smeagol.formatting/process-vega
|
||||
|
||||
"mermaid" smeagol.extensions.mermaid/process-mermaid
|
||||
|
||||
"backticks" smeagol.formatting/process-backticks
|
||||
|
||||
"pswp" smeagol.formatting/process-photoswipe}
|
||||
{:backticks {:formatter "smeagol.formatting/process-backticks"
|
||||
:scripts {}
|
||||
:styles {}}
|
||||
:mermaid {:formatter "smeagol.extensions.mermaid/process-mermaid"
|
||||
:scripts {:core {:local "vendor/node_modules/mermaid/dist/mermaid.min.js"
|
||||
:remote "https://cdnjs.cloudflare.com/ajax/libs/mermaid/8.4.6/mermaid.min.js"}}}
|
||||
:pswp {:formatter "smeagol.extensions.photoswipe/process-photoswipe"
|
||||
:scripts {:core {:local "vendor/node_modules/photoswipe/dist/photoswipe.min.js"
|
||||
:remote "https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.js"}
|
||||
:ui {:local "vendor/node_modules/photoswipe/dist/photoswipe-ui-default.min.js"
|
||||
:remote "https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe-ui-default.min.js"}}
|
||||
:styles {:core {:local "vendor/node_modules/photoswipe/dist/photoswipe.css"}
|
||||
:skin {:local "vendor/node_modules/photoswipe/dist/default-skin/default-skin.css"}}}
|
||||
:test {:formatter "smeagol.extensions.test/process-test" }
|
||||
:vega {:formatter "smeagol.extensions.vega/process-vega"
|
||||
:scripts {:core {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega/5.9.1/vega.min.js"}
|
||||
:lite {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega-lite/4.1.1/vega-lite.min.js"}
|
||||
:embed {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega-embed/6.2.2/vega-embed.min.js"}}}
|
||||
:vis {:formatter "smeagol.extensions.vega/process-vega"
|
||||
:scripts {:core {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega/5.9.1/vega.min.js"}
|
||||
:lite {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega-lite/4.1.1/vega-lite.min.js"}
|
||||
:embed {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega-embed/6.2.2/vega-embed.min.js"}}}}
|
||||
|
||||
:log-level :info ;; the minimum logging level; one of
|
||||
|
||||
;; :trace :debug :info :warn :error :fatal
|
||||
|
||||
:js-from :cdnjs ;; where to load JavaScript libraries
|
||||
|
||||
;; from: options are :local, :cdnjs
|
||||
|
||||
:passwd "resources/passwd"
|
||||
|
||||
;; where the password file is stored
|
||||
|
||||
:site-title "Smeagol" ;; overall title of the site, used in
|
||||
|
||||
;; page headings
|
||||
|
||||
:start-page "Introduction" ;; the page shown to a visitor to the
|
||||
|
||||
;; root URL.
|
||||
|
||||
:thumbnails {:small 64 ;; maximum dimension of thumbnails
|
||||
|
||||
;; stored in the /small directory
|
||||
|
||||
:med 400 ;; maximum dimension of thumbnails
|
||||
|
||||
;; stored in the /med directory
|
||||
|
||||
}}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## content-dir
|
||||
## :content-dir
|
||||
|
||||
The value of `content-dir` should be the full or relative path to the content to be served: the Markdown files, and the upload directories. Full paths are advised, where possible. The directory must be readable and writable by the process running Smeagol. The default is `resources/public/conten`
|
||||
|
||||
|
||||
The value from the configuration file may be overridden with the value of the environment variable `SMEAGOL_CONTENT_DIR`.
|
||||
|
||||
|
||||
## default-locale
|
||||
## :default-locale
|
||||
|
||||
The locale which you expect the majority of your visitors will use. Content negotiation will be done of course, and the best internationalisation file available will be used, but this sets a default for users who do not have any acceptable locale known to us. The default value is `en-GB`.
|
||||
|
||||
This parameter may be overridden with the environment variable `SMEAGOL_DEFAULT_LOCALE`.
|
||||
|
||||
This parameter may be overridden with the environment variable `SMEAGOL-DEFAULT-LOCALE`.
|
||||
## :extensions-from
|
||||
|
||||
Where javascript support for extensions should be loaded from. Valid values are `:local` and `:remote`; if `:remote` is chosen, they will generally be loaded from [CDNJS](https://cdnjs.com/).
|
||||
|
||||
## formatters
|
||||
For an intranet site with limited outside bandwidth, or if you are particularly concerned about security, choose `:local`; otherwise, `:remote` will result in faster loading of your pages.
|
||||
|
||||
Specifications for formatters for markup extensions. The exact data stored will change before Smeagol 1.1.0. TODO: update this.
|
||||
This parameter may be overridden with the environment variable `SMEAGOL_JS_FROM`.
|
||||
|
||||
## :formatters
|
||||
|
||||
## log-level
|
||||
Specifications for formatters for markup extensions.
|
||||
|
||||
For each extension, a map is stored with a key `:formatter`, whose value is the fully qualified name of the Clojure function providing the extension, `:scripts` and `:styles`, each of which hava one additional key for each JavaScript (in the case of `:scripts`) or CSS (in the case of `:styles`) file required by the plugin. Each of these per-file keys points to a final further map, with keys `:local` and `:remote`, whose values are URLs - relative, in the case of the `:local` key, absolute in the case of the `:remote`, pointing to where the required resource can be fetched from.
|
||||
|
||||
This parameter may be overridden with the environment variable `SMEAGOL_FORMATTERS`, but you'd be pretty unwise to do so unless to disable formatters altogether. Otherwise, editing the `config.edn` file would be far more convenient.
|
||||
|
||||
## :log-level
|
||||
|
||||
The level at which logging should operate. Each setting implies all of the settings more severe than itself so
|
||||
|
||||
|
||||
1. setting `:debug` will log all of `debug, info, warn, error` and| `fatal` messages;
|
||||
|
||||
2. setting `:info` will log all of `info, warn, error` and| `fatal` messages;
|
||||
|
||||
|
||||
and so on, so that setting `:fatal` will show only messages which report reasons for Smeagol to fail.
|
||||
|
||||
|
||||
The default setting is `:info`.
|
||||
|
||||
This parameter may be overridden with the environment variable `SMEAGOL_LOG_LEVEL`.
|
||||
|
||||
This parameter may be overridden with the environment variable `SMEAGOL-LOG-LEVEL`.
|
||||
## :passwd
|
||||
|
||||
## TODO: Complete this doumentation!
|
||||
The value of this key should be the path to the password file used to authenticate users. It should **NOT** be in the content directory! For most deployments it should be a file elsewhere in the file system, but it must be readable and writable by the account which runs the process serving Smeagol.
|
||||
|
||||
This parameter may be overridden with the environment variable `SMEAGOL_PASSWD`.
|
||||
|
||||
## :site-title
|
||||
|
||||
The value of this key should be the overall title of the site, which is used in page headings.
|
||||
|
||||
This parameter may be overridden with the environment variable `SMEAGOL_SITE_TITLE`.
|
||||
|
||||
## :start-page
|
||||
|
||||
The value of this key should be the name (without the `.md` extension) of the page to show when a user visits the base URL of your Smeagol installation.
|
||||
|
||||
## :thumbnails
|
||||
|
||||
The value of this key should be a map. Keys in this map should have values which are integers. By default, the key `:small` is bound to `64` and the key `:med` to 400. When an image file is uploaded, it is stored at the resolution you uploaded; but for every key in the `:thumbnails` map whose value is larger than the larger dimension of the uploaded file, scaled copies will also be stored in those sizes.
|
||||
|
|
|
|||
|
|
@ -2,23 +2,45 @@
|
|||
|
||||
You will need [Leiningen](https://github.com/technomancy/leiningen) 2.0 or above installed.
|
||||
|
||||
You will need [node](https://nodejs.org/en/) and [bower](https://bower.io/) installed.
|
||||
You will need [node](https://nodejs.org/en/) installed.
|
||||
|
||||
## Running in development
|
||||
To start a web server for the application during development, run:
|
||||
|
||||
lein bower install
|
||||
```
|
||||
lein npm install
|
||||
lein ring server
|
||||
```
|
||||
|
||||
This should start a development server, and open a new window or tab in your default browser with the default page of the wiki loaded into it.
|
||||
|
||||
## Editing
|
||||
I generally use [LightTable](http://lighttable.com/) as my `Clojure` editor, but it doesn't really matter what you use; if you run Smeagol as described above, then all changes you make in the code (and save) will instantly be applied to the running system. This makes for a productive development environment.
|
||||
|
||||
## Building for deployment
|
||||
|
||||
*Important:* Always run `lein clean` before building a deployment build. Once you have deployed your deployment artifact, run `lein clean` again before continuing development.
|
||||
|
||||
### To build a standalone executable jar file
|
||||
|
||||
run:
|
||||
|
||||
```
|
||||
lein ring uberjar
|
||||
```
|
||||
|
||||
The resulting file may be run by invoking
|
||||
|
||||
```
|
||||
java -jar \[path to uberjar file\]
|
||||
```
|
||||
|
||||
## Documentation
|
||||
It is my intention that the code should be sufficiently well documented to be easy to understand. Documentation may be generated from the code by running
|
||||
|
||||
```
|
||||
lein codox
|
||||
```
|
||||
|
||||
## Contributing
|
||||
If you make changes to Smeagol which you think are useful, please contribute them in the form of a [pull request on github](https://help.github.com/articles/creating-a-pull-request/).
|
||||
|
|
|
|||
|
|
@ -1,18 +1,13 @@
|
|||

|
||||
|
||||
# Welcome to Smeagol!
|
||||
Smeagol is a simple Wiki engine inspired by [Gollum](https://github.com/gollum/gollum/wiki). Gollum is a Wiki engine written in Ruby, which uses a number of simple text formats including [Markdown](http://daringfireball.net/projects/markdown/), and which uses [Git](http://git-scm.com/) to provide versioning and backup. I needed a new Wiki for a project and thought Gollum would be ideal - but unfortunately it doesn't provide user authentication, which I needed, and it was simpler for me to reimplement the bits I did need in Clojure than to modify Gollum.
|
||||
|
||||
So at this stage Smeagol is a Wiki engine written in Clojure which uses Markdown as its text format, which does have user authentication, and which uses Git as its versioning and backup system.
|
||||
|
||||
## Status
|
||||
Smeagol is now a fully working small Wiki engine, and meets my own immediate needs.
|
||||
Smeagol is a hackable, extensible Wiki engine which is reasonably user-friendly. It uses [Markdown](http://daringfireball.net/projects/markdown/) as its text format, and [Git](http://git-scm.com/) to provide versioning and backup.
|
||||
|
||||
## Using Smeagol
|
||||
Read the [[User Documentation]] for an introduction to all Smeagol's features.
|
||||
|
||||
## 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. Read more about [[Extensible Markup]].
|
||||
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. The markdown format is extensible, and has extensions already for inclusions, for data visualisations and for picture galleries. Read more about [[Extensible Markup]].
|
||||
|
||||
## Security and authentication
|
||||
Smeagol now has good security and authentication. While the initial password supplied with the system is not encrypted, when it is changed it will be; and passwords for new users added through the user administration pages are encrypted. Read more about [[Security and authentication]].
|
||||
|
|
@ -26,13 +21,13 @@ You can (if you're logged in) upload files, including images, using the **Upload
|
|||

|
||||
|
||||
## Running Smeagol
|
||||
You can run Smeagol from the [[Docker Image]]; alternatively you can run it from an executable jar file or as a war file in a servlet container. Read how in [[Deploying Smeagol]].
|
||||
You can run Smeagol from the [[Docker Image]]; alternatively you can run it from an executable jar file or as a war file in a servlet container. Read how about [[Configuring Smeagol]] and [[Deploying Smeagol]].
|
||||
|
||||
## Developing Smeagol
|
||||
Smeagol is an open source project; you're entitled to make changes yourself. Read more about [[Developing Smeagol]].
|
||||
|
||||
## License
|
||||
Copyright © 2014-2017 Simon Brooke. Licensed under the GNU General Public License,
|
||||
Copyright © 2014-2020 Simon Brooke. Licensed under the GNU General Public License,
|
||||
version 2.0 or (at your option) any later version. If you wish to incorporate
|
||||
parts of Smeagol into another open source project which uses a less restrictive
|
||||
license, please contact me; I'm open to dual licensing it.
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@
|
|||
|
||||
;; the relative path to the password file.
|
||||
(def password-file-path
|
||||
"Path to the password file."
|
||||
;; TODO: portability; elegance. Not very happy with this.
|
||||
(or
|
||||
(:passwd config)
|
||||
(str (io/resource-path) "../passwd")))
|
||||
|
|
|
|||
|
|
@ -100,17 +100,17 @@
|
|||
'( {:from :smeagol-content-dir :to :content-dir}
|
||||
{:from :smeagol-default-locale :to :default-locale}
|
||||
{:from :smeagol-formatters :to :formatters :transform read-string}
|
||||
{:from :smeagol-js-from :to :js-from :transform to-keyword}
|
||||
{:from :smeagol-js-from :to :extensions-from :transform to-keyword}
|
||||
{:from :smeagol-log-level :to :log-level :transform to-keyword}
|
||||
{:from :smeagol-passwd :to :passwd}
|
||||
{:from :smeagol-site-title :to :site-title}))
|
||||
|
||||
|
||||
(defn build-config
|
||||
[]
|
||||
(defn- build-config
|
||||
"The actual configuration, as a map. The idea here is that the config
|
||||
file is read (if it is specified and present), but that individual
|
||||
values can be overridden by environment variables."
|
||||
[]
|
||||
(try
|
||||
(log/info (str "Reading configuration from " config-file-path))
|
||||
(let [file-contents (try
|
||||
|
|
@ -146,4 +146,6 @@
|
|||
(log/error any "Could not load configuration")
|
||||
{})))
|
||||
|
||||
(def config (build-config))
|
||||
(def config
|
||||
"The actual configuration, as a map."
|
||||
(memoize build-config))
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
[noir.io :as io]
|
||||
[smeagol.configuration :refer [config]]
|
||||
[smeagol.extensions.utils :refer :all]
|
||||
[smeagol.util :refer [content-dir upload-dir]]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
@ -75,7 +76,7 @@
|
|||
src := #'[^)]*' ;
|
||||
SPACE := #'[\\r\\n\\W]*'"))
|
||||
|
||||
(defn simplify
|
||||
(defn- simplify
|
||||
[tree]
|
||||
(if
|
||||
(coll? tree)
|
||||
|
|
@ -88,16 +89,6 @@
|
|||
:END-SRC nil
|
||||
(remove empty? (map simplify tree)))))
|
||||
|
||||
(defn uploaded?
|
||||
"Does this `url` string appear to be one that has been uploaded to our
|
||||
`uploads` directory?"
|
||||
[url]
|
||||
(and
|
||||
(cs/starts-with? (str url) "content/uploads")
|
||||
(fs/exists? (cio/file upload-dir (fs/base-name url)))))
|
||||
|
||||
;; (uploaded? "content/uploads/g1.jpg")
|
||||
|
||||
(defn slide-merge-dimensions
|
||||
"If this `slide` appears to be local, return it decorated with the
|
||||
dimensions of the image it references."
|
||||
|
|
@ -118,7 +109,7 @@
|
|||
;; {:title "Frost on a gate, Laurieston",
|
||||
;; :src "content/uploads/g1.jpg"})
|
||||
|
||||
(defn process-simple-slide
|
||||
(defn- process-simple-slide
|
||||
[slide-spec]
|
||||
(let [s (simplify (simple-grammar slide-spec))
|
||||
s'(zipmap (map first s) (map #(nth % 1) s))
|
||||
|
|
@ -174,6 +165,8 @@
|
|||
;; 1)
|
||||
|
||||
(defn process-photoswipe
|
||||
"Process a Photoswipe specification which may conform either to the
|
||||
`full` or the `simple` syntax."
|
||||
[^String url-or-pswp-spec ^Integer index]
|
||||
(let [data (resource-url-or-data->data url-or-pswp-spec)
|
||||
spec (cs/trim (:data data))]
|
||||
|
|
|
|||
|
|
@ -2,12 +2,15 @@
|
|||
:author "Simon Brooke"}
|
||||
smeagol.extensions.utils
|
||||
(:require [cemerick.url :refer (url url-encode url-decode)]
|
||||
[clj-yaml.core :as yaml]
|
||||
[clojure.data.json :as json]
|
||||
[clojure.java.io :as cjio]
|
||||
[clojure.string :as cs]
|
||||
[me.raynes.fs :as fs]
|
||||
[noir.io :as io]
|
||||
[smeagol.configuration :refer [config]]
|
||||
[taoensso.timbre :as log]))
|
||||
[taoensso.timbre :as log]
|
||||
[smeagol.util :refer [content-dir upload-dir]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
|
|
@ -32,16 +35,6 @@
|
|||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def content-dir
|
||||
(str
|
||||
(fs/absolute
|
||||
(or
|
||||
(:content-dir config)
|
||||
(cjio/file (io/resource-path) "content")))))
|
||||
|
||||
(def upload-dir
|
||||
(str (cjio/file content-dir "uploads")))
|
||||
|
||||
(def resource-url-or-data->data
|
||||
"Interpret this `resource-url-or-data` string as data to be digested by a
|
||||
`process-extension` function. It may be a URL or the pathname of a local
|
||||
|
|
@ -83,3 +76,19 @@
|
|||
(.getName (.getClass x))
|
||||
(.getMessage x) )
|
||||
default))))))
|
||||
|
||||
(defn uploaded?
|
||||
"Does this `url` string appear to be one that has been uploaded to our
|
||||
`uploads` directory?"
|
||||
[url]
|
||||
(and
|
||||
(cs/starts-with? (str url) "content/uploads")
|
||||
(fs/exists? (cjio/file upload-dir (fs/base-name url)))))
|
||||
|
||||
;; (uploaded? "content/uploads/g1.jpg")
|
||||
|
||||
(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)))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
(ns ^{:doc "Format vega/vis extensions to Semagol's extended markdown format."
|
||||
:author "Simon Brooke"}
|
||||
smeagol.extensions.vega
|
||||
(:require [clojure.data.json :as json]
|
||||
[clj-yaml.core :as yaml]
|
||||
[smeagol.extensions.utils :refer :all]
|
||||
(:require [smeagol.extensions.utils :refer :all]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
@ -62,11 +60,6 @@
|
|||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(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)))
|
||||
|
||||
(defn process-vega
|
||||
"If this `src-resource-or-url` is a valid URL, it is assumed to point to a
|
||||
plain text file pointing to valid `vega-src`; otherwise, it is expected to
|
||||
|
|
|
|||
|
|
@ -48,15 +48,19 @@
|
|||
(declare process-text)
|
||||
|
||||
|
||||
(defn process-backticks
|
||||
(defn- process-backticks
|
||||
"Effectively, escape the backticks surrounding this `text`, by protecting them
|
||||
from the `process-text` filter."
|
||||
from the `process-text` filter.
|
||||
|
||||
**NOTE** that it is not expected that this function forms part of a stable
|
||||
API."
|
||||
[^String text ^Integer index]
|
||||
(str "<pre class=\"backticks\">```" (.trim text) "\n```</pre>"))
|
||||
|
||||
|
||||
(defn get-first-token
|
||||
"Return the first space-separated token of this `string`."
|
||||
"Return the first space-separated token of the first line of this `string`,
|
||||
or `nil` if there is none."
|
||||
[^String string]
|
||||
(try
|
||||
(if string (first (cs/split (first (cs/split-lines string)) #"[^a-zA-Z0-9]+")))
|
||||
|
|
@ -69,7 +73,10 @@
|
|||
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."
|
||||
corresponding inclusion should be inserted.
|
||||
|
||||
**NOTE** that it is not expected that this function forms part of a stable
|
||||
API."
|
||||
[^Integer index ^clojure.lang.Associative result ^String fragment fragments processed]
|
||||
(process-text
|
||||
(inc index)
|
||||
|
|
@ -78,8 +85,9 @@
|
|||
(cons fragment processed)))
|
||||
|
||||
|
||||
(defn deep-merge [v & vs]
|
||||
"Cripped in its entirety from https://clojuredocs.org/clojure.core/merge."
|
||||
(defn deep-merge
|
||||
"Cripped in its entirety from [here](https://clojuredocs.org/clojure.core/merge)."
|
||||
[v & vs]
|
||||
(letfn [(rec-merge [v1 v2]
|
||||
(if (and (map? v1) (map? v2))
|
||||
(merge-with deep-merge v1 v2)
|
||||
|
|
@ -89,14 +97,23 @@
|
|||
(last vs))))
|
||||
|
||||
|
||||
(defn apply-formatter
|
||||
(defn- apply-formatter
|
||||
"Within the context of `process-text`, process a fragment for which an explicit
|
||||
`formatter` has been identified.
|
||||
`formatter` has been identified, and then recurse back into `process-text` to
|
||||
process the remainder of the fragments. Arguments are as for `process-text`, q.v.,
|
||||
the addition of
|
||||
|
||||
* `fragment` the current fragment to be processed;
|
||||
* `token` the identifier of the extension processor to be applied;
|
||||
* `formatter` the actual extension processor to be applied.
|
||||
|
||||
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."
|
||||
corresponding inclusion should be inserted.
|
||||
|
||||
**NOTE** that it is not expected that this function forms part of a stable
|
||||
API."
|
||||
[^Integer index
|
||||
^clojure.lang.Associative result
|
||||
fragments
|
||||
|
|
@ -117,9 +134,12 @@
|
|||
(cons inky processed))))
|
||||
|
||||
|
||||
(defn reassemble-text
|
||||
(defn- reassemble-text
|
||||
"Reassemble these processed strings into a complete text, and process it as
|
||||
Markdown."
|
||||
Markdown.
|
||||
|
||||
**NOTE** that it is not expected that this function forms part of a stable
|
||||
API."
|
||||
[result processed]
|
||||
(assoc result :text
|
||||
(local-links
|
||||
|
|
@ -128,9 +148,13 @@
|
|||
:heading-anchors true))))
|
||||
|
||||
|
||||
(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."
|
||||
(defn- reintegrate-inclusions
|
||||
"Given a map of the form produced by `process-text`, return a map based on
|
||||
that map with the key `:content` bound to a string of HTML text
|
||||
with the inclusions (if any) generated by extension processors reintegrated.
|
||||
|
||||
**NOTE** that it is not expected that this function forms part of a stable
|
||||
API."
|
||||
([processed-text]
|
||||
(assoc
|
||||
processed-text
|
||||
|
|
@ -155,7 +179,19 @@
|
|||
(cs/replace (kw inclusions) "\\/" "/"))))))))
|
||||
|
||||
|
||||
(defn process-text
|
||||
(defn- process-text
|
||||
"Process extension fragments in this text. Arguments are:
|
||||
* `index`, the index number of the current fragment;
|
||||
* `result`, a context within which the final result is being accumulated;
|
||||
* `fragments`, a sequence of the fragments of the original text which have
|
||||
not yet been processed;
|
||||
* `processed`, a reverse sequence of the fragments of the original text
|
||||
which have already been processed.
|
||||
|
||||
Returns a map derived from `result` enhanced with the accumulated result.
|
||||
|
||||
**NOTE** that it is not expected that this function forms part of a stable
|
||||
API."
|
||||
[^Integer index ^clojure.lang.Associative result fragments processed]
|
||||
(let [fragment (first fragments)
|
||||
;; if I didn't find a formatter for a back-tick marked fragment,
|
||||
|
|
@ -184,13 +220,16 @@
|
|||
(process-markdown-fragment index result remarked (rest fragments) processed))))
|
||||
|
||||
(defn md->html
|
||||
"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.
|
||||
"Process the source text (assumed to be the value of the key `:source` in this
|
||||
`context`, expected to be a full HTTP request map, so that extensions may in
|
||||
future potentially have access to things like `accepts-*` headers.
|
||||
|
||||
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."
|
||||
The source is assumed to be markdown potentially containing both local links
|
||||
and extension specifications, and return a map with top-level keys:
|
||||
* `:content`, the HTML content of the page to serve; and
|
||||
* `:extensions`, being a subset of the `:formatters` map from
|
||||
`smeagol.configuration/config` covering the extensions actually used in the
|
||||
generated content."
|
||||
[^clojure.lang.Associative context]
|
||||
(reintegrate-inclusions
|
||||
(process-text
|
||||
|
|
|
|||
|
|
@ -37,7 +37,11 @@
|
|||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def template-path "templates/")
|
||||
(def template-path
|
||||
"Path to the resource directory in which Selmer templates are stored. These
|
||||
should be in a place which is not editable from the Wiki, otherwise
|
||||
users may break things which they cannot subsequently fix!"
|
||||
"templates/")
|
||||
|
||||
(parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field)))
|
||||
|
||||
|
|
@ -48,10 +52,14 @@
|
|||
(fn [args context-map]
|
||||
(let [messages (:i18n context-map)
|
||||
default (or (second args) (first args))]
|
||||
(if (map? messages) (or (messages (keyword (first args))) default) default))))
|
||||
(if (map? messages) (or (messages (keyword (first args))) default)
|
||||
default))))
|
||||
|
||||
|
||||
(deftype RenderableTemplate [template params]
|
||||
(deftype RenderableTemplate
|
||||
;; Boilerplate from Luminus. Load a template file into an object which may
|
||||
;; be rendered.
|
||||
[template params]
|
||||
Renderable
|
||||
(render [this request]
|
||||
(try
|
||||
|
|
@ -75,6 +83,8 @@
|
|||
|
||||
|
||||
(defn render
|
||||
"Boilerplate from Luminus. Render an HTML page based on this `template` and
|
||||
these `params`. Returns HTML source as a string."
|
||||
[template & [params]]
|
||||
(try
|
||||
(RenderableTemplate. template params)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
|
||||
(defn edit-users
|
||||
"Put a list of users on-screen for editing."
|
||||
"Render a page showing a list of users for editing."
|
||||
[request]
|
||||
(let [params (keywordize-keys (:params request))
|
||||
user (session/get :user)]
|
||||
|
|
@ -43,7 +43,8 @@
|
|||
:users (auth/list-users)}))))
|
||||
|
||||
(defn delete-user
|
||||
"Delete a user."
|
||||
"Render a form allowing a user to be deleted; and
|
||||
process that form.."
|
||||
[request]
|
||||
(let [params (keywordize-keys (:params request))
|
||||
target (:target params)
|
||||
|
|
@ -59,7 +60,8 @@
|
|||
|
||||
|
||||
(defn edit-user
|
||||
"Put an individual user's details on screen for editing."
|
||||
"Render a form showing an individual user's details for editing; and
|
||||
process that form."
|
||||
[request]
|
||||
(let [params (keywordize-keys (:params request))]
|
||||
(try
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
[clojure.walk :refer :all]
|
||||
[compojure.core :refer :all]
|
||||
[java-time :as jt]
|
||||
[markdown.core :as md]
|
||||
[me.raynes.fs :as fs]
|
||||
[noir.io :as io]
|
||||
[noir.response :as response]
|
||||
|
|
@ -20,6 +21,7 @@
|
|||
[smeagol.formatting :refer [md->html]]
|
||||
[smeagol.history :as hist]
|
||||
[smeagol.layout :as layout]
|
||||
[smeagol.local-links :refer :all]
|
||||
[smeagol.routes.admin :as admin]
|
||||
[smeagol.sanity :refer [show-sanity-check-error]]
|
||||
[smeagol.util :as util]
|
||||
|
|
@ -107,7 +109,10 @@
|
|||
(merge (util/standard-params request)
|
||||
{:title (str (util/get-message :edit-title-prefix request) " " page)
|
||||
:page page
|
||||
:side-bar (md->html (assoc request :source (slurp (cjio/file util/content-dir side-bar))))
|
||||
:side-bar (md/md-to-html-string
|
||||
(local-links
|
||||
(slurp (cjio/file content-dir side-bar)))
|
||||
:heading-anchors true)
|
||||
:content (if exists? (slurp file-path) "")
|
||||
:exists exists?})))))))
|
||||
|
||||
|
|
@ -155,18 +160,22 @@
|
|||
;; :remote "https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.js"} :core)
|
||||
|
||||
(defn collect-preferred
|
||||
"Collect preferred variants of resources required by extensions used in the
|
||||
page described in this `processed-text`."
|
||||
([processed-text]
|
||||
(concat
|
||||
(collect-preferred processed-text :scripts)
|
||||
(collect-preferred processed-text :styles)))
|
||||
([processed-text resource-type]
|
||||
(reduce concat
|
||||
(reduce
|
||||
concat
|
||||
(map
|
||||
(fn [extension-key]
|
||||
(map
|
||||
(fn [requirement]
|
||||
(let [r (preferred-source
|
||||
(-> processed-text :extensions extension-key resource-type requirement)
|
||||
(-> processed-text :extensions extension-key
|
||||
resource-type requirement)
|
||||
requirement)]
|
||||
(if (empty? r)
|
||||
(log/warn "Found no valid URL for requirement"
|
||||
|
|
@ -235,7 +244,9 @@
|
|||
;;;; this next section is all stuff supporting the list-uploads page, and maybe
|
||||
;;;; should be moved to its own file.
|
||||
|
||||
(def image-extns #{".gif" ".jpg" ".jpeg" ".png"})
|
||||
(def image-extns
|
||||
"File name extensions suggesting files which can be considered to be images."
|
||||
#{".gif" ".jpg" ".jpeg" ".png"})
|
||||
|
||||
(defn format-instant
|
||||
"Format this `unix-time`, expected to be a Long, into something human readable.
|
||||
|
|
@ -322,6 +333,7 @@
|
|||
{:title (util/get-message :file-upload-title request)
|
||||
:uploaded uploaded}))))
|
||||
|
||||
|
||||
(defn version-page
|
||||
"Render a specific historical version of a page"
|
||||
[request]
|
||||
|
|
@ -363,7 +375,7 @@
|
|||
|
||||
|
||||
(defn auth-page
|
||||
"Render the auth page"
|
||||
"Render the authentication (login) page"
|
||||
[request]
|
||||
(or
|
||||
(show-sanity-check-error)
|
||||
|
|
@ -395,6 +407,7 @@
|
|||
|
||||
(defn wrap-restricted-redirect
|
||||
;; TODO: this is not idiomatic, and it's too late to write something idiomatic just now
|
||||
;; TODO TODO: it's also not working.
|
||||
[f request]
|
||||
(route/restricted
|
||||
(apply
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
(ns ^{:doc "Functions related to sanity checks and error reporting in conditions
|
||||
where the environment may not be sane."
|
||||
where the environment may not be sane. Generally, the functions in this
|
||||
file are called (via `sanity-check-installation`, which is the only
|
||||
supported entry point) at first start-up."
|
||||
:author "Simon Brooke"}
|
||||
smeagol.sanity
|
||||
(:import (java.util Locale))
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@
|
|||
(.write iw nil iio-img iw-param)))
|
||||
|
||||
(def image?
|
||||
"True if the file at this `filename` appears as though it may be an image"
|
||||
(memoize
|
||||
(fn [filename]
|
||||
(image-file-extns (fs/extension (cs/lower-case (str filename)))))))
|
||||
|
|
|
|||
|
|
@ -39,9 +39,12 @@
|
|||
|
||||
|
||||
(def start-page
|
||||
"The page to load on startup, taken from configuration."
|
||||
(:start-page config))
|
||||
|
||||
(def content-dir
|
||||
"The absolute path to the directory in which Wiki content (i.e., Markdown
|
||||
files) are stored."
|
||||
(str
|
||||
(fs/absolute
|
||||
(or
|
||||
|
|
@ -49,6 +52,7 @@
|
|||
(cjio/file (io/resource-path) "content")))))
|
||||
|
||||
(def upload-dir
|
||||
"The absolute path to the directory in which uploaded files are stored."
|
||||
(str (cjio/file content-dir "uploads")))
|
||||
|
||||
(def local-url-base
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue