root/render/timeline-atom.scm

(define-module (render timeline-atom)
  #:use-module (srfi srfi-19) ;; date
  #:use-module (web server)
  #:use-module (web request)
  #:use-module (web response)
  #:use-module (web uri)
  #:use-module (sxml simple)
  #:use-module (database postgres)
  #:use-module (database)
  #:use-module (database-fetch)
  #:use-module (privileges)
  #:use-module (settings)
  #:use-module (utils)
  #:use-module (notice)
  #:use-module (search)
  #:use-module (render profile)
  #:use-module (ice-9 match)
  #:export (make-timeline-atom
            make-atom-entry))

(define xmlns
  '((xmlns "http://www.w3.org/2005/Atom")
    (xmlns:thr "http://purl.org/syndication/thread/1.0")
    (xmlns:georss "http://www.georss.org/georss")
    (xmlns:activity "http://activitystrea.ms/spec/1.0/")
    (xmlns:media "http://purl.org/syndication/atommedia")
    (xmlns:poco "http://portablecontacts.net/spec/1.0")
    (xmlns:ostatus "http://ostatus.org/schema/1.0")
    (xmlns:statusnet "http://status.net/schema/api/1/")))

(define-syntax-rule (optional-tag condition tag)
  (if condition
      tag
      #f))

(define* (make-atom-entry db notice #:optional (include-xmlns #t) (author #f))
  (define verb (assoc-ref notice 'verb))
  (define verb-suffix (or (string-match1 "/([^/]+)$" verb) verb))
  (define (unimplemented)
    (warn "not implemented renderer for verb" verb)
    (make-entry-basic db notice include-xmlns author))
  (match verb
    ;; ("follow" (make-entry-un/follow db notice follower followed #:optional (include-xmlns #t)))
    ;; ("unfollow" (make-entry-un/follow db notice follower followed #:optional (include-xmlns #t)))
    ("post" (make-entry-basic db notice include-xmlns author))
    (else (unimplemented))))

(define* (make-entry-basic db notice #:optional (include-xmlns #t) (author #f))
  (define id-str (notice-id-str notice))
  (define (make-mention user)
    `(link (@ (rel "mentioned")
              (ostatus:object-type "http://activitystrea.ms/schema/1.0/person")
              (href ,(assoc-ref user 'uri)))))
  (define mentions (cons '(link (@ (rel "mentioned")
                                   (ostatus:object-type "http://activitystrea.ms/schema/1.0/collection")
                                   (href "http://activityschema.org/collection/public")))
                         (map (lambda (handle+host)
                                (make-mention
                                 (import-profile-webfinger!
                                  db
                                  (string-append "acct:" (cdr handle+host)))))
                              (notice-recipents db notice))))
  (define conversation-uri (assoc-ref notice 'conversation_uri))
  (define conversation-url (assoc-ref notice 'conversation_url))

  `(entry ,(if include-xmlns
               (cons '@ xmlns)
               '())
          ,@(basic-entry-headers notice)
          (activity:object-type  "http://activitystrea.ms/schema/1.0/note")
          ,(if author
               (make-author author)
               '())
          (link (@ (rel "alternate")
                   (type "text/html")
                   (href ,(string-append *site-base-url* "/notice/" id-str))))
          (link (@ (rel "self")
                   (type "application/atom+xml")
                   (href ,(string-append *site-base-url* "api/statuses/show/" id-str ".atom"))))
          (statusnet:notice_info (@ (local_id ,id-str)
                                    (source "web")
                                    (repeated "false")
                                    (favorite "false")))
          ,(if conversation-uri
               `(ostatus:conversation (@ ,(if conversation-url `(href ,conversation-url) '())
                                         (local_id ,(assoc-ref notice 'conversation))
                                         (ref ,conversation-uri))
                                      ,conversation-uri)
               '())
          ,@mentions
          (content (@ (type "html"))
                   ,(assoc-ref notice 'text))))

(define* (make-entry-un/follow db notice follower followed #:optional (include-xmlns #t))
  `(entry ,(if include-xmlns
               (cons '@ xmlns)
               '())
          ,@(basic-entry-headers notice)
          ,(make-author follower)
          ;; maybe add the followers tag, but I see no reason. Wouldn't following make more sense?
          ;; ,(cons `(followers (@ (url ,(string-append *site-base-url* handle "followers"))))
          ;;        (make-author follower))
          ;; the followed
          (activity:object
           ,(cdr (map (lambda (field)
                        ;; rename uri to id
                        (if (eq? (car field) 'uri)
                            (cons 'id (cdr field))
                            field))
                      (make-author followed))))))

(define (basic-entry-headers notice)
  (define verb (assoc-ref notice 'verb))
  (define date (assoc-ref notice 'createtime))
  (define id-str (notice-id-str notice))
  (define date-str (if (string? date)
                       date
                       (date->string date "~1T~2")))
  `((id ,(assoc-ref notice 'uri))
    (title "new notice by")
    (status_net (@ (notice_id ,id-str)))
    (activity:verb ,verb)
    (published ,date-str)
    (updated ,date-str)))

(define (make-author user)
  (define handle (assoc-ref user 'handle))
  (define any-name (if (string-null? (assoc-ref user 'displayname))
                       (assoc-ref user 'handle)
                       (assoc-ref user 'displayname)))
  (define bio (assoc-ref user 'bio))
  `(author
    (activity:object-type "http://activitystrea.ms/schema/1.0/person")
    (name ,handle)
    (uri ,(assoc-value 'uri user))
    (summary ,bio)
    ;; TODO add more details about thumbs
    (link (@ (rel "avatar")
             (type "image/png")
             (href ,(avatar-absolutepath user))))
    (poco:preferredUsername ,any-name)
    (poco:displayName ,any-name)
    (poco:note ,bio)))

(define* (make-timeline-atom db notices current-user user pagination-id)
  (define (make-entry notice) (make-atom-entry db notice #f))
  (define (feed-headers)
    (define user-id-str (assoc-ref user 'id))
    ;;  <link href="http://localhost/gs/index.php/api/statuses/user_timeline/1.atom" rel="self" type="application/atom+xml"/>
    `((link (@ (rel "self")
               (type "application/atom+xml")
               (href ,(string-append *site-base-url*
                                     "/api/statuses/user_timeline/"
                                     user-id-str
                                     ".atom"))))
      ;;  <link href="http://localhost/gs/index.php/main/push/hub" rel="hub"/>
      (link (@ (rel "hub")
               (href ,(string-append *site-base-url*
                                     "/main/push/hub"))))
      (link (@ (rel "salmon")
               (href ,(string-append *site-base-url*
                                     "/main/salmon/user/" user-id-str))))))
  `(feed (@ (xml:lang "en-US")
            ,@xmlns)
         (title "timeline")
         (subtitle "sub title")
         (logo ,(avatar-absolutepath user))
         ,@(feed-headers)
         ,(make-author user)
         ,(map make-entry notices)))