rants

David Van Horn <[email protected]> Sun, 07 Mar 2004 01:23:26 -0500
Newsgroups gmane.org.ballistichelmet.devel
Message-ID <[email protected]>
I still haven't been able to find a back up of the rants.ss program.  This is
the latest version I could find, which doesn't use the RSS backend.  I guess
I'll have to reimplement that from my notes.  Shit.  Makes you wish we had
backups on phpwh.  Or maybe I should just post every Scheme program I've ever
written here. ;)

David



;; -*- scheme -*-

;; David Van Horn
;; Sun Sep 15 03:31:58 EDT 2002

;; modified to make latest.html, an HTML fragment for inclusion in BH's
;; index.html
;; Sat Apr  5 18:15:47 EST 2003

;; modified to work as command line program, and cgi program.
;; Sat Oct 26 14:47:59 EDT 2002

#cs
(module rant mzscheme

  (require (lib "date.ss"         "mzlib")
           (lib "cmdline.ss"      "mzlib")
           (lib "match.ss"        "mzlib")
           (lib "cgi.ss"          "net")
           (lib "xml.ss"          "xml")
           (lib "sxml-to-html.ss" "ssax")
           (lib "rfc2822.ss"      "chomsky")
           (lib "1.ss"            "srfi"))

  (provide cgi-add-rant
           cmdline-add-rant
           add-rant
           latest)

  (xexpr-drop-empty-attributes #t)
  (date-display-format 'rfc2822)        ; Should use 'american for HTML,
                                        ; 'rfc2822 for RSS.

  (define base "/home/dvanhorn/public_html/rant/")
  (define url-base "http://ballistichelmet.org/rant/")
  (define (url+ s)
    (format "http://ballistichelmet.org/rant/~a" s))

  ;; this is the CGI interface for adding a rant.
  ;; we extract the rant from the CGI environment variables `rant-subj'
  ;; and `rant-text', add the rant, and then print out an html page to
  ;; report to the user that all is well, and the rant is available for
  ;; viewing.
  (define (cgi-add-rant)
    (output-http-headers)
    (sxml->html

     ;; add the rant and get the filename of the new rant
     (let ((file (add-rant
                  (extract-binding/single 'rant-subj (get-bindings))
                  (extract-binding/single 'rant-text (get-bindings)))))

       ;; let user know where the new rant is placed.
       `(html (head (title "rant submitted")
                    (link (@ (rel "stylesheet")
                             (href ,(url+ "rant.css")))))
              (body (p "your rant has been submitted and is available now at "
                       (a (@ (href ,(url+ file)))
                          ,(url+ file)) ".")
                    (p "thank you")
                    (p (a (@ (href ,url-base))
                          "back to rant index"))
                    (p (a (@ (href "/"))
                          "back to ballistic helmet")))))))

  ;; this is the command line interface for adding a rant.
  ;; the rant subject and body may be given to us on the command line,
  ;; otherwise we prompt the user for them.
  (define (cmdline-add-rant)
    (let ((subj null) (text null))

      (command-line "rant" (current-command-line-arguments)
        (once-each
         [("-s" "--subject") s "the subject of your rant"
          (set! subj s)]
         [("-b" "--body") b "the body of your rant"
          (set! text b)]))

      ;; if the subject and text were not specified on the command line
      ;; then prompt for them from the user.
      (when (null? subj) (set! subj (prompt "subj: "  #\newline)))
      (when (null? text) (set! text (prompt "rant:\n" eof)))

      ;; let the user know where the rant has been placed
      (printf "your rant has been submitted and is available now at ~a~n"
              (url+ (add-rant subj text)))))


  ;; reads characters from the current input port until the character is
  ;; equal to the end object.  returns a string of all characters read.
  ;; the end object is trimmed (ie does not get returned at the end of
  ;; the string)
  (define (prompt p end-obj)
    (printf p)
    (if (eqv? end-obj #\newline)
        (let loop () (let ((x (read-line))) (if (string=? "" x) (loop) x)))
        (apply string-append
               (let loop ((lines null))
                 (let ((line (read-line)))
                   (if (eof-object? line)
                       (reverse! lines)
                       (loop (cons (string-append line "\n")
                                   lines))))))))

  ;; add the rant and return the filename of the new rant
  (define (add-rant subj text) ;; -> string
    (let* ((seconds (current-seconds))
           (date    (date->string (seconds->date seconds) #t))
           (file    (string-append (number->string seconds) ".html"))
           (item    `(item (title ,subj)
                           (link ,(url+ file))
                           (pubDate ,date)
                           (description ,(format "~n<pre>~a</pre>" text)))))

      ;; Insert the new item into the RSS feed file.
      (match (xml->xexpr (with-input-from-file (string-append base "rants.xml")
                           read-xml/element))
        (('rss att ('channel title link desc . items))
         (begin
           (make-index (cons item items))  ;; make index.html
           (with-output-to-file (string-append base "rants.xml")
             (lambda ()
               (write-xml/content
                (xexpr->xml
                 `(rss ,att (channel ,title ,link ,desc ,item ,@items)))))
             'replace))))

      ;; make a file for the rant
      ;; the name of the file is the time (in seconds) plus .html
      (with-output-to-file (string-append base file)
        (lambda ()
          (sxml->html
           `(html (head (title ,(format "rant - ~a" subj))
                        (link (@ (rel "stylesheet")
                                 (href ,(url+ "rant.css")))))
                  (body (h1 ,subj)
                        (h2 "created "
                            ,(parameterize ((date-display-format 'american))
                               (date->string (seconds->date seconds) #t)))
                        (pre (tt ,text))
                        (p (a (@ (href ,url-base))
                              "back to rant index"))
                        (p (a (@ (href "/"))
                              "back to ballistic helmet")))))))

      file)) ;; return the filename of the new rant.

  ; make an index.html page listing all the rants
  (define (make-index rants)  ;; listof item
    (with-output-to-file (string-append base "index.html")
      (lambda ()
        (sxml->html
         `(html (head (title "rant index")
                      (link (@ (rel "stylesheet")
                               (href ,(url+ "rant.css")))))
                (body
                 (table
                  (@ (style "width: 100%;"))
                  (tr
                   (td
                    (img (@ (border "0")
                            (src "http://www.uvm.edu/~dvanhorn/bh.png"))))))
                 (p (@ (style "text-align: right"))
                    (a (@ (href "/")) "back to bh"))
                 (img (@ (src "rant-top.png") (alt "RANTS")))
                 ,(with-input-from-file (string-append base "about.txt") read)
                 (img (@ (src "rant-write.png") (alt "write a new goddamn rant")))
                 (a (@ (href "rants.xml"))
                    (img (@ (src "rss.gif") (alt "rss feed")
                            (align "right") (border "0"))))
                 (p "create a new rant "
                    (a (@ (href ,(url+ "new.html")))
                       "here"))
                 (img (@ (src "rant-read.png") (alt "read a goddamn rant")))
                 (table
                  ,(map
                    (match-lambda
                     (('item ('title t) ('link l) ('pubDate d) . _)
                      `(tr (td ,(parameterize ((date-display-format 'american))
                                  (date->string (rfc2822-date-time->date d) #t)))
                           (td (a (@ (href ,l))
                                  ,t)))))
                    rants))))))
      'replace))

  ;; Requires date.ss to provide all-defined, which is not the default.
  ;; Converts a date into a ballistichelmet "style" string.
  ;; FIX: calculate time zone.
  (define date->bh-date-string
    (let ((add-zero (lambda (n) (if (< n 10)
                                    (string-append "0" (number->string n))
                                    (number->string n)))))
      (lambda (date)
        (format "~a ~a ~a ~a:~a:~a EDT"
                (date-year date)
                (month/number->string (date-month date))
                (add-zero (date-day date))
                (add-zero (date-hour date))
                (add-zero (date-minute date))
                (add-zero (date-second date))
                ;; How do we calculate eg EDT from this?
                ;;(date-time-zone-offset date)
                ))))

  ;; print an HTML report of the latest 4 rants.  For use on BH front page.
  (define (latest)
    (display-xml/content
     (xexpr->xml
      (match (xml->xexpr
              (with-input-from-file (string-append base "rants.xml")
read-xml/element))

             (('rss att ('channel title link desc . items))
              `(div ((id "rants") (class "newsfeed"))
                    ,(make-comment " rant section ")
                    (div ((class "heading"))
                         (h3 "[ rants ]"
                             (a ((href "/rant/rss.xml"))
                                (img ((src "/rant/rss.gif")
                                      (border "0")))
                                ))

                         (p ((class "publish"))
                            (a ((href "/rant/")) "read goddamn rants")
                            " / "
                            (a ((href "/rant/new.html"))
                               "write a goddamn rant")
                            ;;" / "
                            ))
                    ,@(map
                       (match-lambda
                        (('item
                          ('title title) ('link link)
                          ('pubDate date) ('description body . body...))

                         `(div ((class "headline"))
                               (h4 ((class "news"))
                                   (a ((href ,link))
                                      ,title))
                               (p ((class "date"))
                                  ,(parameterize ((date-display-format 'american))
                                      (date->bh-date-string
                                       (rfc2822-date-time->date date))))
                               (p ((class "comment"))
                                  (pre ,(let* ((str (apply string-append (cons
body body...)))
                                               (len (- (string-length str) 6)))
                                          (if (> len 300)
                                              (string-append (substring str 6
300) "...")
                                              (substring str 6 len))))))))
                       (take items 3))))

             (_ (error "malformed channel")))))
    ) ; end of latest

  ) ;; end of rant.ss