(in-package :blog) ;;;; blog-v2: define views ;;;; We'll override some of the fields in the scaffolding. For each ;;;; field, we can specify some arguments to specify how to present, ;;;; obtain, write or parse the data. Note also that currently, if I ;;;; override a field then I lose the associated scaffolding ;;;; (e.g. that the field is required). ;;; added in src/views.lisp (defview post-form-view (:type form :inherit-from '(:scaffold post)) (time :hidep t) ;; POST-AUTHOR-ID and ALL-USERS will be defined below (author :reader #'post-author-id :present-as (dropdown :choices #'all-users :label-key #'user-name) :parse-as (object-id :class-name 'user) :requiredp t) (short-text :present-as textarea :requiredp t) (text :present-as (textarea :cols 30) :requiredp t)) ;;;; Here we don't really need to manually edit the time as it should ;;;; be done automatically by the backend, so we hide the TIME field. ;;;; ;;;; We had better present the SHORT-TEXT and TEXT by using a TEXTAREA ;;;; presentation so that they're easier to edit. It is actually a ;;;; class named TEXTAREA-PRESENTATION located in the weblocks source ;;;; in the file ;;;; "cl-weblocks/src/views/types/presentations/textarea.lisp". Now ;;;; is probably a good time to have a look. You will see that it has ;;;; among others a COLS slot, which the DEFVIEW macro allows us to ;;;; set by using the syntax above. ;;;; ;;;; Now for the AUTHOR. As is done in the weblocks-demo application, ;;;; we present it as an HTML dropdown list. The idea is to show the ;;;; user a list of the names of users, and each name has for value ;;;; the ID of the user. We then map the selected user ID to the ;;;; actual user object. ;;;; ;;;; This is done by using a DROPDOWN presentation (in a file next to ;;;; the TEXTAREA one). To :CHOICES we assign a function that returns ;;;; a list of objects to choose from, and :LABEL-KEY the function ;;;; used to convert each object to a string. That's it for what ;;;; we'll see in the browser. ;;;; ;;;; Under the cover we need to pass the USER objects as their ID's, ;;;; and parse an ID into a USER object. The former is done by ;;;; specifying a function as :READER (takes a POST as argument), and ;;;; the latter by using a parser named OBJECT-ID. As for ;;;; presentation, this is actually a class named OBJECT-ID-PARSER ;;;; which you'll find in ;;;; cl-weblocks/src/views/types/parsers/common.lisp at the end of the ;;;; file. As before CLASS-NAME is one slot of OBJECT-ID-PARSER. ;;;; ;;;; Finally, before we make this work we need to define ;;;; POST-AUTHOR-ID and ALL-USERS, ;;; added in src/models.lisp: (in-package :blog) (defgeneric post-author-id (post) (:method ((post post)) (when (post-author post) (object-id (post-author post))))) (defun all-users (&rest args) "return all objects of class USER. ARGS is an added argument that is ignored (needed for use in dropdown lists in views)." (declare (ignore args)) (find-persistent-objects (class-store 'user) 'user)) ;;;; Now we can add and delete users and posts using the gridedit ;;;; widgets. That should be enough of an introduction to views and ;;;; presentations. ;;;; ChangeLog blog-v2 * src/models.lisp (post-author-id, all-users): functions used by the views * src/views.lisp (post-form-view): override some fields - textarea for the texts, and dropdown list for the author blog-v1: * src/views.lisp (user-table-view, user-data-view, user-form-view) (post-table-view, post-data-view, post-form-view): scaffolded views for the gridedit interface * src/init-session.lisp (init-user-session): call MAKE-ADMIN-PAGE * src/layout.lisp (make-users-gridedit, make-posts-gridedit) (make-admin-page): add simple gridedit interface for the two models * src/models.lisp (user, post): USER and POST models