Merge branch 'release/1.0.2'

This commit is contained in:
Simon Brooke 2019-01-19 17:46:44 +00:00
commit f2479b2f99
41 changed files with 1420 additions and 170 deletions

View file

@ -1,3 +1,29 @@
FROM tomcat:alpine
COPY target/smeagol-*-standalone.war $CATALINA_HOME/webapps/smeagol.war
FROM alpine:3.6
MAINTAINER Simon Brooke <simon@journeyman.cc>
ENV JAVA_HOME=/usr/lib/jvm/default-jvm
RUN apk add --no-cache openjdk8 && \
ln -sf "${JAVA_HOME}/bin/"* "/usr/bin/"
# ensure the directories I'm going to write to actually exist!
RUN mkdir -p /usr/local/bin
RUN mkdir -p /usr/local/etc
COPY target/smeagol-*-standalone.jar /usr/local/bin/smeagol.jar
COPY resources/passwd /usr/local/etc/passwd
COPY resources/config.edn /usr/local/etc/config.edn
COPY resources/public/content /usr/local/etc/content
ENV SMEAGOL_CONFIG=/usr/local/etc/config.edn
ENV SMEAGOL_CONTENT_DIR=/usr/local/etc/content
ENV SMEAGOL_PASSWD=/usr/local/etc/passwd
ENV TIMBRE_DEFAULT_STACKTRACE_FONTS="{}"
ENV TIMBRE_LEVEL=':info'
ENV PORT=80
EXPOSE 80
CMD java -jar /usr/local/bin/smeagol.jar

View file

