Add per-tag browsing on gopher
This commit is contained in:
parent
4a5228a380
commit
142b84ab78
2
Makefile
2
Makefile
|
@ -3,7 +3,7 @@ LISP= ecl
|
|||
all: dirs html
|
||||
|
||||
html: $(HTML) css
|
||||
$(LISP) -load generator.lisp
|
||||
$(LISP) --load generator.lisp
|
||||
|
||||
dirs:
|
||||
mkdir -p "output/html/static"
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
:gopher-path "/user" ;; absolute path of your gopher directory
|
||||
:gopher-server "my.website" ;; hostname of the gopher server
|
||||
:gopher-port "70" ;; tcp port of the gopher server, 70 usually
|
||||
:gopher-format "[0|~a|~a/article-~d.txt|~a|~a]~%~%" ;; menu format (geomyidae)
|
||||
:gopher-format "[~d|~a|~a|~a|~a]~%" ;; menu format (geomyidae)
|
||||
:gopher-index "index.gph" ;; menu file (geomyidae)
|
||||
;; :gopher-format "0~a ~a/article-~d.txt ~a ~a~%~%" ;; menu format (gophernicus and others)
|
||||
;; :gopher-format "~d~a ~a ~a ~a~%" ;; menu format (gophernicus and others)
|
||||
;; :gopher-index "gophermap" ;; menu file (gophernicus and others)
|
||||
))
|
||||
|
||||
|
|
143
generator.lisp
143
generator.lisp
|
@ -45,22 +45,22 @@
|
|||
(push (make-article :title title
|
||||
:tag tag
|
||||
:date (date-parse date)
|
||||
:rawdate date
|
||||
:rawdate date
|
||||
:tiny tiny
|
||||
:author author
|
||||
:id id
|
||||
:converter converter)
|
||||
:converter converter)
|
||||
*articles*))
|
||||
|
||||
;; we add a converter to the list of the one availables
|
||||
(defun converter(&optional &key name command extension)
|
||||
(setf *converters*
|
||||
(append
|
||||
(list name
|
||||
(make-converter :name name
|
||||
:command command
|
||||
:extension extension))
|
||||
*converters*)))
|
||||
(append
|
||||
(list name
|
||||
(make-converter :name name
|
||||
:command command
|
||||
:extension extension))
|
||||
*converters*)))
|
||||
|
||||
;; load data from metadata and load config
|
||||
(load "data/articles.lisp")
|
||||
|
@ -70,32 +70,32 @@
|
|||
;; common-lisp don't have a replace string function natively
|
||||
(defun replace-all (string part replacement &key (test #'char=))
|
||||
(with-output-to-string (out)
|
||||
(loop with part-length = (length part)
|
||||
for old-pos = 0 then (+ pos part-length)
|
||||
for pos = (search part string
|
||||
:start2 old-pos
|
||||
:test test)
|
||||
do (write-string string out
|
||||
:start old-pos
|
||||
:end (or pos (length string)))
|
||||
when pos do (write-string replacement out)
|
||||
while pos)))
|
||||
(loop with part-length = (length part)
|
||||
for old-pos = 0 then (+ pos part-length)
|
||||
for pos = (search part string
|
||||
:start2 old-pos
|
||||
:test test)
|
||||
do (write-string string out
|
||||
:start old-pos
|
||||
:end (or pos (length string)))
|
||||
when pos do (write-string replacement out)
|
||||
while pos)))
|
||||
|
||||
;; common-lisp don't have a split string function natively
|
||||
(defun split-str(text &optional (separator #\Space))
|
||||
"this function split a string with separator and return a list"
|
||||
(let ((text (concatenate 'string text (string separator))))
|
||||
(loop for char across text
|
||||
counting char into count
|
||||
when (char= char separator)
|
||||
collect
|
||||
;; we look at the position of the left separator from right to left
|
||||
(let ((left-separator-position (position separator text :from-end t :end (- count 1))))
|
||||
(subseq text
|
||||
;; if we can't find a separator at the left of the current, then it's the start of
|
||||
;; the string
|
||||
(if left-separator-position (+ 1 left-separator-position) 0)
|
||||
(- count 1))))))
|
||||
counting char into count
|
||||
when (char= char separator)
|
||||
collect
|
||||
;; we look at the position of the left separator from right to left
|
||||
(let ((left-separator-position (position separator text :from-end t :end (- count 1))))
|
||||
(subseq text
|
||||
;; if we can't find a separator at the left of the current, then it's the start of
|
||||
;; the string
|
||||
(if left-separator-position (+ 1 left-separator-position) 0)
|
||||
(- count 1))))))
|
||||
|
||||
;; load a file as a string
|
||||
;; we escape ~ to avoid failures with format
|
||||
|
@ -175,6 +175,30 @@
|
|||
`(progn
|
||||
(save-file ,name (generate-layout ,@data))))
|
||||
|
||||
;; generate a gopher index file
|
||||
(defun generate-gopher-index(articles)
|
||||
(let ((output (load-file "templates/gopher_head.tpl")))
|
||||
(dolist (article articles)
|
||||
(setf output
|
||||
(string
|
||||
(concatenate 'string output
|
||||
(format nil (getf *config* :gopher-format)
|
||||
0 ;;;; gopher type, 0 for text files
|
||||
;; here we create a 80 width char string with title on the left
|
||||
;; and date on the right
|
||||
;; we truncate the article title if it's too large
|
||||
(let ((title (format nil "~80a"
|
||||
(if (< 80 (length (article-title article)))
|
||||
(subseq (article-title article) 0 80)
|
||||
(article-title article)))))
|
||||
(replace title (article-rawdate article) :start1 (- (length title) (length (article-rawdate article)))))
|
||||
(concatenate 'string
|
||||
(getf *config* :gopher-path) "/article-" (article-id article) ".txt")
|
||||
(getf *config* :gopher-server)
|
||||
(getf *config* :gopher-port)
|
||||
)))))
|
||||
output))
|
||||
|
||||
;; generate the list of tags
|
||||
(defun articles-by-tag()
|
||||
(let ((tag-list))
|
||||
|
@ -243,7 +267,7 @@
|
|||
;; html generation of a tag homepage
|
||||
(defun generate-tag-mainpage(articles-in-tag)
|
||||
(apply #'concatenate 'string
|
||||
(loop for article in *articles*
|
||||
(loop for article in *articles*
|
||||
when (member (article-id article) articles-in-tag :test #'equal)
|
||||
collect (create-article article :tiny t))))
|
||||
|
||||
|
@ -304,10 +328,10 @@
|
|||
|
||||
;; produce index-titles.html where there are only articles titles
|
||||
(generate "output/html/index-titles.html" (generate-semi-mainpage :no-text t))
|
||||
|
||||
|
||||
;; produce index file for each tag
|
||||
(loop for tag in (articles-by-tag) do
|
||||
(generate (format nil "output/html/tag-~d.html" (getf tag :NAME))
|
||||
(generate (format nil "output/html/tag-~d.html" (getf tag :NAME))
|
||||
(generate-tag-mainpage (getf tag :VALUE))))
|
||||
|
||||
;; generate rss gopher in html folder if gopher is t
|
||||
|
@ -325,29 +349,40 @@
|
|||
|
||||
;; produce the gophermap file
|
||||
(save-file (concatenate 'string "output/gopher/" (getf *config* :gopher-index))
|
||||
(let ((output (load-file "templates/gopher_head.tpl")))
|
||||
(dolist (article *articles*)
|
||||
(setf output
|
||||
(string
|
||||
(concatenate 'string output
|
||||
(format nil (getf *config* :gopher-format)
|
||||
;; here we create a 80 width char string with title on the left
|
||||
;; and date on the right
|
||||
;; we truncate the article title if it's too large
|
||||
(let ((title (format nil "~80a"
|
||||
(if (< 80 (length (article-title article)))
|
||||
(subseq (article-title article) 0 80)
|
||||
(article-title article)))))
|
||||
(replace title (article-rawdate article) :start1 (- (length title) (length (article-rawdate article)))))
|
||||
|
||||
|
||||
(getf *config* :gopher-path)
|
||||
(article-id article)
|
||||
(getf *config* :gopher-server)
|
||||
(getf *config* :gopher-port)
|
||||
)))))
|
||||
output))
|
||||
|
||||
(generate-gopher-index *articles*))
|
||||
|
||||
;; produce a tag list menu
|
||||
(let* ((directory-path "output/gopher/_tags_/")
|
||||
(index-path (concatenate 'string directory-path (getf *config* :gopher-index))))
|
||||
(ensure-directories-exist directory-path)
|
||||
(save-file index-path
|
||||
(let ((output (load-file "templates/gopher_head.tpl")))
|
||||
(loop for tag in (articles-by-tag)
|
||||
do
|
||||
(setf output
|
||||
(string
|
||||
(concatenate
|
||||
'string output
|
||||
(format nil (getf *config* :gopher-format)
|
||||
1 ;; gopher type, 1 for menus
|
||||
(getf tag :NAME)
|
||||
(concatenate 'string
|
||||
(getf *config* :gopher-path) "/" (getf tag :NAME) "/")
|
||||
(getf *config* :gopher-server)
|
||||
(getf *config* :gopher-port)
|
||||
)))))
|
||||
output)))
|
||||
|
||||
;; produce each tag gophermap index
|
||||
(loop for tag in (articles-by-tag) do
|
||||
(let* ((directory-path (concatenate 'string "output/gopher/" (getf tag :NAME) "/"))
|
||||
(index-path (concatenate 'string directory-path (getf *config* :gopher-index)))
|
||||
(articles-with-tag (loop for article in *articles*
|
||||
when (member (article-id article) (getf tag :VALUE) :test #'equal)
|
||||
collect article)))
|
||||
(ensure-directories-exist directory-path)
|
||||
(save-file index-path (generate-gopher-index articles-with-tag))))
|
||||
|
||||
;; produce each article file (only a copy/paste in fact)
|
||||
(loop for article in *articles*
|
||||
do
|
||||
|
|
|
@ -2,6 +2,7 @@ Hello, this is the head of your gophermap page, you can
|
|||
customize it how you want !
|
||||
|
||||
[0|RSS Feed|/~me/rss.xml|server|port]
|
||||
[1|Browse by tag|/~me/_tags_/|server|port]
|
||||
|
||||
-----------------------------------------------------------------
|
||||
|
||||
|
|
Loading…
Reference in New Issue