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

import.lisp 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. (eval-when (:compile-toplevel)
  2. (ql:quickload '(cxml cl-ppcre)))
  3. (defpackage :coleslaw-import
  4. (:use :cl :coleslaw :cxml)
  5. (:import-from :local-time #:+short-month-names+
  6. #:encode-timestamp)
  7. (:import-from :cl-ppcre #:regex-replace-all))
  8. (in-package :coleslaw-import)
  9. (defgeneric import-post (service post &key static-p)
  10. (:documentation "Import POST into *storage*. The method to construct the POST
  11. object is determined by SERVICE."))
  12. (defmethod import-post ((service (eql :wordpress)) post &key static-p)
  13. (labels ((nodes (name)
  14. (dom:get-elements-by-tag-name post name))
  15. (value (node)
  16. (let ((child (dom:last-child node)))
  17. (when child (dom:data child))))
  18. (node-val (name)
  19. (let ((nodes (nodes name)))
  20. (if (string= "category" name)
  21. (loop for node across nodes collecting (value node))
  22. (when (plusp (length nodes)) (value (elt nodes 0))))))
  23. (public-p ()
  24. (string= "publish" (node-val "wp:status")))
  25. (post-p ()
  26. (string= "post" (node-val "wp:post_type")))
  27. (make-timestamp (pubdate)
  28. (let* ((date (cl-ppcre:split " " (subseq pubdate 5)))
  29. (time (cl-ppcre:split ":" (fourth date))))
  30. (encode-timestamp 0
  31. (parse-integer (third time))
  32. (parse-integer (second time))
  33. (parse-integer (first time))
  34. (parse-integer (first date))
  35. (position (second date) +short-month-names+
  36. :test #'string=)
  37. (parse-integer (third date))))))
  38. (when (and (public-p)
  39. (post-p))
  40. (let ((new-post (make-post (node-val "title")
  41. (node-val "category")
  42. (make-timestamp (node-val "pubDate"))
  43. (regex-replace-all (string #\Newline)
  44. (node-val "content:encoded")
  45. "<br>")
  46. :aliases (parse-integer (node-val "wp:post_id"))))
  47. (comments (nodes "wp:comment")))
  48. (add-post new-post (post-id new-post))
  49. (when static-p
  50. (ensure-directories-exist coleslaw::*input-dir*)
  51. (export-post new-post))))))
  52. (defmethod export-post (post)
  53. (let ((filepath (merge-pathnames (format nil "~5,'0d-~a.post"
  54. (post-id post)
  55. (coleslaw::escape (post-title post)))
  56. coleslaw::*input-dir*)))
  57. (with-open-file (out filepath :direction :output
  58. :if-exists :supersede :if-does-not-exist :create)
  59. ;; TODO: What other data/metadata should we write out?
  60. (format out ";;;;;~%")
  61. (format out "title: ~A~%" (post-title post))
  62. (format out "tags: ~A~%" (coleslaw::pretty-list (post-tags post)))
  63. (format out "date: ~A~%" (coleslaw::year-month (post-date post)))
  64. (format out ";;;;;~%")
  65. (format out "~A~%" (post-content post)))))
  66. (defgeneric import-posts (service filepath &key static-p)
  67. (:documentation "Import the posts (and potentially comments or other data)
  68. from FILEPATH, converting them to appropriate coleslaw objects and inserting
  69. them into *storage*. The method to parse the file is determined by SERVICE.
  70. If STATIC-P is true, the posts will also be written into *.html files in
  71. *input-dir*."))
  72. (defmethod import-posts ((service (eql :wordpress)) filepath &key static-p)
  73. (let* ((xml (cxml:parse-file filepath (cxml-dom:make-dom-builder)))
  74. (posts (dom:get-elements-by-tag-name xml "item")))
  75. (loop for post across posts do
  76. (import-post service post :static-p static-p))))