Flexible Lisp Blogware. Fork for personal use. Mirrored from https://github.com/kingcons/coleslaw originally.

import.lisp 2.6KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. (eval-when (:compile-toplevel :load-toplevel)
  2. (ql:quickload '(coleslaw cxml cl-ppcre local-time)))
  3. (defpackage :coleslaw-import
  4. (:use :cl :cxml)
  5. (:import-from :coleslaw #:slugify
  6. #:load-config
  7. #:*config*
  8. #:repo)
  9. (:import-from :local-time #:+short-month-names+)
  10. (:import-from :cl-ppcre #:regex-replace-all))
  11. (in-package :coleslaw-import)
  12. (defun node-val (name post)
  13. (flet ((value (node)
  14. (let ((child (dom:last-child node)))
  15. (when child (dom:data child)))))
  16. (let ((nodes (dom:get-elements-by-tag-name post name)))
  17. (if (string= "category" name)
  18. (loop for node across nodes collecting (value node))
  19. (when (plusp (length nodes)) (value (elt nodes 0)))))))
  20. (defun get-timestamp (post)
  21. (destructuring-bind (day date month year time tz)
  22. (cl-ppcre:split " " (node-val "pubDate" post))
  23. (format nil "~a-~2,'0d-~2,'0d ~a" year (position month +short-month-names+
  24. :test #'string=) date time)))
  25. (defun import-post (post &optional (since nil since-supplied-p))
  26. (when (and (string= "publish" (node-val "wp:status" post)) ; is it public?
  27. (string= "post" (node-val "wp:post_type" post)) ; is it a post?
  28. (or (not since-supplied-p) (string>= (get-timestamp post) since)))
  29. (let ((slug (slugify (node-val "title" post))))
  30. (when (string= "" slug)
  31. (error "No valid slug-title for post ~a." (get-timestamp post)))
  32. (export-post (node-val "title" post) (node-val "category" post)
  33. (get-timestamp post) (node-val "content:encoded" post)
  34. (format nil "~a.post" slug)))))
  35. (defun export-post (title tags date content path)
  36. (with-open-file (out (merge-pathnames path (repo *config*))
  37. :direction :output
  38. :if-exists :supersede
  39. :if-does-not-exist :create)
  40. ;; TODO: What other data/metadata should we write out?
  41. (format out ";;;;;~%")
  42. (format out "title: ~A~%" title)
  43. (format out "tags: ~A~%" (format nil "~{~A~^, ~}" tags))
  44. (format out "date: ~A~%" date)
  45. (format out "format: html~%") ; post format: html, md, rst, etc
  46. (format out ";;;;;~%")
  47. (format out "~A~%" (regex-replace-all (string #\Newline) content "<br>"))))
  48. (defun import-posts (filepath &optional since)
  49. (let* ((xml (cxml:parse-file filepath (cxml-dom:make-dom-builder)))
  50. (posts (dom:get-elements-by-tag-name xml "item")))
  51. (load-config)
  52. (ensure-directories-exist (repo *config*))
  53. (loop for post across posts do (import-post post since))))