Creating a Wiki with UCW

Regular Style 

view.ucw 

The view.ucw page is used for viewing the contents of a wiki page. The user specifies which page they're insterested in using the "page-name" HTTP request parameter. If no page-name is specified we default to "WelcomePage," if the specified page-name does not exist we assume the user wants to edit (in this case create) the page.

view.ucw - Introducing defentry-point 

(defentry-point "view.ucw" (:application *example-application*)
    ((page-name "WelcomePage"))
  (if (find-wiki-page page-name)
      (call 'view-wiki-page :page-name page-name)
      (call 'redirect-component
            :target (strcat "edit.ucw?page-name=" page-name))))

DEFENTRY-POINT is UCW's way of associating a piece of code to a particular url, it's analogous to handlers or servlet definitions in other frameworks.

In this case we are creating an entry-point named "view.ucw" and tying it to the example-app application. The entry-point takes one parameter: page-name. If the page-name parameter is not passed in the request we will use "WelcomePage."

This entry-point is very simple, if the requested wiki-page exists then we should just view it using the page (aka component) view-wiki-page, if the requested page doesn't exist then we'll just pretend that the user actually wants to edit the page.

What is the "effective" url for this entry point? Since example-app application's url-prefix is "/ucw/examples/" we simply concatentate the entry-point's name and the url-prefix to get "/ucw/examples/view.ucw". The extension needn't be ".ucw" it can be anything you want or nothing at all. When using the araneida or aserve backends the entry-point's url is automatically registered with the server, with the mod_lisp backend you must manually configure apache to send requests for this entry point to ucw.

view-wiki-page - Introducing components 

How do we tell UCW to show the user some html? While we could just litter our entry-point with (progn (write-line "<html>") ...) we're not going to, it's bad style and UCW is makes that more difficult than it could be.

What we're supposed to do is hand off our request to a component and let it deal with the nitty gritty html stuff. Here's the form which defines the view-wiki-page component:

(defcomponent view-wiki-page (window-component
                              template-component)
  ((page-name :accessor page-name :initarg :page-name))
  (:default-initargs :template-name "ucw/examples/view.tal"))
(defmethod template-component-environment nconc
    ((page view-wiki-page))
  (tal-env 'contents
           (cl-ppcre:regex-replace-all
            "([A-Z][a-z]+){2,}"
            (contents (find-wiki-page (page-name page)))
            "<a href=\"view.ucw?page-name=\\&\">\\&</a>")))

Notice how much the defcomponent macro looks like defclass, that's not accidental. view-wiki-page now names a class of components. The (call 'view-wiki-page ...) form in our view.ucw entry-point is little more than a call to make-instance.

view-wiki-page, since it's a window-component, is designed to occupy the entire browser window, it has to worry about emiting <html> and <head> tags and setting up javascript includes and style sheet links. Since view-wiki-page is also a template component it depends an a TAL file ("ucw/examples/view.tal") to specify what html to output.

edit.ucw 

The entry-point 

Editing pages is only slightly more complicated than viewing:

(defentry-point "edit.ucw" (:application *example-application*)
  ((page-name "WelcomePage") name summary contents)
  (if contents
      (progn
        (update-wiki-page page-name (make-instance 'wiki-edit
                                                   :author name
                                                   :summary summary
                                                   :contents contents))
        (call 'thankyou :page-name page-name))
      (call 'edit-wiki-page :page-name page-name)))

We assume that if the request contains the contents parameter then we're submitting an edit, otherwise we're asking for the edit page form. We've already seen DEFENTRY-POINT, entry-point lambda lists and call, so we can jump directly to the edit-wiki-page component:

The component 

(defcomponent edit-wiki-page (window-component template-component)
  ((page-name :accessor page-name :initarg :page-name))
  (:default-initargs :template-name "ucw/examples/edit.tal"))

Like view-wiki-page this is also a window component based on a TAL template.

The thankyou page 

Just for fun we're going to use YACLML as opposed to TAL for the thankyou component:

(defcomponent thankyou (window-component)
  ((page-name :accessor page-name :initarg :page-name)))
(defmethod render-on ((res response) (page thankyou))
  (symbol-macrolet ((page-name (<:as-html (page-name page))))
    (<:html
      (<:head
        (<:title "Thank you for editing " page-name))
      (<:body
        (<:p "Thank you for editing " page-name)
        (<:a :href (strcat "view.ucw?page-name=" (page-name page))
          "View " page-name)))))

As you can see by the strcat UCW isn't well adapted to munging strings into urls. [the situation is slightly better in tal pages with tal expression language].