<?xml version="1.0"?>
<paste-with-annotations>
  <paste>
    <number>
      <integer>60601</integer>
    </number>
    <user>
      <string>eslick@media.mit.edu</string>
    </user>
    <title>
      <string>A generic admin model and widget for Weblocks</string>
    </title>
    <contents>
      <string>;;
;; Keep track of models as they are defined
;;

(defvar *models* (make-hash-table)
  &quot;Keeps track of all the models that have been created using defmodel and
   creates an admin model to generate widgets on demand&quot;)

(defvar *registered-models* (make-hash-table)
  &quot;Keeps track of all the models that have been created using defmodel and
   ensures that an editing widget has been created for it&quot;)

(defmacro defmodel (name &amp;rest args)
  `(progn 
     (defpclass ,name ,@args)
     (setf (gethash ',name *models*) nil)
     (when (gethash ',name *registered-models*)
       (setf (gethash ',name *registered-models*) nil))))

;;
;; Dynamically create an object that keeps track of these models and provide views
;;

;;
;; Model admin view &amp; creation
;;

(defpclass admin-model ()
  ((name :accessor model-name :initarg :name)
   (description :accessor model-description :initform &quot;&quot; :initarg :description)))

(defview admin-table-view (:type table :inherit-from '(:scaffold admin-model))
  (widget :hidep t))

(defview admin-data-view (:type data :inherit-from '(:scaffold admin-model))
  (widget :hidep t))


(defun register-model (model-class &amp;optional (store *default-store*))
  (let ((amod (ensure-admin-model model-class store)))
    (setf (gethash (class-name model-class) *registered-models*) amod)
    amod))

(defun ensure-admin-model (class &amp;optional (store *default-store*))
  (let* ((objects (find-persistent-objects store 'admin-model))
	 (model (find (class-name class) objects :key #'model-name)))
    (if model 
	(progn
	  (setf (model-description model) (documentation class t))
	  model)
	(let ((new-model (make-instance 'admin-model
					:name (class-name class)
					:description (documentation class t))))
	  (persist-object store new-model)))))

(defun unregister-model (model-name &amp;optional (store *default-store*))
  (let ((model (gethash model-name *registered-models*)))
    (delete-persistent-object store model)
    (remhash model-name *registered-models*)))


;;
;; Model views
;;

(defun make-model-view (model)
  (make-instance 'gridedit 
		 :name (model-name model)
		 :data-class (model-name model)
		 :view (model-table-view-name model)
		 :item-data-view (model-data-view-name model)
		 :item-form-view (model-form-view-name model)
		 :common-ops `((done . ,#'(lambda (w i)
					    (declare (ignore i))
					    (answer w))))))

(defmethod model-table-view-name (model)
  (intern (nstring-upcase (format nil &quot;~A-table-view&quot; (model-name model)))
	  (symbol-package (model-name model))))

(defmethod model-data-view-name (model)
  (intern (nstring-upcase (format nil &quot;~A-data-view&quot; (model-name model))) 
	  (symbol-package (model-name model))))

(defmethod model-form-view-name (model)
  (intern (nstring-upcase (format nil &quot;~A-form-view&quot; (model-name model))) 
	  (symbol-package (model-name model))))




;;
;; Admin widget which interfaces to models
;;

(defun make-admin-widget ()
  (make-instance 'composite :widgets (list (make-admin-grid))))

;;
;; Specialize gridedit
;;

(defclass admin-grid (datagrid)
  ())

(defun make-admin-grid ()
  (make-instance 'admin-grid
;;		 :name 'admin-grid
		 :data-class 'admin-model
		 :view 'admin-table-view
;;		 :item-data-view 'admin-data-view
		 :allow-drilldown-p t
		 :autoset-drilled-down-item-p t
		 :on-drilldown (cons :view-models #'goto-model-view)))

(defun goto-model-view (widget item)
  (with-flow widget
    (yield (make-model-view item))))

(defmethod initialize-instance :after ((admin admin-grid) &amp;rest args)
  (loop for key being the hash-key of *models* do
        (unless (gethash key *registered-models*)
	  (register-model (find-class key)))))

</string>
    </contents>
    <universal-time>
      <integer>3419520988</integer>
    </universal-time>
    <channel>
      <string>None</string>
    </channel>
    <colorization-mode>
      <string>Common Lisp</string>
    </colorization-mode>
    <maybe-spam>
      <null/>
    </maybe-spam>
    <is-unicode>
      <keyword>TRUE</keyword>
    </is-unicode>
  </paste>
</paste-with-annotations>