~solene/cl-yag

142b84ab78cb64423e2781a3e520bbd1e9cb40cd — Solene Rapenne 6 years ago 4a5228a
Add per-tag browsing on gopher
4 files changed, 93 insertions(+), 57 deletions(-)

M Makefile
M data/articles.lisp
M generator.lisp
M templates/gopher_head.tpl
M Makefile => Makefile +1 -1
@@ 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"

M data/articles.lisp => data/articles.lisp +2 -2
@@ 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)
   ))


M generator.lisp => generator.lisp +89 -54
@@ 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

M templates/gopher_head.tpl => templates/gopher_head.tpl +1 -0
@@ 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]

 -----------------------------------------------------------------