Changeset 3713
- Timestamp:
- 08/01/08 00:25:05 (4 months ago)
- Files:
-
- trunk/bknr/web/src/rss/rss.lisp (modified) (3 diffs)
- trunk/projects/quickhoney/src/handlers.lisp (modified) (1 diff)
- trunk/projects/quickhoney/src/json.lisp (added)
- trunk/projects/quickhoney/src/quickhoney.asd (modified) (1 diff)
- trunk/projects/quickhoney/src/webserver.lisp (modified) (1 diff)
- trunk/projects/quickhoney/website/static/javascript.js (modified) (2 diffs)
- trunk/projects/quickhoney/website/static/styles.css (modified) (1 diff)
- trunk/projects/quickhoney/website/templates/index.xml (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/bknr/web/src/rss/rss.lisp
r3702 r3713 122 122 (let ((month-string (bknr.web:query-param "month"))) 123 123 (when month-string 124 (mapcar #'parse-integer (cl-ppcre:split "([-/]|(?<=.. ))" month-string :limit 2))))))124 (mapcar #'parse-integer (cl-ppcre:split "([-/]|(?<=....))" month-string :limit 2)))))) 125 125 126 126 (defun rss-channel-archive (channel) 127 127 "Return the channel archive consisting of lists of lists ((MONTH YEAR) ITEM...)" 128 (group-on (rss-channel-items channel )128 (group-on (rss-channel-items channel :all t) 129 129 :test #'equal 130 130 :key (lambda (item) … … 132 132 (decode-universal-time (rss-item-pub-date item)) 133 133 (declare (ignore seconds minutes hours day)) 134 (list month year)))))134 (list year month))))) 135 135 136 136 (defgeneric rss-channel-items (channel &key) 137 137 (:documentation "Return all non-expired items in channel.") 138 (:method ((channel rss-channel) &key days month count )138 (:method ((channel rss-channel) &key days month count all) 139 139 (unless month 140 140 (setf month (month-from-query-parameter))) … … 142 142 (setf days (or (days-from-query-parameter) 143 143 (rss-channel-max-item-age channel)))) 144 (let ((items (if month 145 (cdr (find month (rss-channel-archive channel) :test #'equal)) 146 (let ((expiry-time (- (get-universal-time) (* 60 60 24 days)))) 147 (remove-if (lambda (item) (or (object-destroyed-p item) 148 (< (rss-item-pub-date item) expiry-time))) 149 (slot-value channel 'items)))))) 150 (if count 151 (subseq items 0 (min count (length items))) 152 items)))) 144 (if all 145 (remove-if #'object-destroyed-p (slot-value channel 'items)) 146 (let ((items (if month 147 (cdr (find month (rss-channel-archive channel) :test #'equal :key #'car)) 148 (let ((expiry-time (- (get-universal-time) (* 60 60 24 days)))) 149 (remove-if (lambda (item) (or (object-destroyed-p item) 150 (< (rss-item-pub-date item) expiry-time))) 151 (slot-value channel 'items)))))) 152 (if count 153 (subseq items 0 (min count (length items))) 154 items))))) 153 155 154 156 (defgeneric rss-channel-archived-months (channel) trunk/projects/quickhoney/src/handlers.lisp
r3703 r3713 427 427 (:p ((:a :href "javascript:window.close()") "ok")))))))))))) 428 428 429 (defclass news-json-handler (object-handler)429 (defclass rss-channel-handler (object-handler) 430 430 () 431 431 (:default-initargs :object-class 'rss-channel :query-function #'find-rss-channel)) 432 432 433 (defvar *json-output*) 434 435 (defclass json-output-stream () 436 ((output-stream :reader output-stream 437 :initarg :output-stream) 438 (stack :accessor stack 439 :initform nil))) 440 441 (defun next-aggregate-element () 442 (if (car (stack *json-output*)) 443 (princ #\, (output-stream *json-output*)) 444 (setf (car (stack *json-output*)) t))) 445 446 (defmacro with-json-output ((stream) &body body) 447 `(let ((*json-output* (make-instance 'json-output-stream :output-stream ,stream))) 448 ,@body)) 449 450 (defmacro with-json-output-to-string (() &body body) 451 `(with-output-to-string (s) 452 (with-json-output (s) 453 ,@body))) 454 455 (defmacro with-json-aggregate ((begin-char end-char) &body body) 456 `(progn 457 (when (stack *json-output*) 458 (next-aggregate-element)) 459 (princ ,begin-char (output-stream *json-output*)) 460 (push nil (stack *json-output*)) 461 (prog1 462 (progn ,@body) 463 (pop (stack *json-output*)) 464 (princ ,end-char (output-stream *json-output*))))) 465 466 (defmacro with-json-array (() &body body) 467 `(with-json-aggregate (#\[ #\]) 468 ,@body)) 469 470 (defmacro with-json-object (() &body body) 471 `(with-json-aggregate (#\{ #\}) 472 ,@body)) 473 474 (defun encode-array-element (object) 475 (next-aggregate-element) 476 (json:encode-json object (output-stream *json-output*))) 477 478 (defun encode-object-element (key value) 479 (next-aggregate-element) 480 (json:encode-json key (output-stream *json-output*)) 481 (princ #\: (output-stream *json-output*)) 482 (json:encode-json value (output-stream *json-output*))) 483 484 (defmethod handle-object ((handler news-json-handler) (channel rss-channel)) 485 (with-http-response (:content-type "application/json") 486 (with-json-output-to-string () 433 (defclass json-news-handler (rss-channel-handler) 434 ()) 435 436 437 (defgeneric json-encode-news-item (item) 438 (:method ((item t)) 439 ; do nothing 440 ) 441 (:method ((image quickhoney-image)) 442 (let ((vectorp (member :vector (store-image-keywords image)))) 443 (encode-object-element "uploader" (if vectorp "Peter" "Nana")) 444 (encode-object-element "category" (if vectorp "vector" "pixel")) 445 (encode-object-element "subcategory" "unknown") 446 (encode-object-element "date" (format-date-time (rss-item-pub-date image) :vms-style t :show-time nil)) 447 (encode-object-element "name" (store-image-name image))))) 448 449 (defmethod handle-object ((handler json-news-handler) (channel rss-channel)) 450 (with-json-response () 451 (with-object-element ("items") 487 452 (with-json-array () 488 453 (dolist (item (rss-channel-items channel)) 489 454 (with-json-object () 490 (encode-object-element "pubDate" (format-date-time (rss-item-pub-date item) :vms-style t)) 491 (encode-object-element "title" (rss-item-title item)) 492 (encode-object-element "description" (rss-item-description item)))))))) 455 (json-encode-news-item item))))))) 456 457 (defclass json-news-archive-handler (rss-channel-handler) 458 ()) 459 460 (defmethod handle-object ((handler json-news-archive-handler) (channel rss-channel)) 461 (with-json-response () 462 (with-object-element ("months") 463 (with-json-array () 464 (dolist (month (sort (rss-channel-archived-months channel) 465 (lambda (a b) 466 (if (= (first a) (first b)) 467 (> (second a) (second b)) 468 (> (first a) (first b)))))) 469 (with-json-array () 470 (encode-array-element (first month)) 471 (encode-array-element (second month)))))))) trunk/projects/quickhoney/src/quickhoney.asd
r3701 r3713 33 33 (:file "layout" :depends-on ("config")) 34 34 (:file "imageproc" :depends-on ("config")) 35 (:file "handlers" :depends-on ("layout" "config" "image")) 35 (:file "json" :depends-on ("packages")) 36 (:file "handlers" :depends-on ("json" "layout" "config" "image")) 36 37 (:file "tags" :depends-on ("image")) 37 38 (:file "webserver" :depends-on ("handlers")) trunk/projects/quickhoney/src/webserver.lisp
r3701 r3713 34 34 ("/upload-news" upload-news-handler) 35 35 ("/digg-image" digg-image-handler) 36 ("/news-json" news-json-handler) 36 ("/json-news-archive" json-news-archive-handler) 37 ("/json-news" json-news-handler) 37 38 ("/" template-handler 38 39 :default-template "frontpage" trunk/projects/quickhoney/website/static/javascript.js
r3683 r3713 216 216 /* news */ 217 217 218 function load_news() 219 { 220 218 var month_names = [ 'January', 'February', 'March', 'April', 'May', 'June', 219 'July', 'August', 'September', 'October', 'November', 'December' ]; 220 221 function select_archive_year() 222 { 223 var year = this.href.match(/#news\/(\d+)/)[1]; 224 map(function (element) { 225 if (element.href) { 226 ((element.href.match(/#news\/(\d+)/)[1] == year) ? addElementClass : removeElementClass)(element, 'active'); 227 } 228 }, this.parentNode.childNodes); 229 return true; 230 } 231 232 function select_archive_month() 233 { 234 var month = this.href.match(/#news\/(\d+\/\d+)/)[1]; 235 loadJSONDoc('/json-news/quickhoney?month=' + month).addCallbacks(load_news, alert); 236 return true; 237 } 238 239 function load_news(data) 240 { 241 log('load news: ' + data.items.length); 242 replaceChildNodes('newsentries', 243 map(function (item) { 244 var color = (item.category == 'pixel') ? 'ff00ff' : '00ccff'; 245 return [ DIV({ 'class': 'newsentry autonews news_' + item.category }, 246 IMG({ src: "/image/" + item.name + '/cutout-button,,' + color + ',98,4'}), 247 DIV(null, 248 H1(null, item.name), 249 item.date, ' by ', item.uploader, ' | ', 250 A({ href: '/index#' + item.category + '/' + item.subcategory + '/' + item.image_name }, 'permalink'), 251 BR(), 252 item.description)), 253 DIV({ 'class': 'news_sep' }) ]; 254 }, data.items)); 255 } 256 257 function load_news_archive(data) 258 { 259 try { 260 if (!data.months) { 261 alert('no archive data found'); 262 } 263 var currentYear; 264 var active = true; 265 replaceChildNodes('archive-navigation', 266 SPAN({ 'class': 'title' }, 'Archive'), BR(), 267 map(function (entry) { 268 var year = entry[0]; 269 var month = entry[1]; 270 var result = []; 271 if (year != currentYear) { 272 if (currentYear) { 273 active = false; 274 } 275 currentYear = year; 276 var link = A({ href: '#news/' + year, 'class': 'year' }, year, BR()); 277 link.onclick = select_archive_year; 278 result.push(link); 279 } 280 var link = A({ href: '#news/' + year + '/' + month, 'class': 'month ' + (active ? ' active' : '')}, 281 month_names[month - 1], BR()); 282 link.onclick = select_archive_month; 283 result.push(link); 284 return result; 285 }, data.months)); 286 } 287 catch (e) { 288 log('error while processing archive data: ' + e); 289 } 221 290 } 222 291 … … 392 461 function() { 393 462 footer_hide(); 394 load_news(); 463 loadJSONDoc('/json-news-archive/quickhoney').addCallbacks(load_news_archive, alert); 464 // load_news(); 395 465 }); 396 466 trunk/projects/quickhoney/website/static/styles.css
r3683 r3713 629 629 visibility: hidden; 630 630 } 631 632 .archive span.title, .archive a.year { font-size: 1.5em; } 633 .archive a.month.active { display: block; } 634 .archive a.month { display: none; } 635 .archive { padding-left: 1em; } trunk/projects/quickhoney/website/templates/index.xml
r3683 r3713 131 131 132 132 <div id="news_page"> 133 <p id="news_content"> 134 <div class="newsentry news_vector autonews"> 135 <img src="/image/TSG_Platforms_web/cutout-button,,00ccff,98,4"/> 136 <div> 137 <h1>Jan and Ella</h1> 138 March 8th, 2008 by Peter | <a href="foo">permalink</a><br/> 139 description 140 </div> 141 </div> 142 <div class="news_sep"> </div> 143 <br/> 144 <div class="newsentry news_pixel autonews"> 145 <img src="/image/TSG_Platforms_web/cutout-button,,00ccff,98,4"/> 146 <div> 147 March 8th, 2008 by Peter | <a href="foo">permalink</a><br/> 148 description 149 </div> 150 </div> 151 </p> 133 <table border="0"> 134 <tbody> 135 <tr> 136 <td valign="top" id="newsentries"> 137 <div class="newsentry news_vector autonews"> 138 <img src="/image/TSG_Platforms_web/cutout-button,,00ccff,98,4"/> 139 <div> 140 <h1>Jan and Ella</h1> 141 March 8th, 2008 by Peter | <a href="foo">permalink</a><br/> 142 description 143 </div> 144 </div> 145 <div class="news_sep"> </div> 146 <br/> 147 <div class="newsentry news_pixel autonews"> 148 <img src="/image/TSG_Platforms_web/cutout-button,,00ccff,98,4"/> 149 <div> 150 March 8th, 2008 by Peter | <a href="foo">permalink</a><br/> 151 description 152 </div> 153 </div> 154 </td> 155 <td class="archive" id="archive-navigation" valign="top"> 156 </td> 157 </tr> 158 </tbody> 159 </table> 152 160 </div> 153 161
