<?xml version="1.0"?>
<paste-with-annotations>
  <paste>
    <number>
      <integer>5833</integer>
    </number>
    <user>
      <string>drewc</string>
    </user>
    <title>
      <string>UCW code for Re: Looking for bored Lisper with GUI chops.</string>
    </title>
    <contents>
      <string>(defpackage :cl-bloglines
  (:use :cl :it.bese.ucw :it.bese.ucw-user))

(in-package :cl-bloglines)

(require 'xmls)
(require 'aserve)

(setf ucw::*debug-on-error* t)

(defparameter *rpc-url* &quot;http://rpc.bloglines.com/&quot;)

(defun do-bloglines-request (username password &amp;key (feed nil) (update 0))
  &quot;Does a listsubs, or a getitems if FEED is non-nil.
   Set UPDATE to 1 to mark a feed read&quot;
  (let ((args `(:basic-authorization ,(cons username password)))
	(url (concatenate 'string *rpc-url* (if feed &quot;getitems&quot;	&quot;listsubs&quot;))))
    (when feed (setf args (append `(:query ((&quot;s&quot; . ,feed) (&quot;n&quot; . ,update))) args)))
    (apply #'net.aserve.client:do-http-request
	   url args)))

;;;some xmls utilities functions
(defun assoc-value (key alist)
  (cadr (assoc key alist :test #'equalp)))

(defun get-by-name (name node)
  (let ((n (find-if #'(lambda (x) (equalp (car x) name))
		    (xmls:node-children node))))
    (if (atom (third n))
	(values (third n) (second n))
	n)))

(defun get-all-by-name (name node)
  (remove nil (mapcar #'(lambda (x)
			  (when (equalp (car x) name) x))
		      (xmls:node-children node))))

(defapplication cl-bloglines
  (:url-prefix &quot;/bl/&quot;))

;;;; define the window component

(defclass bl-window (simple-window-component)
  ((body :initarg :body
         :accessor body
         :component bl-login))
  (:default-initargs
      :title &quot;CL-Bloglines&quot;
    :stylesheet &quot;stylesheet.css&quot;)
  (:metaclass standard-component-class))

(defentry-point &quot;index.ucw&quot; (:application cl-bloglines) ()
  (call 'bl-window))

(defmethod render-on ((res response) (win bl-window))
  (&lt;:h1 (&lt;:as-html &quot;CL-Bloglines RSS Reader&quot;))
  (render-on res (body win)))

;;; a base class for all UI objects.
(defclass bl-class ()
  ((feeds :accessor feeds
	  :initarg :feeds :initform &quot;&quot;)
   (items :accessor items
	  :initarg :items :initform nil)
   (current-feed :accessor current-feed
		 :initarg :current-feed :initform nil)
   (current-item :accessor current-item
		 :initarg :current-item :initform nil)
   (auth :accessor auth :initarg :auth :initform nil))
  (:metaclass standard-component-class))

(defmethod get-feeds ((bl bl-class))
  &quot;Returns the XML text in outline format and sets (feeds self). nil on errors&quot;
  (multiple-value-bind (feeds result)
      (do-bloglines-request (car (auth bl)) (cdr (auth bl)))
    (when (equal 200 result) (setf (feeds bl) feeds))))

(defmethod get-items ((bl bl-class))
  &quot;Returns the RSS XML for the current feed&quot;
  (multiple-value-bind (items result)
	(do-bloglines-request (car (auth bl)) (cdr (auth bl))
			      :feed (current-feed bl))
  (if (equal 200 result)
      (setf (items bl) items)
      (setf (items bl) nil))))

(defmethod extract-feeds ((bl bl-class))
  &quot;Extracts the feed outine from the xml. returns an xmls node&quot;
  (cddr (third (fourth (xmls:parse (feeds bl))))))

(defmethod extract-items ((bl bl-class))
  &quot;extracts the rss items from the XML. Returns an xmls node&quot;
  (caddr (xmls:parse (items bl))))

(defaction select-item ((bl bl-class) item)
  &quot;Makes item the current item&quot;
  (setf (current-item bl) item))

(defaction select-feed ((bl bl-class) feed)
  &quot;Makes feed the current feed&quot;
  (setf (current-feed bl) feed))

(defclass bl-login (login bl-class)
  ()
  (:metaclass standard-component-class)
   (:documentation &quot;Gets the username and password and retrieves the feed&quot;))

(defmethod check-credentials ((login bl-login))
  (setf (auth login)
	(cons (login.username login)
	      (login.password login)))
  (get-feeds login))

;;;this has to be ini the ucw package. possibly a bug in ucw.
(defaction it.bese.ucw::login-successful ((login bl-login))
  (call 'rss-reader
	:feeds (feeds login)
	:auth (auth login)))

(defclass feeds-pane (widget-component bl-class)
  ((feeds-outline :accessor feeds-outline))
  (:metaclass standard-component-class)
  (:documentation &quot;a UI class to hold the list of feeds&quot;))

(defmethod render-on ((res response) (f feeds-pane))
  (&lt;:h3 (&lt;:as-html &quot;Subscriptions&quot;))
  (dolist (x (feeds-outline f))
    (let ((id (assoc-value &quot;BloglinesSubId&quot; (second x)))
	  (title (assoc-value &quot;title&quot; (second x)))
	  (unread (assoc-value &quot;BloglinesUnread&quot; (second x))))
      (&lt;:div (&lt;ucw:a :action (select-feed f id)
		     (if (equalp &quot;0&quot; unread)
			 (&lt;:as-html title)
			 (&lt;:as-html
			  (format nil &quot;~A (~A)&quot; title unread))))))))

(defclass items-pane (widget-component bl-class)
  ()
  (:metaclass standard-component-class)
  (:documentation &quot;A UI pane to hold the Titles of the RSS feed&quot;))

(defmethod render-on ((res response) (ip items-pane))
  (&lt;:h3 (&lt;:a :href (get-by-name &quot;link&quot; (items ip))
	     (&lt;:as-html (get-by-name &quot;title&quot; (items ip)))))
  (dolist (item (get-all-by-name &quot;item&quot; (items ip)))
    (&lt;:div :class &quot;item&quot; (&lt;ucw:a :action (select-item ip item)
	    (&lt;:as-html (get-by-name &quot;title&quot; item))))))

(defclass body-pane (widget-component bl-class)
  ()
  (:metaclass standard-component-class)
  (:documentation &quot;a UI pane to hold the bdy of the blog post&quot;))

(defmethod render-on ((res response) (b body-pane))
  (&lt;:h3 (&lt;:as-html (get-by-name &quot;title&quot; (current-item b))))
  (&lt;:a :href (get-by-name &quot;link&quot; (current-item b))
       (&lt;:as-html &quot;(link)&quot;))
  (&lt;:div :class &quot;content&quot;
	 (&lt;:as-is (get-by-name &quot;description&quot; (current-item b)))))

(defclass rss-reader (widget-component bl-class)
  ((feeds-pane :accessor feeds-pane :component feeds-pane)
   (items-pane :accessor items-pane :component items-pane)
   (body-pane :accessor body-pane :component body-pane))
  (:metaclass standard-component-class)
  (:documentation &quot;the RSS reader application itself&quot;))

(defmethod render-on :before ((res response) (rss rss-reader))
  &quot;Propogates the state of the current-feed/item throughout the application
Also gets the current RSS Feed if needed&quot;
  (with-slots (current-feed current-item feeds-pane
			    items-pane body-pane) rss
					;set the current-feed/item
    (setf current-feed (current-feed feeds-pane)
	  current-item (current-item items-pane))
    (setf  (current-item body-pane) current-item)
					;update if current-feed has changed.
    (when (not (equal current-feed (current-feed items-pane)))
      (get-items rss)
      (setf (current-feed items-pane) current-feed
	    (items items-pane) (extract-items rss)))))
    
(defmethod shared-initialize :after ((rss rss-reader) foo &amp;rest bar)
  &quot;Sets up the initial feed pane&quot;
  (declare (ignore foo bar))
  (setf (feeds-outline (feeds-pane rss))
	(extract-feeds rss)))

(defmethod render-on ((res response) (rss rss-reader))
  (&lt;:table :width &quot;100%&quot; :style &quot;{border:1px solid black}&quot;
   (&lt;:tr (&lt;:td :rowspan 2 :valign &quot;top&quot; :width &quot;20%&quot; :style &quot;{border-right : 1px solid black}&quot;
	       (render-on res (feeds-pane rss)))
	 (&lt;:td (render-on res (items-pane rss))))
   (&lt;:tr (&lt;:td (&lt;:hr)(render-on res (body-pane rss))))))

</string>
    </contents>
    <universal-time>
      <integer>3317511768</integer>
    </universal-time>
    <channel>
      <string>#lisp</string>
    </channel>
    <colorization-mode>
      <string></string>
    </colorization-mode>
    <maybe-spam>
      <null/>
    </maybe-spam>
    <is-unicode>
      <null/>
    </is-unicode>
  </paste>
</paste-with-annotations>