@ -98,6 +98,18 @@ You can (if you're logged in) upload files, including images, using the **Upload
![Smeagol](http://vignette3.wikia.nocookie.net/lotr/images/e/e1/Gollum_Render.png/revision/latest?cb=20141218075509)
## Includes
You can include pages into the current page you're working on. To do so, you can add a include link:
`&[: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.
## Advertisement
If you like what you see here, I am available for work on open source Clojure projects.

15
doc/include.md Normal file
View file

@ -0,0 +1,15 @@
# Include Feature
## Requirements
The user can include page title, abstract or the whole content in a given page. Headings and enumerations can be indented by a given include-level.
## Thoughts & Questions
* Which include syntax should be used?
* page include can be definde alongsite of image includes - sth. like
`&[:indent-heading s/Num :indent-list s/Num](relative or absolute url s/Str)`
* Which kind of urls should we accept for page includes?
* relative local urls (we will need some care to prohibit directory traversal ...)
* absolute github / gitlab / gitblit urls without authentication.
* Which metadata can be used for title / abstract ?
* MultiMarcdown-Metadata is supported by clj-markdown :-)
* How can we test page includes?
* we will need a content resolver component for testing and at least one for production resolving.

View file

@ -1,4 +1,4 @@
(defproject smeagol "1.0.0-rc3"
(defproject smeagol "1.0.2"
:description "A simple Git-backed Wiki inspired by Gollum"
:url "https://github.com/simon-brooke/smeagol"
:license {:name "GNU General Public License,version 2.0 or (at your option) any later version"
@ -7,10 +7,9 @@
[clj-yaml "0.4.0"]
[com.cemerick/url "0.1.1"]
[com.fzakaria/slf4j-timbre "0.3.7"]
[com.stuartsierra/component "0.3.2"]
[com.taoensso/encore "2.92.0"]
[com.cemerick/url "0.1.1"]
[com.taoensso/timbre "4.10.0"]
[com.fzakaria/slf4j-timbre "0.3.7"]
[com.taoensso/tower "3.0.2" :exclusions [com.taoensso/encore]]
[crypto-password "0.2.0"]
[environ "1.1.0"]
@ -28,6 +27,7 @@
[org.slf4j/log4j-over-slf4j "1.7.25"]
[org.slf4j/jul-to-slf4j "1.7.25"]
[org.slf4j/jcl-over-slf4j "1.7.25"]
[prismatic/schema "1.1.9"]
[prone "1.1.4"]
[ring/ring-anti-forgery "1.1.0"]
[ring-server "0.4.0"]
@ -61,10 +61,10 @@
:release-tasks [["vcs" "assert-committed"]
["change" "version" "leiningen.release/bump-version" "release"]
["vcs" "commit"]
;; ["vcs" "tag"] -- not working, problems with secret key
["vcs" "tag" "v." "--no-sign"]
["clean"]
["bower" "install"]
["ring" "uberwar"]
["ring" "uberjar"]
["docker" "build"]
["docker" "push"]
["change" "version" "leiningen.release/bump-version"]

View file

@ -22,14 +22,22 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; config.edn: a simple configuration map for Smeagol; inspired by Cryogen.
;;; This is top-level configuration.
;;; This is top-level configuration. All values can be overridden with
;;; environment variables.
;; ; ; ; ; ; ; ; ; ;
{
:site-title "Smeagol" ;; overall title of the site, used in page headings
:content-dir "resources/public/content"
:start-page "Introduction"
;; where content is served from.
: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
"backticks" smeagol.formatting/process-backticks}
}
:log-level :info ;; the minimum logging level; one of
;; :trace :debug :info :warn :error :fatal
:passwd "resources/passwd"
;; where the password file is stored
:site-title "Smeagol"} ;; overall title of the site, used in
;; page headings

144
resources/i18n/de-DE.edn Normal file
View file

@ -0,0 +1,144 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; 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.
;;;;
;;;; German language translation contributed by and
;;;; Copyright (C) 2017 Soukyan Blackwood <priority.inc@gmail.com>
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; en-GB.edn: English-language messages.
;;; This is essentially all the text in the chrome - that which isn't editable
;;; through the wiki itself; and the test in the sanity check report.
;; ; ; ; ; ; ; ; ; ;
{:add-user-label "Neuen Benutzer zufügen" ;; label for the add user link on edit users page
:change-pass-label "Kennwort ändern!"
;; text of the change password widget itself on the
;; change password page
:change-pass-link "Kennwort ändern"
;; text of the change password link on the menu
:change-pass-prompt "Um Ihr Kennwort zu ändern"
;; text of the change password widget prompt on the
;; change password page
:change-col-hdr "Änderungen" ;; header for the changes column in history
:chpass-bad-match "Die von Ihnen vorgeschlagenen Kennwörter stimmen nicht überein"
;; error text if proposed passwords don't match
:chpass-fail "Ihr Kennwort wurde nicht geändert"
;; error text on fail other htan too short or bad match
:chpass-success "Ihr Kennwort wurde geändert"
;; confirmation text on password change
:chpass-too-short "Das von Ihnen vorgeschlagene Kennwort war zu kurz: 8 Zeichen erforderlich"
;; error text if proposed password is too short
:chpass-title-prefix "Kennwort ändern für"
;; prefix for title of change password page
:content-dir "Das Inhaltsverzeichnis"
;; used in sanity check report
:content-dir-exists "Das Inhaltsverzeichnis existiert"
;; used in sanity check report
:content-dir-is-dir "Das Inhaltsverzeichnis ist ein Verzeichnis"
;; used in sanity check report
:cookies-about "Über Cookies" ;; about cookies text
:cookies-more "Es wird von Ihrem Browser gelöscht, sobald Sie diese Seite verlassen. Diese Webseite benutzt keine Cookies von dritten Parteien, daher kann Ihr Besuch hier nicht von anderen Seiten verfolgt werden."
;; more about cookies text
:default-page-title "Einleitung" ;; title of the default page in this wiki
:del-col-hdr "Löschen" ;; header for delete column on edit users page
:del-user-fail "Könnte Benutzer nicht löschen"
;; error message on failure to delete user
:del-user-success "Benutzer wurde erfolgreich gelöscht"
;; confirmation message on deletion of user
:diff-title-prefix "Änderungen seit der Version"
;; prefix for the header of the changes page
:does-not-exist "existiert nicht"
;; (of a file or directory); used in sanity check report
:edit-col-hdr "Bearbeiten" ;; header for edit column on edit users page
:edit-page-link "Diese Seite bearbeiten"
;; text of the edit page link on the content frame
:edit-title-prefix "Bearbeiten" ;; prefix for title of edit content page
:edit-users-link "Benutzer bearbeiten" ;; text of the edit users link on the menu
:edit-users-title "Benutzer zum Bearbeiten auswählen"
;; title of edit users page
:email-prompt "E-Mail Adresse" ;; text of the email widget prompt on edit user page
:file-or-directory "Datei oder Verzeichnis"
;; used in sanity check report
:file-summary-prompt "Beschreibung/was wurde gändert"
;; prompt for the file upload summary input
:file-upload-link-text "Sie können auf diese Datei verweisen, mit Hilfe von einer Verknüpfung von der Form"
;; Text introducing the link to an uploaded file
:file-upload-prompt "Datei zum Hochladen" ;; prompt string for the file upload widget
:file-upload-title "Upload a file" ;; title for the file upload page
:is-admin-prompt "Ist Administrator?"
:here "hier" ;; used in sanity check report
:home-link "Startseite" ;; text of the home link on the menu
:is-not-directory "ist kein Verzeichnis"
;; (of a file or directory) used in sanity check report
:is-not-readable "ist nicht lesbar"
;; (of a file or directory) used in sanity check report
:is-not-writable "ist nicht schreibbar"
;; (of a file or directory) used in sanity check report
: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 "Um dieses Wiki zu bearbeiten"
;; text of the action widget prompt on the login page
:logout-label "Abmelden!" ;; text of the logout widget on the logout page
:logout-link "Abmelden" ;; text of the logout link on the menu
:logged-in-as "Sie sind angemeldet als"
;; text of the 'logged in as' label on the menu
:history-link "Historie" ;; text of the history link on the content frame
:history-title-prefix "Historie von" ;; prefix of the title on the history page
:new-pass-prompt "Neues Kennwort" ;; text of the new password widget prompt on the change
;; password and edit user pages
:no-admin-users "In der Datei 'passwd' gibt es keine Benutzer mit Administratorrechten"
;; used in sanity check report
:old-pass-prompt "Ihr Kennwort"
;; text of the old password widget prompt on the change
;; password page, and password widget on login page
:password-file "Die Kennwort ('passwd') Datei"
;; used in sanity check report
:problems-found "Probleme gefunden"
;; used in sanity check report
:rpt-pass-prompt "und noch einmal" ;; text of the new password widget prompt on the change
;; password and edit user pages
:save-prompt "Wenn Sie mit dem Bearbeiten fertig sind"
;; text of the save widget label on edit content
;; and edit user page
:save-label "Speichern!" ;; text of the save widget itself
:save-user-fail "Benutzer konnte nicht gespeichert werden"
:save-user-success "Benutzer erfolgreich gespeichert"
:see-documentation "Für mehr Information sehen Sie bitte die Dokumentation "
;; used in sanity check report
:smeagol-not-initialised
"Smeagol ist nicht richtig initialisiert"
;; title of the sanity check report
:smeagol-misconfiguration
"Smeagol konnte einige der Ressourcen, von denen es abhängt,
nicht finden; wahrscheinlich wegen einer Fehlkonfiguration oder fehlenden Umgebungsvariablen."
;; used in sanity check report
:user-lacks-field "Das Feld für den Benutzereintrag in die passwd Datei fehlt"
;; used in sanity check report
:username-prompt "Benutzername" ;; text of the username widget prompt on edit user page
;; text of the is admin widget prompt on edit user page
:user-title-prefix "Benutzer bearbeiten" ;; prefix for title of edit user page
:vers-col-hdr "Version" ;; header for the version column in history
:what-col-hdr "Wass" ;; header for the what column in history
:what-changed-prompt "Was haben Sie verändert?"
;; text of the summary widget prompt on edit
;; content page
:when-col-hdr "Was" ;; header for the when column in history
:your-uname-prompt "Ihr Benutzername" ;; text of the username widget prompt on the login page
}

1
resources/i18n/de.edn Symbolic link
View file

@ -0,0 +1 @@
de-DE.edn

View file

@ -42,7 +42,7 @@
;; 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"
:chpass-too-short "Your 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

1
resources/i18n/lt.edn Symbolic link
View file

@ -0,0 +1 @@
lt_LT.edn

143
resources/i18n/lt_LT.edn Normal file
View file

@ -0,0 +1,143 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; 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.
;;;;
;;;; Lithuanian language translation contributed by and
;;;; Copyright (C) 2017 Soukyan Blackwood <priority.inc@gmail.com>
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; en-GB.edn: English-language messages.
;;; This is essentially all the text in the chrome - that which isn't editable
;;; through the wiki itself; and the test in the sanity check report.
;; ; ; ; ; ; ; ; ; ;
{:add-user-label "Pridėti naują naudotoją" ;; label for the add user link on edit users page
:change-pass-label "Pakeiskite slaptažodį!"
;; text of the change password widget itself on the
;; change password page
:change-pass-link "Pakeiskite slaptažodį"
;; text of the change password link on the menu
:change-pass-prompt "Pakeisti slaptažodį"
;; text of the change password widget prompt on the
;; change password page
:change-col-hdr "Pakeitimai" ;; header for the changes column in history
:chpass-bad-match "Jūsų siūlomi slaptažodžiai - nesutampa"
;; error text if proposed passwords don't match
:chpass-fail "Jūsų slaptažodis nebuvo pakeistas"
;; error text on fail other htan too short or bad match
:chpass-success "Jūsų slaptažodis buvo pakeistas"
;; confirmation text on password change
:chpass-too-short "Jūsų siūlomas slaptožodis per trumpas: reikia bent aštuonių ženklų"
;; error text if proposed password is too short
:chpass-title-prefix "Pakeisti slaptažodį, dėl"
;; prefix for title of change password page
:content-dir "Turinio katalogas"
;; used in sanity check report
:content-dir-exists "Turinio katalogas egzistuoja"
;; used in sanity check report
:content-dir-is-dir "Turinio katalogas yra katalogas"
;; used in sanity check report
:cookies-about "Apie slapukus" ;; about cookies text
:cookies-more "Šis puslapis saugo jūsų sesijų informaciją jūsų naršyklėje “slapukų” forma. Tai mums leidžia jums rodyti tik tai, ką norite matyti. Slapukai neturi jokių jūsų identifikavimo duomenų ir kiti puslapiai negali jų “perskaityti”. Šie slapukai ištrinami iš karto, vos jūs išjungiate šį puslapį. Šis puslapis nenaudoja jokių trečiųjų asmenų slapukų, ir jūsų apsilankymas čia negali būti atsektas jokio kito puslapio."
;; more about cookies text
:default-page-title "Pristatymas" ;; title of the default page in this wiki
:del-col-hdr "Ištrinti" ;; header for delete column on edit users page
:del-user-fail "Naudotojas negalėjo būti ištrintas"
;; error message on failure to delete user
:del-user-success "Naudotojas sėkmingai ištrintas"
;; confirmation message on deletion of user
:diff-title-prefix "Pakeitimai nuo versijos"
;; prefix for the header of the changes page
:does-not-exist "neegzsituoja"
;; (of a file or directory); used in sanity check report
:edit-col-hdr "Keisti" ;; header for edit column on edit users page
:edit-page-link "Keisti šį puslapį"
;; text of the edit page link on the content frame
:edit-title-prefix "Keisti" ;; prefix for title of edit content page
:edit-users-link "Keisti naudotojus" ;; text of the edit users link on the menu
:edit-users-title "Pasirinkti naudotojus keitimui"
;; title of edit users page
:email-prompt "el. Paštas" ;; text of the email widget prompt on edit user page
:file-or-directory "Failas ar katalogas"
;; used in sanity check report
:file-summary-prompt "Aprašymas/kas pakeista"
;; prompt for the file upload summary input
:file-upload-link-text "Galite nukreipti į šį failą naudodami formos nuorodą"
;; Text introducing the link to an uploaded file
:file-upload-prompt "Failas įkėlimui" ;; prompt string for the file upload widget
:file-upload-title "Įkelti failą" ;; title for the file upload page
:is-admin-prompt "Administratorius?"
:here "čia" ;; used in sanity check report
:home-link "Pradinis" ;; text of the home link on the menu
:is-not-directory "ne katalogas"
;; (of a file or directory) used in sanity check report
:is-not-readable "neperskaitomas"
;; (of a file or directory) used in sanity check report
:is-not-writable "nerašomas"
;; (of a file or directory) used in sanity check report
:login-label "Prisijunkite!" ;; text of the login widget on the login page
:login-link "Prisijunkite" ;; text of the login link on the menu
:login-prompt "Pakeisti šį viki"
;; text of the action widget prompt on the login page
:logout-label "Atsijunkite!" ;; text of the logout widget on the logout page
:logout-link "Atsijunkite" ;; text of the logout link on the menu
:logged-in-as "Jūs prisijungęs, kaip"
;; text of the 'logged in as' label on the menu
:history-link "Istorija" ;; text of the history link on the content frame
:history-title-prefix "Istorija apie" ;; prefix of the title on the history page
:new-pass-prompt "Naujas slaptažodis" ;; text of the new password widget prompt on the change
;; password and edit user pages
:no-admin-users "Naudotojų passwd faile su administatoriaus privilegijomis nėra"
;; used in sanity check report
:old-pass-prompt "Jūsų slaptažodis"
;; text of the old password widget prompt on the change
;; password page, and password widget on login page
:password-file "slaptažodžio (passwd) failas"
;; used in sanity check report
:problems-found "rasta problemų"
;; used in sanity check report
:rpt-pass-prompt "Ir dar kartą" ;; text of the new password widget prompt on the change
;; password and edit user pages
:save-prompt "Kai baigsite redaguoti"
;; text of the save widget label on edit content
;; and edit user page
:save-label "Išsaugokite!" ;; text of the save widget itself
:save-user-fail "Nepavyko išsaugoti naudotojo"
:save-user-success "Naudotojas sėkmingai išsaugotas"
:see-documentation "Daugiau informacijos ieškokite dokumentacijoje "
;; used in sanity check report
:smeagol-not-initialised
"Smygolas buvo blogai paleistas"
;; title of the sanity check report
:smeagol-misconfiguration
"Smygolas nerado kai kurių jam reikalingų resursų, taip nutikti galėjo dėl neteisingų nustatymų, arba trūkstamų aplinkos kintamųjų"
;; used in sanity check report
:user-lacks-field "Naudotojo passwd failo įraše trūksta laukelio"
;; used in sanity check report
:username-prompt "Naudotojo vardas" ;; text of the username widget prompt on edit user page
;; text of the is admin widget prompt on edit user page
:user-title-prefix "Pakeisti naudotoją" ;; prefix for title of edit user page
:vers-col-hdr "Versija" ;; header for the version column in history
:what-col-hdr "Kas" ;; header for the what column in history
:what-changed-prompt "Ką pakeitėte?"
;; text of the summary widget prompt on edit
;; content page
:when-col-hdr "Kada" ;; header for the when column in history
:your-uname-prompt "Jūsų naudotojo vardas" ;; text of the username widget prompt on the login page
}

1
resources/i18n/ru.edn Symbolic link
View file

@ -0,0 +1 @@
ru_RU.edn

144
resources/i18n/ru_RU.edn Normal file
View file

@ -0,0 +1,144 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; 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.
;;;;
;;;; Russian language translation contributed by and
;;;; Copyright (C) 2017 Soukyan Blackwood <priority.inc@gmail.com>
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; en-GB.edn: English-language messages.
;;; This is essentially all the text in the chrome - that which isn't editable
;;; through the wiki itself; and the test in the sanity check report.
;; ; ; ; ; ; ; ; ; ;
{:add-user-label "добавить нового пользователя" ;; label for the add user link on edit users page
:change-pass-label "изменить пароль!"
;; text of the change password widget itself on the
;; change password page
:change-pass-link "изменить пароль"
;; text of the change password link on the menu
:change-pass-prompt "Чтобы изменить пароль"
;; text of the change password widget prompt on the
;; change password page
:change-col-hdr "изменения" ;; header for the changes column in history
:chpass-bad-match "Ваши предложенные пароли не совпадают"
;; error text if proposed passwords don't match
:chpass-fail "Ваш пароль не был изменён"
;; error text on fail other htan too short or bad match
:chpass-success "Ваш пароль был изменён"
;; confirmation text on password change
:chpass-too-short "Вы предложили пароль недостаточно длины: требуется восемь символов"
;; error text if proposed password is too short
:chpass-title-prefix "Изменить пароль для"
;; prefix for title of change password page
:content-dir "Каталог содержимого"
;; used in sanity check report
:content-dir-exists "Каталог содержимого существуе"
;; used in sanity check report
:content-dir-is-dir "Каталог содержимого - это каталог"
;; used in sanity check report
:cookies-about "Об кукисах" ;; about cookies text
:cookies-more "Этот сайт хранит информацию о сеансе как «cookie» в вашем браузере. Это поможет нам показать вам контент, который вы хотите увидеть. Этот файл cookie не идентифицирует вас и не может быть прочитан другими веб-сайтами. Он удаляется браузером, как только вы покидаете этот сайт. Этот веб-сайт не использует сторонние файлы cookie, поэтому ваш визит здесь не может быть отслежен другими веб-сайтами"
;; more about cookies text
:default-page-title "представление" ;; title of the default page in this wiki
:del-col-hdr "Удалить" ;; header for delete column on edit users page
:del-user-fail "Не удалось удалить пользователя"
;; error message on failure to delete user
:del-user-success "успешно удалён пользователь"
;; confirmation message on deletion of user
:diff-title-prefix "Изменения с версии"
;; prefix for the header of the changes page
:does-not-exist "не существует"
;; (of a file or directory); used in sanity check report
:edit-col-hdr "редактировать" ;; header for edit column on edit users page
:edit-page-link "Редактировать эту страницу"
;; text of the edit page link on the content frame
:edit-title-prefix "редактировать" ;; prefix for title of edit content page
:edit-users-link "Редактировать пользователей" ;; text of the edit users link on the menu
:edit-users-title "Выберите пользователя для редактирования"
;; title of edit users page
:email-prompt "Адрес электронной почты" ;; text of the email widget prompt on edit user page
:file-or-directory "Файл или каталог"
;; used in sanity check report
:file-summary-prompt "Описание / что изменилось"
;; prompt for the file upload summary input
:file-upload-link-text "Вы можете ссылать этот файл, используя ссылку формы"
;; Text introducing the link to an uploaded file
:file-upload-prompt "Файл для загрузки" ;; prompt string for the file upload widget
:file-upload-title "Загрузить файл" ;; title for the file upload page
:is-admin-prompt "Администратор?"
:here "здесь" ;; used in sanity check report
:home-link "Главная" ;; text of the home link on the menu
:is-not-directory "не является каталогом"
;; (of a file or directory) used in sanity check report
:is-not-readable "не читаемый"
;; (of a file or directory) used in sanity check report
:is-not-writable "недоступен для записи"
;; (of a file or directory) used in sanity check report
:login-label "Вход!" ;; text of the login widget on the login page
:login-link "Вход" ;; text of the login link on the menu
:login-prompt "Чтобы отредактировать эту вики"
;; text of the action widget prompt on the login page
:logout-label "Выйти!" ;; text of the logout widget on the logout page
:logout-link "Выйти" ;; text of the logout link on the menu
:logged-in-as "Вы вошли как"
;; text of the 'logged in as' label on the menu
:history-link "история" ;; text of the history link on the content frame
:history-title-prefix "История об" ;; prefix of the title on the history page
:new-pass-prompt "Новый пароль" ;; text of the new password widget prompt on the change
;; password and edit user pages
:no-admin-users "В файле 'passwd' нет пользователей с правами администратора"
;; used in sanity check report
:old-pass-prompt "Ваш пароль"
;; text of the old password widget prompt on the change
;; password page, and password widget on login page
:password-file "файл пароля ('passwd')"
;; used in sanity check report
:problems-found "проблемы были найдены"
;; used in sanity check report
:rpt-pass-prompt "И опять" ;; text of the new password widget prompt on the change
;; password and edit user pages
:save-prompt "Когда вы закончили редактирование"
;; text of the save widget label on edit content
;; and edit user page
:save-label "Сохранить!" ;; text of the save widget itself
:save-user-fail "Не удалось сохранить пользователя"
:save-user-success "Успешно сохранённый пользователь"
:see-documentation "для получения дополнительной информации смотрите документацию "
;; used in sanity check report
:smeagol-not-initialised
"Смеаголь неправильно инициализирован"
;; title of the sanity check report
:smeagol-misconfiguration
"Смеаголь не смог найти некоторые ресурсы, от которых это зависит,
возможно, из-за неправильной конфигурации или отсутствующих переменных среды."
;; used in sanity check report
:user-lacks-field "В пользовательской записи в файле passwd отсутствует поле"
;; used in sanity check report
:username-prompt "Имя пользователя" ;; text of the username widget prompt on edit user page
;; text of the is admin widget prompt on edit user page
:user-title-prefix "Изменить пользователя" ;; prefix for title of edit user page
:vers-col-hdr "Версия" ;; header for the version column in history
:what-col-hdr "Что" ;; header for the what column in history
:what-changed-prompt "Что вы изменили?"
;; text of the summary widget prompt on edit
;; content page
:when-col-hdr "когда" ;; header for the when column in history
:your-uname-prompt "Ваш логин" ;; text of the username widget prompt on the login page
}

View file

@ -1 +1 @@
{:admin {:admin true, :email "info@weft.scot", :password "admin"}, :jenny {:email "jenny@auchencairn.org", :admin false, :password "$s0$f0801$1uniQfftB37G5e5GklJANQ==$kQ0+/YcCuaz2x5iYjwhNlDlnWX/exE/8pSC+R4C0WvQ="}}
{:admin {:admin true, :email "info@weft.scot", :password "admin"}, :simon {:email "simon@journeyman.cc", :admin true, :password "$s0$f0801$sqhbxtzK6nx9RnVUhwtQlg==$dMIUbof8esjsGyiB+zb3gMH21L/WSCR+wD3vIag4EVc="}}

View file

@ -0,0 +1,7 @@
# 1.0.2
Two fixes contributed by a user:
* [Configurable start page](https://github.com/journeyman-cc/smeagol/commit/b1eeecde1d0555e9b708807c63e28fac21de58c4)
* [Fixed uploads](https://github.com/journeyman-cc/smeagol/commit/1136e792d4810fd9af8b443fdea6d6be4aeda79e)
Many thanks to [M Jerger](https://github.com/jerger) for these.

View file

@ -2,19 +2,32 @@ Smeagol reads a configuration file, whose content should be formatted as a cloju
The default content is as follows:
```
{
:site-title "Smeagol" ;; overall title of the site, used in page headings
:default-locale "en-GB" ;; default language used for messages
:content-dir "/usr/local/etc/content"
;; where content is served from
:passwd "/usr/local/etc/passwd"
;; where the password file is stored
:log-level :info ;; the minimum logging level; one of
;; :trace :debug :info :warn :error :fatal
:formatters {"vega" smeagol.formatting/process-vega
"vis" smeagol.formatting/process-vega
"mermaid" smeagol.formatting/process-mermaid
"backticks" smeagol.formatting/process-backticks}
}
```
The three keys given above should be present. The values should be:
The values should be:
* **:site-title** The title for your wiki
* **:default-locale** A string comprising a lower-case [ISO 639](https://en.wikipedia.org/wiki/ISO_639) code specifying a language, optionally followed by a hyphen and an upper-case [ISO 3166](https://en.wikipedia.org/wiki/ISO_3166) specifying a country.
* **:formatters** A map of formatters used in [[Extensible Markup]], q.v.
* `:content-dir` The directory in which your editable content is stored;
* `:default-locale` A string comprising a lower-case [ISO 639](https://en.wikipedia.org/wiki/ISO_639) code specifying a language, optionally followed by a hyphen and an upper-case [ISO 3166](https://en.wikipedia.org/wiki/ISO_3166) specifying a country.
* `:formatters` A map of formatters used in [[Extensible Markup]], q.v.
* `:log-level` The minimum level of log messages to be logged; one of `:trace :debug :info :warn :error :fatal`
* `:passwd` The path to your `passwd` file - see [[Security and authentication]];
* `:site-title` The title for your wiki.
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.
Note that all the values in the configuration can be overridden with [[Environment Variables]].

View file

@ -1,3 +1,8 @@
## Choosing a deployment mechanism
There are currently three ways you can deploy Smeagol: as an executable Jar file, as a Docker image, and as a web-app in a [Servlet container](https://en.wikipedia.org/wiki/Web_container). Each method has advantages and disadvantages.
The Jar file is extremely easy to deploy and to configure, but cannot currently serve [HTTPS](https://en.wikipedia.org/wiki/HTTPS), which, on the modern web, is a significant disadvantage. The Docker image is just a wrapper around the Jar file; it's particularly suitable for automated deployment. The web-app solution offloads responsibility for things like HTTPS to the Servlet container, and consequently can be much more secure; but it can really only be configured at compile time.
## Deploying as a stand-alone application
To deploy Smeagol as a stand-alone application, either download the jar file for the release you want to deploy, or clone the source and compile it with:
@ -6,19 +11,26 @@ To deploy Smeagol as a stand-alone application, either download the jar file for
This will create a jar file in the `target` directory, named `smeagol-`*VERSION*`-standalone.jar`.
Smeagol cannot access either its configuration or its content from the jar file, as otherwise they would not be editable. Consequently you should set up three environment variables:
Smeagol cannot access either its configuration or its content from the jar file, as otherwise they would not be editable. There are three solutions to this:
1. `SMEAGOL_CONFIG` should be the full or relative pathname of a Smeagol [[Configuration]] file;
2. `SMEAGOL_CONTENT_DIR` should be the full or relative pathname of the directory from which Smeagol should serve content (which may initially be empty, but must be writable by the process which runs Smeagol)'
3. `SMEAGOL_PASSWD` should be the full or relative pathname of a Smeagol Passwd file - see [[Security and authentication]]. This file must contain an entry for at least your initial user, and, if you want to administer users through the user interface, must be writable by the process which runs Smeagol.
### Custom configuration file
You can copy the standard configuration file `resources/config.edn` to somewhere outside the jar file, edit it to suit your installation, and set up a single environment variable, `SMEAGOL_CONFIG`, whose value is the path to your new configuration file.
**NOTE** that `SMEAGOL_CONTENT_DIR` must contain at least the following files:
### Environment variables
Alternatively, you can configure everything through [[Environment Variables]].
### Hybrid strategy
You can have both a configuration file and environment variables. If you do this, the environment variables override the values in the configuration file.
### Necessary content
**NOTE** that the directory at `SMEAGOL_CONTENT_DIR` must contain at least the following files:
1. `_edit-side-bar.md` - the side-bar that should be displayed when editing pages;
2. `_header.md` - the header to be displayed on all pages;
3. `_side-bar.md` - the side-bar that should be displayed when not editing pages.
Standard versions of these files can be found in the [source repository](https://github.com/journeyman-cc/smeagol/tree/master/resources/public/content). All these files should be in markdown format - see [[Extensible Markup]].
Standard versions of these files can be found in the [source repository](https://github.com/journeyman-cc/smeagol/tree/master/resources/public/content).
You can run the jar file with:
@ -32,29 +44,12 @@ To deploy Smeagol within a servlet container, either download the jar file for t
This will create a war file in the `target` directory, named `smeagol-`*VERSION*`-standalone.war`. Deploy this to your servlet container in the normal way; details will depend on your container. Instructions for Tomcat are [here](https://tomcat.apache.org/tomcat-8.0-doc/deployer-howto.html).
The problem with this is that unless the environment variables (see above) were already set up in the environment of the servlet container at the time when the servlet container were launched, Smeagol will run with its built-in defaults. This will run perfectly satisfactorily provided your servlet container is configured to unpack war files, which most are.
The problem with this is that unless the environment variables (see above) were already set up in the environment of the servlet container at the time when the servlet container were launched, Smeagol will run with its built-in defaults. If you want to change the defaults, you would have to edit the `resources/config.edn` file and recompile the war file.
Smeagol will run as a web-app with the default configuration perfectly satisfactorily.
## Experimental Docker image
You can now run Smeagol as a [Docker](http://www.docker.com) image. Read more about [[Using the Docker Image]].
You can now run Smeagol as a [Docker](http://www.docker.com) image. Read more about using the [[Docker 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.

View file

@ -0,0 +1,55 @@
Smeagol is available as a Docker image
To run my Docker image, use
docker run -p 127.0.0.1:80:80 simonbrooke/smeagol
Where 127.0.0.1 is the IP address through which you want to forward port 80 (in real life it wouldn't be 127.0.0.1, but that's safe for testing).
You can then browse to Smeagol by pointing your browser at http://localhost/.
As of version 0.99.10, the Docker image is now based on the Jetty, rather than the Tomcat, deployment of Smeagol (that is to say, it runs the executable jar file). This makes for a lighter weight Docker image. All configuration can be overridden with [[Environment Variables]], which can be passed into the Docker container when the image is invoked, or from a [[Configuration]] file.
The `config.edn` and `passwd` files and the `content` directory are copied into `/usr/local/etc` in the Docker image, and the appropriate environment variables are set up to point to them:
```
COPY resources/passwd /usr/local/etc/passwd
COPY resources/config.edn /usr/local/etc/config.edn
COPY resources/public/content /usr/local/etc/content
ENV SMEAGOL_CONFIG=/usr/local/etc/config.edn
ENV SMEAGOL_CONTENT_DIR=/usr/local/etc/content
ENV SMEAGOL_PASSWD=/usr/local/etc/passwd
```
This works for play purposes. However, it means that any edits made to either the `passwd` file or the `content` directory will be lost when the Docker image is shut down. You really need to have these resources copied to a place in a real file system which is mounted by the image. While I intend that by the 1.1.0 release of Smeagol it will be possible to configure a remote origin repository to which changes are periodically pushed, which will backup and preserve the content, this won't save the `passwd` file, as this is deliberately not stored in the git repository for security reasons.
## Mounting real file systems
It's possible to mount external file systems, and to override environment variables, with arguments to Docker's extraordinarily complex [run command](https://docs.docker.com/engine/reference/commandline/run/).
I'm currently working with a recipe:
docker run -p 127.0.0.1:80:80 -v ~/tmp/etc:/usr/local/etc simonbrooke/smeagol
Where:
1. `127.0.0.1` is the IP address on the real host on which you wish to serve;
2. `:80:80` maps port 80 on the image to port 80 on the specified IP address;
3. `~/tmp/etc` is the directory on the file system of the real host where files are stored;
4. `/usr/local/etc` is the directory within the image file system to which that will be mounted;
This works, and uses the default values of the environment variables which are set up in the Docker image. However, I'm very much prepared to believe there are better recipes.
## Status
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 unless you have mounted an external file store, 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.
## Building the Docker image
To build your own Docker image, run:
lein clean
lein bower install
lein ring uberjar
lein docker build
This will build a new Docker image locally; you can, obviously, push it to your own Docker repository if you wish.

View file

@ -0,0 +1,23 @@
## Smeagol-specific environment variables
Smeagol can be configured entirely with environment variables. The variables are:
1. `SMEAGOL_CONFIG` (optional but advised) should be the full or relative pathname of a Smeagol [[Configuration]] file;
2. `SMEAGOL_CONTENT_DIR` should be the full or relative pathname of the directory from which Smeagol should serve content (which may initially be empty, but must be writable by the process which runs Smeagol);
3. `SMEAGOL_DEFAULT_LOCALE` which should be a locale specification in the form "en-GB", "fr-FR", or whatever to suit your users;
4. `SMEAGOL_FORMATTERS` should be an [edn](https://github.com/edn-format/edn)-formatted map of formatter directives (this would be pretty hard to do from an environment variable);
5. `SMEAGOL_LOG_LEVEL` which should be one of `TRACE DEBUG INFO WARN ERROR FATAL`
6. `SMEAGOL_PASSWD` should be the full or relative pathname of a Smeagol Passwd file - see [[Security and authentication]]. This file must contain an entry for at least your initial user, and, if you want to administer users through the user interface, must be writable by the process which runs Smeagol.
7. `SMEAGOL_SITE_TITLE` which should be the title you want shown on the header of all pages.
You can have both a configuration file and environment variables; if you do, the values of the environment variables take precedence over the values in the config file.
## Other environment variables
If Smeagol is compiled as an executable jar file, the actual web server component is [Ring server](https://github.com/weavejester/ring-server). This recognises the `PORT` environment variable, and, if this is present and its value is a positive integer, will listen on the specified port (otherwise its default is 3000, which is... unusual).
Smeagol uses the [Timbre](https://github.com/ptaoussanis/timbre) logging library. This recognises the following environment variables:
1. `TIMBRE_DEFAULT_STACKTRACE_FONTS` Timbre by default colourises stacktrace dumps using ANSI terminal codes. This can be quite useful in a console, but is a real pain in a log file. To turn colourised stacktraces off, set the value of this to an empty string;
2. `TIMBRE_LEVEL` Sets the minimum logging level; but there are two problems with this. The first is that the environment variable is only read at compile time not at run time, and the second is that the syntax is a bit odd, which is why I've implemented `SMEAGOL_LOG_LEVEL` (above);
3. `TIMBRE_NS_WHITELIST` Sets a list of [Clojure namespaces](https://clojure.org/reference/namespaces) from which messages should be logged; however this is only read at compile time so isn't much use in practice;
4. `TIMBRE_NS_BLACKLIST` As above, but sets a list of namespaces from which messages should **not** be logged.

View file

@ -17,6 +17,9 @@ Smeagol uses the Markdown format as provided by [markdown-clj](https://github.co
## 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]].
## 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 to other images already available on the web, like this:

View file

@ -1,4 +1,5 @@
* [[Introduction]]
* [[Change log]]
* [[User Documentation]]
* [[Deploying Smeagol]]
* [[Developing Smeagol]]

View file

@ -265,6 +265,14 @@ th {
padding: 0 2em 0 0;
}
.sanity-cause .sanity-stacktrace {
display: none;
}
.sanity-cause:hover .sanity-stacktrace {
display: block;
}
.vega-bindings, .vega-actions {
font-size: 66%;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

View file

@ -4,6 +4,7 @@
(:require [crypto.password.scrypt :as password]
[environ.core :refer [env]]
[noir.io :as io]
[smeagol.configuration :refer [config]]
[taoensso.timbre :as timbre]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -37,8 +38,8 @@
;; the relative path to the password file.
(def password-file-path
(or
(env :smeagol-passwd)
(str (clojure.java.io/resource "passwd"))))
(:passwd config)
(str (io/resource-path) "../passwd")))
(defn- get-users
@ -112,7 +113,7 @@
(timbre/info (str "Successfully changed password for user " username))
true))
(catch Exception any
(timbre/error
(timbre/error any
(format "Changing password failed for user %s failed: %s (%s)"
username (.getName (.getClass any)) (.getMessage any)))
false))))
@ -162,7 +163,7 @@
(timbre/info "Successfully added user " username)
true)
(catch Exception any
(timbre/error
(timbre/error any
(format "Adding user %s failed: %s (%s)"
username (.getName (.getClass any)) (.getMessage any)))
false)))))
@ -179,7 +180,7 @@
(timbre/info (str "Successfully deleted user " username))
true)
(catch Exception any
(timbre/error
(timbre/error any
(format "Deleting user %s failed: %s (%s)"
username (.getName (.getClass any)) (.getMessage any)))
false))))

View file

@ -1,7 +1,9 @@
(ns ^{:doc "Read and make available configuration."
:author "Simon Brooke"}
smeagol.configuration
(:require [environ.core :refer [env]]
(:require [clojure.pprint :refer [pprint]]
[clojure.string :as s]
[environ.core :refer [env]]
[noir.io :as io]
[taoensso.timbre :as timbre]))
@ -37,7 +39,6 @@
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def config-file-path
"The relative path to the config file."
(or
@ -45,10 +46,92 @@
(str (io/resource-path) "../config.edn")))
(def config
"The actual configuration, as a map."
(defn- from-env-vars
"Read a map from those of these environment variables which have values"
[& vars]
(reduce
#(let [v (env %2)]
(if v (assoc %1 %2 v) %1))
{}
vars))
(defn to-keyword
"Convert this argument into an idiomatic clojure keyword."
[arg]
(if (and arg (not (keyword? arg)))
(keyword
(s/lower-case
(s/replace (str arg) #"[^A-Za-z0-9]+" "-")))
arg))
(defn transform-map
"transform this map `m` by applying these `transforms`. Each transforms
is expected to comprise a map with the keys :from and :to, whose values
are respectively a key to match and a key to replace that match with,
and optionally a key :transform, whose value is a function of one
argument to be used to transform the value of that key."
[m tuples]
(timbre/debug
"transform-map:\n"
(with-out-str (clojure.pprint/pprint m)))
(reduce
(fn [m tuple]
(if
(and (map? tuple) (map? m) (m (:from tuple)))
(let [old-val (m (:from tuple))
t (:transform tuple)]
(assoc
(dissoc m (:from tuple))
(:to tuple)
(if-not
(nil? t)
(eval (list t old-val)) old-val)))
m))
m
tuples))
(def config-env-transforms
"Transforms to use with `transform-map` to convert environment
variable names (which need to be specific) into the shorter names
used internally"
'( {:from :smeagol-content-dir :to :content-dir}
{:from :smeagol-default-locale :to :default-locale}
{:from :smeagol-formatters :to :formatters :transform read-string}
{: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
[]
"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
(let [file-contents (try
(read-string (slurp config-file-path))
(catch Exception _ {}))
config (merge
file-contents
(transform-map
(from-env-vars
:smeagol-content-dir
:smeagol-default-locale
:smeagol-formatters
:smeagol-log-level
:smeagol-passwd
:smeagol-site-title)
config-env-transforms))]
(if (env :dev)
(timbre/debug
"Loaded configuration\n"
(with-out-str (clojure.pprint/pprint config))))
config)
(catch Exception any
(timbre/error "Could not load configuration" any)
(timbre/error any "Could not load configuration")
{})))
(def config (build-config))

View file

@ -2,6 +2,7 @@
:author "Simon Brooke"}
smeagol.handler
(:require [clojure.java.io :as cjio]
[clojure.string :refer [lower-case]]
[compojure.core :refer [defroutes]]
[compojure.route :as route]
[cronj.core :as cronj]
@ -11,6 +12,7 @@
[noir.util.middleware :refer [app-handler]]
[ring.middleware.defaults :refer [site-defaults]]
[selmer.parser :as parser]
[smeagol.configuration :refer [config]]
[smeagol.routes.wiki :refer [wiki-routes]]
[smeagol.middleware :refer [load-middleware]]
[smeagol.session-manager :as session-manager]
@ -43,6 +45,7 @@
(defn user-access [request]
(session/get :user))
(defroutes base-routes
(route/resources "/")
(route/not-found "Not Found"))
@ -69,14 +72,18 @@
{:rotor (rotor/rotor-appender
{:path "smeagol.log"
:max-size (* 512 1024)
:backlog 10})}})
:backlog 10})}
:level (or
(:log-level config)
(if (env :dev) :debug)
:info)})
(cronj/start! session-manager/cleanup-job)
(if (env :dev) (parser/cache-off!))
;;start the expired session cleanup job
(timbre/info "\n-=[ smeagol started successfully"
(when (env :dev) "using the development profile") "]=-")
(catch Exception any
(timbre/error "Failure during startup" any)
(timbre/error any "Failure during startup")
(destroy))))
;; timeout sessions after 30 minutes
@ -90,6 +97,7 @@
[xss-protection?]
(-> site-defaults
(update-in [:session] merge session-defaults)
(dissoc :static)
(assoc-in [:security :anti-forgery] xss-protection?)))

61
src/smeagol/include.clj Normal file
View file

@ -0,0 +1,61 @@
(ns ^{:doc "Functions related to the include of markdown-paged in a given markdown."
:author "Michael Jerger"}
smeagol.include
(:require
[clojure.string :as cs]
[schema.core :as s]
[com.stuartsierra.component :as component]
[smeagol.include.parse :as parse]
[smeagol.include.resolve :as resolve]
[smeagol.include.indent :as indent]))
(s/defrecord Includer
[resolver])
(defprotocol IncludeMd
(expand-include-md
[includer md-src]
"return a markdown containing resolved includes"))
(s/defn
do-expand-one-include :- s/Str
[includer :- Includer
include :- parse/IncludeLink
md-src :- s/Str]
(let [{:keys [uri replace indent-heading indent-list]} include]
(cs/replace-first
md-src
(re-pattern (cs/escape
replace
{\[ "\\["
\] "\\]"
\( "\\("
\) "\\)"}))
(indent/do-indent-list
indent-list
(indent/do-indent-heading
indent-heading
(resolve/resolve-md (:resolver includer) uri))))))
(s/defn
do-expand-includes :- s/Str
[includer :- Includer
includes :- [parse/IncludeLink]
md-src :- s/Str]
(loop [loop-includes includes
result md-src]
(if (empty? loop-includes)
result
(recur
(rest loop-includes)
(do-expand-one-include includer (first loop-includes) result)))))
(extend-type Includer
IncludeMd
(expand-include-md [includer md-src]
(do-expand-includes includer (parse/parse-include-md md-src) md-src)))
(s/defn
new-includer
[]
(map->Includer {}))

View file

@ -0,0 +1,58 @@
(ns ^{:doc "Functions related to the include of markdown-paged - handling the
list & heading indents of includes. This namespaces is implementation detail for
smeagol.include and not inteded for direct usage."
:author "Michael Jerger"}
smeagol.include.indent
(:require
[clojure.string :as cs]
[schema.core :as s]))
(s/defn
parse-list
[md-resolved :- s/Str]
(distinct
(into
(re-seq #"((^|\R? *)([\*\+-] ))" md-resolved)
(re-seq #"((^|\R? *)([0-9]+\. ))" md-resolved))))
(s/defn
parse-heading
[md-resolved :- s/Str]
(distinct
(re-seq #"((^|\R?)(#+ ))" md-resolved)))
(s/defn
do-indent :- s/Str
[indent :- s/Num
indentor :- s/Str
elements
md-resolved :- s/Str]
(loop [result md-resolved
elements elements]
(if (empty? elements)
result
(let [element (first elements)
replace (nth element 1)
start (nth element 2)
end (nth element 3)]
(recur
(cs/replace
result
(re-pattern (cs/escape
replace
{\* "\\*"
\n "\\n"}))
(str start (apply str (repeat indent indentor)) end))
(rest elements))))))
(s/defn
do-indent-heading :- s/Str
[indent :- s/Num
md-resolved :- s/Str]
(do-indent indent "#" (parse-heading md-resolved) md-resolved))
(s/defn
do-indent-list :- s/Str
[indent :- s/Num
md-resolved :- s/Str]
(do-indent indent " " (parse-list md-resolved) md-resolved))

View file

@ -0,0 +1,50 @@
(ns ^{:doc "Functions related to the include of markdown-paged - parsing of
include links. This namespaces is implementation detail for
smeagol.include and not inteded for direct usage."
:author "Michael Jerger"}
smeagol.include.parse
(:require
[schema.core :as s]))
(def IncludeLink
{:replace s/Str
:uri s/Str
:indent-heading s/Num
:indent-list s/Num})
(s/defn
convert-indent-to-int :- s/Num
[indents :- [s/Str]]
(if (some? indents)
(Integer/valueOf (nth indents 2))
0))
(s/defn
parse-indent-list
[md-src :- s/Str]
(re-matches #".*(:indent-list (\d)).*" md-src))
(s/defn
parse-indent-heading
[md-src :- s/Str]
(re-matches #".*(:indent-heading (\d)).*" md-src))
(s/defn
parse-include-link
[md-src :- s/Str]
(re-seq #".*(&\[\w*(.*)\w*\]\((.*)\)).*" md-src))
(s/defn
parse-include-md :- [IncludeLink]
[md-src :- s/Str]
(vec
(map
(fn [parse-element]
(let [replace (nth parse-element 1)
uri (nth parse-element 3)
indents-text (nth parse-element 2)]
{:replace replace
:uri uri
:indent-heading (convert-indent-to-int (parse-indent-heading indents-text))
:indent-list (convert-indent-to-int (parse-indent-list indents-text))}))
(parse-include-link md-src))))

View file

@ -0,0 +1,46 @@
(ns ^{:doc "Functions related to the include of markdown-paged - providing
a plugable load-content componet. This namespaces is implementation detail for
smeagol.include and not inteded for direct usage."
:author "Michael Jerger"}
smeagol.include.resolve
(:require
[schema.core :as s]
[com.stuartsierra.component :as component]))
(s/defrecord Resolver
[type :- s/Keyword
local-base-dir :- s/Str])
;As schema doesn't support s/defprotocol we use the dispatcher for annotation & validation.
(s/defn dispatch-by-resolver-type :- s/Keyword
"Dispatcher for different resolver implementations."
[resolver :- Resolver
uri :- s/Str]
(:type resolver))
(defmulti do-resolve-md
"Multimethod return a markdown file content for given uri."
dispatch-by-resolver-type)
(s/defmethod do-resolve-md :default
[resolver :- Resolver
uri :- s/Str]
(throw (Exception. (str "No implementation for " resolver))))
(defprotocol ResolveMd
(resolve-md
[resolver uri]
"return a markfown file content for given uri."))
(extend-type Resolver
ResolveMd
(resolve-md [resolver uri]
(s/validate s/Str uri)
(s/validate s/Str (do-resolve-md resolver uri))))
(s/defn
new-resolver
([type :- s/Keyword]
(map->Resolver {:type type :local-base-dir nil}))
([type :- s/Keyword
local-base-dir :- s/Str]
(map->Resolver {:type type :local-base-dir local-base-dir})))

View file

@ -0,0 +1,27 @@
(ns ^{:doc "Functions related to the include of markdown-paged - providing
a plugable load-local-include-links componet. This namespaces is implementation detail for
smeagol.include and not inteded for direct usage."
:author "Michael Jerger"}
smeagol.include.resolve-local-file
(:require
[schema.core :as s]
[smeagol.include.resolve :as resolve]
[com.stuartsierra.component :as component]
[clojure.java.io :as cjio]
[taoensso.timbre :as timbre]))
(s/defmethod resolve/do-resolve-md :local-file
[resolver
uri :- s/Str]
(let [file-name uri
file-path (cjio/file (:local-base-dir resolver) file-name)
exists? (.exists (clojure.java.io/as-file file-path))]
(cond exists?
(do
(timbre/info (format "Including page '%s' from file '%s'" uri file-path))
(slurp file-path)))))
(s/defn
new-resolver
[local-base-dir :- s/Str]
(resolve/new-resolver :local-file local-base-dir))

View file

@ -6,7 +6,12 @@
[selmer.middleware :refer [wrap-error-page]]
[prone.middleware :refer [wrap-exceptions]]
[ring.middleware.anti-forgery :refer [wrap-anti-forgery]]
[noir-exception.core :refer [wrap-internal-error]]))
[ring.middleware.file :refer [wrap-file]]
[ring.middleware.resource :refer [wrap-resource]]
[ring.middleware.content-type :refer [wrap-content-type]]
[ring.middleware.not-modified :refer [wrap-not-modified]]
[noir-exception.core :refer [wrap-internal-error]]
[smeagol.util :as util]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
@ -44,7 +49,12 @@
(def production-middleware
[#(wrap-internal-error % :log (fn [e] (timbre/error e)))])
[#(wrap-internal-error % :log (fn [e] (timbre/error e)))
#(wrap-resource % "public")
#(wrap-file % util/content-dir
{:index-files? false :prefer-handler? true})
#(wrap-content-type %)
#(wrap-not-modified %)])
(defn load-middleware []

View file

@ -98,7 +98,7 @@
:details details
:users (auth/list-users)})))
(catch Exception any
(timbre/error (.getMessage any))
(timbre/error any)
(layout/render "edit-user.html"
(merge (util/standard-params request)
{:title (str (:edit-title-prefix (util/get-messages request)) " " (:target params))

View file

@ -20,7 +20,10 @@
[smeagol.sanity :refer [show-sanity-check-error]]
[smeagol.util :as util]
[smeagol.uploads :as ul]
[taoensso.timbre :as timbre]))
[taoensso.timbre :as timbre]
[com.stuartsierra.component :as component]
[smeagol.include.resolve-local-file :as resolve]
[smeagol.include :as include]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
@ -45,23 +48,17 @@
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn get-git-repo
"Get the git repository for my content, creating it if necessary"
[]
(hist/load-or-init-repo util/content-dir))
(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 request]
(timbre/trace (format "process-source: '%s'" request))
(let [source-text (:src params)
page (:page params)
file-name (str page suffix)
file-path (cjio/file util/content-dir file-name)
exists? (.exists (cjio/as-file file-path))
git-repo (get-git-repo)
git-repo (hist/load-or-init-repo util/content-dir)
user (session/get :user)
email (auth/get-email user)
summary (format "%s: %s" user (or (:summary params) "no summary"))]
@ -115,13 +112,22 @@
(edit-page request "stylesheet" ".css" "edit-css.html" "_edit-side-bar.md"))
(def md-include-system
(component/start
(component/system-map
:resolver (resolve/new-resolver util/content-dir)
:includer (component/using
(include/new-includer)
[:resolver]))))
(defn wiki-page
"Render the markdown page specified in this `request`, if any. If none found, redirect to edit-page"
[request]
(timbre/trace (format "wiki-page: '%s'" request))
(or
(show-sanity-check-error)
(let [params (keywordize-keys (:params request))
page (or (:page params) (util/get-message :default-page-title "Introduction" request))
page (or (:page params) util/start-page (util/get-message :default-page-title "Introduction" request))
file-name (str page ".md")
file-path (cjio/file util/content-dir file-name)
exists? (.exists (clojure.java.io/as-file file-path))]
@ -132,7 +138,10 @@
(merge (util/standard-params request)
{:title page
:page page
:content (md->html (slurp file-path))
:content (md->html
(include/expand-include-md
(:includer md-include-system)
(slurp file-path)))
:editable true})))
true (response/redirect (str "/edit?page=" page))))))
@ -148,7 +157,7 @@
(timbre/info (format "Showing history of page '%s'" page))
(layout/render "history.html"
(merge (util/standard-params request)
{:title (str "History of " page)
{:title (util/get-message :history-title-prefix request)
:page page
:history (hist/find-history repo-path file-name)}))))
@ -156,8 +165,8 @@
"Render a form to allow the upload of a file."
[request]
(let [params (keywordize-keys (:params request))
data-path (str (io/resource-path) "/content/uploads/")
git-repo (get-git-repo)
data-path (str util/content-dir "/content/uploads/")
git-repo (hist/load-or-init-repo util/content-dir)
upload (:upload params)
uploaded (if upload (ul/store-upload params data-path))
user (session/get :user)

View file

@ -1,4 +1,5 @@
(ns ^{:doc "Functions related to sanity checks and error reporting in conditions where the environment may not be sane."
(ns ^{:doc "Functions related to sanity checks and error reporting in conditions
where the environment may not be sane."
:author "Simon Brooke"}
smeagol.sanity
(:import (java.util Locale))
@ -252,13 +253,28 @@
(as-hiccup [this dictionary] "")
clojure.lang.Keyword
(as-hiccup [this dictionary] (str (or (this dictionary)(string/replace (name this) "-" " ")) " "))
(as-hiccup [this dictionary]
(str
(or
(this dictionary)
(string/replace (name this) "-" " "))
" "))
clojure.lang.PersistentList
(as-hiccup [this dictionary] (apply vector (cons :div (map #(as-hiccup % dictionary) this))))
(as-hiccup [this dictionary]
(apply
vector
(cons
:div
(map #(as-hiccup % dictionary) this))))
clojure.lang.PersistentVector
(as-hiccup [this dictionary] (apply vector (cons :div (map #(as-hiccup % dictionary) this))))
(as-hiccup [this dictionary]
(apply
vector
(cons
:div
(map #(as-hiccup % dictionary) this))))
clojure.lang.PersistentArrayMap
(as-hiccup [this dictionary]
@ -302,7 +318,7 @@
(fn [x]
[:div
{:class "sanity-cause"}
(.getMessage x)
[:h2 (.getMessage x)]
[:div {:class "sanity-stacktrace"}
(apply
vector
@ -317,34 +333,43 @@
(as-hiccup [this dictionary] (str this " ")))
(defn sanity-check-report
[problems]
(defn get-locale-messages
"Get messages for the server-side locale."
[]
(let [locale (Locale/getDefault)
locale-specifier (str (.getLanguage locale) "-" (.getCountry locale))
messages (try
locale-specifier (str (.getLanguage locale) "-" (.getCountry locale))]
(try
(i18n/get-messages locale-specifier "i18n" "en-GB")
(catch Exception any {}))]
(html
[:html
(catch Exception any {}))))
;; Prepackaged hiccup sub-units
(defn as-hiccup-head
[messages]
[:head
[:title (as-hiccup :smeagol-not-initialised messages)]
[:link {:href "/content/stylesheet.css" :rel "stylesheet"}]]
[:body
[:link {:href "/content/stylesheet.css" :rel "stylesheet"}]])
(defn as-hiccup-header
[messages]
[:header
[:div {:id "nav"} "&nbsp;"]
[:h1 (as-hiccup :smeagol-not-initialised messages)]
[:p "&nbsp;"]]
[:div {:id "error" :class "error"}
[:div {:class "error"}
(as-hiccup [(count (keys problems)) :problems-found] messages)]]
[:div {:id "main-container" :class "sanity-check-report"}
[:p (as-hiccup :smeagol-misconfiguration messages)]
(as-hiccup problems messages)
[:p "&nbsp;"]])
(defn as-hiccup-see-doc
[messages]
[:p (as-hiccup :see-documentation messages)
[:a
{:href
"https://github.com/journeyman-cc/smeagol/blob/master/resources/public/content/Deploying%20Smeagol.md"}
(as-hiccup :here messages)]]]
"https://github.com/journeyman-cc/smeagol/wiki/Deploying-Smeagol"}
(as-hiccup :here messages)] "."])
(defn as-hiccup-footer
[messages]
[:footer
[:div {:id "credits"}
[:div
@ -355,7 +380,26 @@
:alt "The Web Engineering Factory &amp; Toolworks"
:src "http://www.weft.scot/images/weft.logo.64.png"}]
" Developed by "
[:a {:href "http://www.weft.scot/"}"WEFT"]]]]]])))
[:a {:href "http://www.weft.scot/"}"WEFT"]]]])
(defn sanity-check-report
"Convert this `problem` report into a nicely formatted HTML page"
[problems]
(let [messages (get-locale-messages)]
(html
[:html
(as-hiccup-head messages)
[:body
(as-hiccup-header messages)
[:div {:id "error"}
[:p {:class "error"}
(rest (as-hiccup [(count (keys problems)) :problems-found] messages))]]
[:div {:id "main-container" :class "sanity-check-report"}
[:p (as-hiccup :smeagol-misconfiguration messages)]
(as-hiccup problems messages)
(as-hiccup-see-doc messages)]
(as-hiccup-footer messages)]])))
(defn- raw-sanity-check-installation
@ -369,7 +413,7 @@
(timbre/warn "Sanity check completed; " (count (keys result)) " problem(s) found")
(sanity-check-report result))
(do
(timbre/info "Sanity check completed; no problem(s) found")
(timbre/info "Sanity check completed; no problems found")
nil))))
@ -383,26 +427,26 @@
If no argument is passed, run the sanity check and if it fails return page contents;
if `error` is passed, just return page content describing the error."
([error]
(let [messages (get-locale-messages)]
(html
[:html
[:head
[:title "Smeagol is not initialised correctly"]
[:link {:href "/content/stylesheet.css" :rel "stylesheet"}]]
(as-hiccup-head messages)
[:body
[:header
[:h1 "Smeagol is not initialised correctly"]]
(as-hiccup-header messages)
[:div {:id "error"}
[:p {:class "error"} (.getMessage error)]]
[:p "There was a problem launching Smeagol probably because of misconfiguration:"]
(apply
vector
(cons :ol
(map #(vector :li (.getMessage %))
(get-causes error))))
[:p :see-documentation
[:a {:href "https://github.com/journeyman-cc/smeagol/blob/develop/resources/public/content/Deploying%20Smeagol.md"} "here"]]]]))
[:div {:id "main-container" :class "sanity-check-report"}
[:p (as-hiccup :smeagol-misconfiguration messages)]
(as-hiccup error messages)
(as-hiccup-see-doc messages)]
(as-hiccup-footer messages)]])))
([]
(try
(sanity-check-installation)
(catch Exception any (show-sanity-check-error any)))))
(catch Exception any
(timbre/error any "Failure during sanity check")
(show-sanity-check-error any)))))
(show-sanity-check-error (Exception. "That's insane!"))

View file

@ -56,6 +56,8 @@
filename (:filename upload)]
(timbre/info
(str "Storing upload file: " upload))
(timbre/debug
(str "store-upload mv file: " tmp-file " to: " path filename))
(if tmp-file
(do
(.renameTo tmp-file

View file

@ -8,7 +8,8 @@
[scot.weft.i18n.core :as i18n]
[smeagol.authenticate :as auth]
[smeagol.configuration :refer [config]]
[smeagol.formatting :refer [md->html]]))
[smeagol.formatting :refer [md->html]]
[taoensso.timbre :as timbre]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
@ -34,9 +35,12 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def start-page
(:start-page config))
(def content-dir
(or
(env :smeagol-content-dir)
(:content-dir config)
(cjio/file (io/resource-path) "content")))
@ -55,12 +59,19 @@
"Return the most acceptable messages collection we have given the
`Accept-Language` header in this `request`."
[request]
(let [specifier ((:headers request) "accept-language")
messages (try
(i18n/get-messages specifier "i18n" "en-GB")
(catch Exception any
(timbre/error
any
(str
"Failed to parse accept-language header "
specifier))
{}))]
(merge
(i18n/get-messages
((:headers request) "accept-language")
"i18n"
"en-GB")
config))
messages
config)))
(def get-messages (memoize raw-get-messages))

View file

@ -0,0 +1,106 @@
(ns smeagol.test.include
(:require [clojure.test :refer :all]
[schema.core :as s]
[com.stuartsierra.component :as component]
[smeagol.include.resolve :as resolve]
[smeagol.include :as sut]))
(def include-simple
"# Heading1
&[](./simple.md)")
(def include-surounding-simple
"# Heading1
Some surounding &[](./simple.md) text")
(def include-heading-0
"# Heading1
&[:indent-heading 0](./with-heading.md)")
(def include-heading-list-1
"# Heading1
&[:indent-heading 1 :indent-list 1](./with-heading-and-list.md)")
(def include-heading-list-0
"# Heading1
&[:indent-list 0 :indent-heading 0](./with-heading-and-list.md)")
(def include-invalid-indent
"# Heading1
&[ invalid input should default to indent 0 ](./simple.md)")
(def include-spaced-indent
"# Heading1
&[ :indent-heading 2 :indent-list 33 ](./with-heading-and-list.md)")
(def multi
"# Heading1
&[ :indent-heading 2 :indent-list 33 ](./with-heading-and-list.md)
some text
&[](./simple.md)
more text.")
(s/defmethod resolve/do-resolve-md :test-mock
[resolver
uri :- s/Str]
(cond
(= uri "./simple.md") "Simple content."
(= uri "./with-heading-and-list.md") "# Heading2
some text
* List
## Heading 3
more text"))
(def system-under-test
(component/start
(component/system-map
:resolver (resolve/new-resolver :test-mock)
:includer (component/using
(sut/new-includer)
[:resolver]))))
(deftest test-expand-include-md
(testing "The whole integration of include"
(is
(= "# Heading"
(sut/expand-include-md (:includer system-under-test) "# Heading")))
(is
(= "# Heading1
Simple content."
(sut/expand-include-md
(:includer system-under-test)
include-simple)))
(is
(= "# Heading1
Some surounding Simple content. text"
(sut/expand-include-md
(:includer system-under-test)
include-surounding-simple)))
(is
(= "# Heading1
# Heading2
some text
* List
## Heading 3
more text"
(sut/expand-include-md
(:includer system-under-test)
include-heading-list-0)))
(is
(= "# Heading1
### Heading2
some text
* List
#### Heading 3
more text
some text
Simple content.
more text."
(sut/expand-include-md
(:includer system-under-test)
multi)))))

View file

@ -0,0 +1,35 @@
(ns smeagol.test.include.indent
(:require [clojure.test :refer :all]
[smeagol.include.indent :as sut]))
(deftest test-parse-heading
(testing
(is (= '(["# " "# " "" "# "])
(sut/parse-heading "# h1")))
(is (= '(["\n# " "\n# " "\n" "# "])
(sut/parse-heading "\n# h1")))))
(deftest test-indent-heading
(testing
(is (= "# h1"
(sut/do-indent-heading 0 "# h1")))
(is (= "### h1"
(sut/do-indent-heading 2 "# h1")))
(is (= "\n### h1"
(sut/do-indent-heading 2 "\n# h1")))))
(deftest test-parse-list
(testing
(is (= '([" * " " * " " " "* "])
(sut/parse-list " * list")))
(is (= '(["\n * " "\n * " "\n " "* "])
(sut/parse-list "\n * list")))))
(deftest test-indent-list
(testing
(is (= " * list"
(sut/do-indent-list 0 " * list")))
(is (= " * list"
(sut/do-indent-list 2 " * list")))
(is (= "\n * list"
(sut/do-indent-list 2 "\n * list")))))

View file

@ -0,0 +1,91 @@
(ns smeagol.test.include.parse
(:require [clojure.test :refer :all]
[schema.core :as s]
[smeagol.include.parse :as sut]))
(def include-simple
"# Heading1
&[](./simple.md)")
(def include-surounding-simple
"# Heading1
Some surounding &[](./simple.md) text")
(def include-heading-0
"# Heading1
&[:indent-heading 0](./with-heading.md)")
(def include-heading-list-1
"# Heading1
&[:indent-heading 1 :indent-list 1](./with-heading-and-list.md)")
(def include-heading-list-0
"# Heading1
&[:indent-list 0 :indent-heading 0](./with-heading-and-list.md)")
(def include-invalid-indent
"# Heading1
&[ invalid input should default to indent 0 ](./simple.md)")
(def include-spaced-indent
"# Heading1
&[ :indent-heading 2 :indent-list 33 ](./with-heading-and-list.md)")
(def multi
"# Heading1
&[ :indent-heading 2 :indent-list 33 ](./with-heading-and-list.md)
some text
&[](./simple.md)
more text.")
(deftest test-parse-include-md
(testing "parse include links"
(is
(= []
(sut/parse-include-md "# Heading")))
(is
(= [{:replace "&[](./simple.md)" :uri "./simple.md", :indent-heading 0, :indent-list 0}]
(sut/parse-include-md
include-simple)))
(is
(= [{:replace "&[](./simple.md)" :uri "./simple.md", :indent-heading 0, :indent-list 0}]
(sut/parse-include-md
include-surounding-simple)))
(is
(= [{:replace "&[:indent-heading 0](./with-heading.md)" :uri "./with-heading.md", :indent-heading 0, :indent-list 0}]
(sut/parse-include-md
include-heading-0)))
(is
(= [{:replace
"&[:indent-heading 1 :indent-list 1](./with-heading-and-list.md)"
:uri "./with-heading-and-list.md", :indent-heading 1, :indent-list 1}]
(sut/parse-include-md
include-heading-list-1)))
(is
(= [{:replace
"&[:indent-list 0 :indent-heading 0](./with-heading-and-list.md)"
:uri "./with-heading-and-list.md", :indent-heading 0, :indent-list 0}]
(sut/parse-include-md
include-heading-list-0)))
(is
(= [{:replace
"&[ invalid input should default to indent 0 ](./simple.md)"
:uri "./simple.md", :indent-heading 0, :indent-list 0}]
(sut/parse-include-md
include-invalid-indent)))
(is
(= [{:replace
"&[ :indent-heading 2 :indent-list 33 ](./with-heading-and-list.md)"
:uri "./with-heading-and-list.md", :indent-heading 2, :indent-list 3}]
(sut/parse-include-md
include-spaced-indent)))
(is
(= [{:replace
"&[ :indent-heading 2 :indent-list 33 ](./with-heading-and-list.md)"
:uri "./with-heading-and-list.md",
:indent-heading 2,
:indent-list 3}
{:replace "&[](./simple.md)" :uri "./simple.md", :indent-heading 0, :indent-list 0}]
(sut/parse-include-md
multi)))))

View file

@ -0,0 +1,8 @@
(ns smeagol.test.include.resolve
(:require [clojure.test :refer :all]
[smeagol.include.resolve :as sut]))
(deftest test-local-links
(testing "Rewriting of local links"
(is (thrown? Exception
(sut/resolve-md (sut/new-resolver (:default)) "./some-uri.md")))))