% -*- mode: latex; -*- mustache tags:  
\ifx\wholebook\relax\else

	\documentclass[10pt,twoside,english]{root/support/latex/sbabook/sbabook}
  \usepackage{import}
  \subimport{root/support/latex/}{common.tex}

  \hypersetup{pdfinfo = {
    Title = {TinyBlog: Building a Web Interface with Seaside},
    Author = {},
    Keywords = {pharo, smalltalk}}}
  \begin{document}
\fi

\chapter{TinyBlog: Building a Web Interface with Seaside}\section{Previous Week Solution}
You can load the solution of the previous week using the following snippet:

\begin{displaycode}{smalltalk}
Gofer new 
   smalltalkhubUser: 'PharoMooc' project: 'TinyBlog';
   package: 'ConfigurationOfTinyBlog';
   load. 
#ConfigurationOfTinyBlog asClass loadWeek3Correction
\end{displaycode}

Make sure that you Teapot server is stopped. 

\begin{displaycode}{smalltalk}
	TBTeapotWebApp stop
\end{displaycode}
\section{A Web UI for TinyBlog with Seaside}
The work presented in this exercise does not require the one on Voyage and MongoDB.

This week, we start with a web UI dedicated to display posts to users. Next week, we will develop another web UI for the blog owner to admnistrate the posts. In both cases, we will define Seaside components \url{http://www.seaside.st} which has a freely available book online here: \url{http://book.seaside.st}.


\begin{figure}

\begin{center}
\includegraphics[width=0.5\textwidth]{figures/RunningSeaside.png}\caption{Start Seaside Server.\label{RunningSeaside}}\end{center}
\end{figure}



\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/SeasideWebStart.png}\caption{Accessing Seaside Home Page.\label{SeasideWebStart}}\end{center}
\end{figure}

\section{Start Seaside}
There are two ways to start Seaside. The first one consists in executing the following code:

\begin{displaycode}{smalltalk}
ZnZincServerAdaptor startOn: 8080.
\end{displaycode}

The second one uses the graphical tool named \symbol{34}Seaside Control Panel\symbol{34} (World Menu\textgreater{}Tools\textgreater{}Seaside Control Panel).
In the contextual menu (right clic) of this tool, select \symbol{34}add adaptor...\symbol{34} and add a server of type \textcode{ZnZincServerAdaptor}, then define the port number (e.g. 8080) it should run on (cf. figure \ref{RunningSeaside}). By opening a web browser on the URL \url{http://localhost:8080}, you should see the Seaside home page as displayed on Figure \ref{SeasideWebStart}.
\section{Entry Point for TinyBlog Web UI}
Create a class named \textcode{TBApplicationRootComponent} which will be the entry point of the application.

\begin{displaycode}{smalltalk}
WAComponent subclass: #TBApplicationRootComponent
   instanceVariableNames: ''
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

On class-side, we implement the \textcode{initialize} method in the \textcode{'initialization'} protocol to declare the application to Seaside. We also integrate dependencies to the Bootstrap framework (CSS and JS files will be embedded in the application).

\begin{displaycode}{smalltalk}
TBApplicationRootComponent class >> initialize
   "self initialize"
   | app |
   app := WAAdmin register: self asApplicationAt: 'TinyBlog'.
   app
      addLibrary: JQDeploymentLibrary;
      addLibrary: JQUiDeploymentLibrary;
      addLibrary: TBSDeploymentLibrary
\end{displaycode}

Once declared, you should execute this method with \textcode{TBApplicationRootComponent initialize}. Indeed, class-side \textcode{initialize} methods are executed at loading-time of a class but since the class already exists, we must execute it by hand.

We also add a method named \textcode{canBeRoot} to specify that \textcode{TBApplicationRootComponent} is not a simple Seaside component but a complete application. This component will be automatically instantiated when a user connects to the application.

\begin{displaycode}{smalltalk}
TBApplicationRootComponent class >> canBeRoot
   ^ true
\end{displaycode}


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/BrowseApplications.png}\caption{TinyBlog is a Registered Seaside Application.\label{BrowseApplications}}\end{center}
\end{figure}


You can verify that your application is correctly registered by Seaside by connecting to the Seaside server through your web browser, click on \symbol{34}Browse the applications installed in your image\symbol{34} and then see that TinyBlog appears in the list.
\section{First Simple Rendering}
Let's add an instance  method named \textcode{renderContentOn:} in \textcode{rendering}  protocol to make our application displaying something.

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> renderContentOn: html
   html text: 'TinyBlog'
\end{displaycode}

If you open \url{http://localhost:8080/TinyBlog} in your web browser, the displayed a web should look like to the one on figure \ref{EmptyPage}.


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/TinyBlog-EmptyPage2.png}\caption{Your First Seaside Web Page.\label{EmptyPage}}\end{center}
\end{figure}


You can customize the web page header and declare it as HTML 5 compliant by redefining the \textcode{updateRoot:} method.

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> updateRoot: anHtmlRoot
   super updateRoot: anHtmlRoot.
   anHtmlRoot beHtml5.
   anHtmlRoot title: 'TinyBlog'.
\end{displaycode}

The \textcode{TBApplicationRootComponent} component is the root component of our application.  It will not display a lot of things but will contain and display other components. For example, a component to display posts to the blog readers, a component to administrate the blog and its posts, ... Therefore, we decided that the \textcode{TBApplicationRootComponent} component will contain components inheriting from the abstract class named \textcode{TBScreenComponent} that we will now define.
\section{Visual Components for TinyBlog}
We are now ready to define multiple visual components for our application. The first chapters of \url{http://book.seaside.st} can help you and give more details than this tutorial if needed.

Figure \ref{ComponentOverview} shows the different components to develop and where they are taking place.


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/ComponentOverview.pdf}\caption{TinyBlog Components.\label{ComponentOverview}}\end{center}
\end{figure}

\subsection{TBScreenComponent Component }
All components contained in \textcode{TBApplicationRootComponent} will be subclasses of the abstract class \textcode{TBScreenComponent}. This class allows us to factorize shared behaviors between all our components.  

\begin{displaycode}{smalltalk}
WAComponent subclass: #TBScreenComponent
   instanceVariableNames: ''
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

All components need to access the model of our application. Therefore, in the 'accessing' protocol, we add a \textcode{blog} method that returns an instance of \textcode{TBBlog} (here the singleton). 

\begin{displaycode}{smalltalk}
TBScreenComponent >> blog
   "Return the current blog in the future we will ask the 
   session to return the blog of the currently logged user."
   ^ TBBlog current
\end{displaycode}

Inspect now the blog object returned  by \textcode{TBBlog current} and verify that it contains some posts.  If it does not, execute: \textcode{TBBlog current createDemoPost}.

In the future, if you want to manage multiple blogs, you will need to modify this method and use information stored in the active session to retrieve the current user/blog (cf. \textcode{TBSession} later on). 


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/SeasideBootstrap.png}\caption{Bootstrap Library.\label{bootstrap}}\end{center}
\end{figure}

\section{Bootstrap for Seaside}
The Bootstrap library is accessible from Seaside as we will see. If you access the Seaside Bootstrap application in your web browser (\textbf{bootstrap} link in the list of Seaside application or directly access \url{http://localhost:8080/bootstrap}) you can see several examples as shown in figure \ref{bootstrap}.

If you click on the \textbf{Examples} link at the bottom of the web page you can see some graphical elements and their respective code to integrate them in your application (cf. figure \ref{examplebootstrap}). 


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/TBSAlert.png}\caption{Bootstrap Elements and their code.\label{examplebootstrap}}\end{center}
\end{figure}

\subsection{Bootstrap}
The Seaside Bootstrap library code and documentation is available here: \url{http://smalltalkhub.com/#!/~TorstenBergmann/Bootstrap}.
There is also an online demo here: \url{http://pharo.pharocloud.com/bootstrap}. This library is already loaded in the PharoWeb image used in this tutorial.
\section{Definition of TBHeaderComponent}
Let's define a component to display the top of all components in our application.

\begin{displaycode}{smalltalk}
WAComponent subclass: #TBHeaderComponent
   instanceVariableNames: ''
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

The 'rendering' protocol contains the \textcode{renderContentOn:} method that displays our application header.

\begin{displaycode}{smalltalk}
TBHeaderComponent >> renderContentOn: html
   html tbsNavbar beDefault with: [
      html tbsNavbarBrand
         url: '#';
         with: 'TinyBlog' ]
\end{displaycode}

Our header is a simple navigation bar of Bootstrap (See Figure \ref{navBlog})

By default, in Bootstrap navigation bars, there is a link on tbsNavbarBrand. Since we consider that useless in our application, we used a \textcode{'\#'} anchor. Now, when the user click on the title link, nothing happen.

Usually, clicking on an application title bring back the user to the web application home page.
\section{Header Use}
It is not desirable to instantiate the \textcode{TBHeaderComponent} each time a component is called. 
So, we add an instance variable named \textcode{header} in the \textcode{TBScreenComponent} component and initializes it in its \textcode{initialize} method.

\begin{displaycode}{smalltalk}
WAComponent subclass: #TBScreenComponent
   instanceVariableNames: 'header'
   classVariableNames: ''
   package: 'TinyBlog'
	
TBScreenComponent >> initialize
   super initialize.
   header := TBHeaderComponent new.
\end{displaycode}


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/navBlog.png}\caption{TinyBlog Application with a Header Navigation Bar.\label{navBlog}}\end{center}
\end{figure}

\subsection{Composite-Component relationship}
In Seaside, subcomponents of a component must be returned by the composite when sending it the \textcode{children} message. 
So, we must define that the \textcode{TBHeaderComponent} instance is a children of the \textcode{TBScreenComponent} component in the Seaside component hierachy (and not in the Pharo classes hierarchy). We do so by specializing the method \textcode{children}.

\begin{displaycode}{smalltalk}
TBScreenComponent >> children
   ^ OrderedCollection with: header
\end{displaycode}

In the \textcode{renderContentOn:} method ('rendering' protocol), we can now display the subcomponent (the header):

\begin{displaycode}{smalltalk}
TBScreenComponent >> renderContentOn: html
   html render: header
\end{displaycode}
\section{Using the Screen Component in our Application}
Temporarily, we will directly use the \textcode{TBScreenComponent} while we develop other components.
So, we instantiate it in \textcode{initialize} method and store it in the instance variable \textcode{main}. 

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> initialize
   super initialize.
   main := TBScreenComponent new. 
\end{displaycode}

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> renderContentOn: html
   html render: main
\end{displaycode}

We declare the containment relationship by returning \textcode{main} as children of \textcode{TBApplicationRootComponent}.

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> children
   ^ { main }
\end{displaycode}

If you refresh your application in a web browser, you should obtain what is depicted on Figure \ref{navBlog}.
\subsubsection{Possible Enhancements}
The blog name should be customizable using an instance vairable in the \textcode{TBBlog} class and the application header component should display this title.
\section{List of Posts}
We will now display the list of all posts - which is the primary goal in fact. Remember that we speak about the public access to the blog here and not the administration interface that will be developped later.

Let's create a \textcode{TBPostsListComponent} inheriting from \textcode{TBScreenComponent}:

\begin{displaycode}{smalltalk}
TBScreenComponent subclass: #TBPostsListComponent
   instanceVariableNames: ''
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

Add a temporary \textcode{renderContentOn:} method (in the 'rendering' protocole) to test during development (cf. Figure \ref{elementary}).

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderContentOn: html
   super renderContentOn: html.
   html text: 'Blog Posts here !!!'
\end{displaycode}

Add this new component in the root application component: 

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> initialize
   super initialize.
   main := TBPostsListComponent new. 
\end{displaycode}

Modify this method is not a good practice so we add a setter method to dynamically change the component to display in the future.
But, we keep that by default the \textcode{TBPostsListComponent} component will be displayed.

\begin{displaycode}{smalltalk}
TBApplicationRootComponent >> main: aComponent
   main := aComponent
\end{displaycode}


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/ElementaryListPost.png}\caption{TinyBlog with a First List of Posts Component.\label{elementary}}\end{center}
\end{figure}

\section{A Post Component}
Now e will define \textcode{TBPostComponent} to display the deatils of a Post.
Each post will be graphically displayed by an instance of \textcode{TBPostComponent} which will show the post title, its date and its content.

\begin{displaycode}{smalltalk}
WAComponent subclass: #TBPostComponent
   instanceVariableNames: 'post'
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostComponent >> initialize
      super initialize.
      post := TBPost new.
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostComponent >> title
   ^ post title
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostComponent >> text
   ^ post text
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostComponent >> date
   ^ post date
\end{displaycode}

The \textcode{renderContentOn:} method defines the HTML rendering of a post.

\begin{displaycode}{smalltalk}
TBPostComponent >> renderContentOn: html
   html heading level: 2; with: self title.
   html heading level: 6; with: self date.
   html text: self text
\end{displaycode}
\subsubsection{About HTML Forms}
Next week, we will develop the administration web UI using Magritte and this will demonstrate that it is not common at all to manually develop components as we explained above. Indeed, Magritte is used to describe model data and then offers automatic Seaside component generators. As we will see, all the code of the previous section could be done just with:

\begin{displaycode}{smalltalk}
TBPostComponent >> renderContentOn: html
   html render: post asComponent
\end{displaycode}
\section{Display Posts}
To display all visible posts in the database, we just need to modify the  \textcode{TBPostsListComponent \textgreater{}\textgreater{} renderContentOn:} method:

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderContentOn: html
   super renderContentOn: html.
   self blog allVisibleBlogPosts do: [ :p |
      html render: (TBPostComponent new post: p) ]
\end{displaycode}

Refresh you web browser and you should get ... the following error:

\textcode{Not Found /TinyBlog}
\section{Debugging Errors }
By default, when an error occurs in a web application, Seaside returns an HTML page that contains a generic message. You can change this message or during development, you can configure Seaside to open a debugger directly in Pharo IDE. To configure Seaside, just execute the following snippet:

\begin{displaycode}{smalltalk}
(WAAdmin defaultDispatcher handlerAt: 'TinyBlog') 
    exceptionHandler: WADebugErrorHandler
\end{displaycode}

Now, if you refresh the web page in your browser, a debugger should open on Pharo side. If you analyze the stack, you should see that we forgot to define the following method:

\begin{displaycode}{smalltalk}
TBPostComponent >> post: aPost
	post := aPost
\end{displaycode}

The web application should now correctly renders and you obtains what is shown in Figure \ref{better}.


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/betterListPosts.png}\caption{TinyBlog with a List of Posts.\label{better}}\end{center}
\end{figure}

\section{Displaying the List of Posts with Bootstrap}
Let's use Bootstrap to make the list of posts more beautiful using a Bootstrap container.

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderContentOn: html
   super renderContentOn: html.
   html tbsContainer: [ 
   self blog allVisibleBlogPosts do: [ :p |
      html render: (TBPostComponent new post: p) ] ]
\end{displaycode}

Yous web application whould look like Figure \ref{container}.

\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/ContainerList.png}\caption{TinyBlog with a Bootstrap List of Posts \'{e}l\'{e}mentaire.\label{container}}\end{center}
\end{figure}

\section{Displaying Posts by Category}
Posts are classified into categories. By default, a post is classified into a special category named \symbol{34}Unclassified\symbol{34} if nothing is specified.

We will now create a new component named \textcode{TBCategoriesComponent} to manage the list of categories.
\subsection{Category Component Definition}
\textcode{TBCategoriesComponent} is responsible to display the list of all categories available in the database (the model) and also to select one category. This component should be able to communicate with the \textcode{TBPostsListComponent} to pass it the currently selected category.

\begin{displaycode}{smalltalk}
WAComponent subclass: #TBCategoriesComponent
   instanceVariableNames: 'categories postsList'
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> categories
   ^ categories
\end{displaycode}

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> categories: aCollection
   categories := aCollection
\end{displaycode}

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> postsList: aComponent
      postsList := aComponent
\end{displaycode}

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> postsList
   ^ postsList
\end{displaycode}

On class-side, we define a creation method.

\begin{displaycode}{smalltalk}
TBCategoriesComponent class >> categories: aCollectionOfCategories postsList: aTBScreen
   ^ self new categories: aCollectionOfCategories; postsList: aTBScreen 
\end{displaycode}

The \textcode{selectCategory:} method (protocol 'action') communicates the currently selected category to the \textcode{TBPostsListComponent}.

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> selectCategory: aCategory
   postsList currentCategory: aCategory
\end{displaycode}

In \textcode{TBPostsListComponent}, we now add an instance variable to store the current category.

\begin{displaycode}{smalltalk}
TBScreenComponent subclass: #TBPostsListComponent
   instanceVariableNames: 'currentCategory'
   classVariableNames: ''
   package: 'TinyBlog-Components'
\end{displaycode}

\begin{displaycode}{smalltalk}
TBScreenComponent >> currentCategory
   ^ currentCategory

TBScreenComponent >> currentCategory: anObject
   currentCategory := anObject
\end{displaycode}
\section{Category Rendering }
We add a rendering method ('rendering' protocol) to display one category.
A category is rendered as a link and that make this category the current one if the user click on it.

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> renderCategoryLinkOn: html with: aCategory
   html tbsLinkifyListGroupItem
      callback: [ self selectCategory: aCategory ];
      with: aCategory
\end{displaycode}

Finally, the \textcode{renderContentOn:} method of the \textcode{TBCategoriesComponent} is now straighforward, it just iterates over all categories and renders them using bootstrap brushes:

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> renderContentOn: html
   html tbsListGroup: [
      html tbsListGroupItem
         with: [  html strong: 'Categories' ].
      categories do: [ :cat | 
         self renderCategoryLinkOn: html with: cat ]]
\end{displaycode}

It remains to display the list of categories in the root application component and refresh the list of displayed posts regarding the currently selected category.
\section{Refreshing the List of Posts}
We have to modify some methods of \textcode{TBPostsListComponent}.

First, the \textcode{readSelectedPosts} method returns the posts to display. 
If the current category is \textcode{nil}, it means that the user did not select any category yet and all visible posts of the database are displayed.
If the user has selected a category, only posts of this category are displayed.

\begin{displaycode}{smalltalk}
TBPostsListComponent >> readSelectedPosts
   ^ self currentCategory
      ifNil: [ self blog allVisibleBlogPosts ]
      ifNotNil: [ self blog allVisibleBlogPostsFromCategory: self currentCategory ]
\end{displaycode}

Then, the method that renders the list of posts can be modified as follows:

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderContentOn: html
   super renderContentOn: html.
   html render: (TBCategoriesComponent
               categories: (self blog allCategories)
               postsList: self).
   html tbsContainer: [ 
      self readSelectedPosts do: [ :p |
         html render: (TBPostComponent new post: p) ] ]
\end{displaycode}

An instance of \textcode{TBCategoriesComponent} is rendered on the web page and allows users to select a category (See Figure \ref{ugly}).


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/categoriesUgly.png}\caption{Select a Category to Filter Posts.\label{ugly}}\end{center}
\end{figure}

\section{Look and Layout}
We will improve the layout of \textcode{TBPostsListComponent} using a repsonsive design for the list of posts.
It means that the CSS will adapt the component size and placement based on the available space. 

Components are displayed inside two Bootstrap containers in a row and two columns.
Column width is determined according to the resolution (viewport) of the displaying device.  
The 12 columns of the Bootstrap grid are splitted between the list of categories and list of posts. 
If a low resolution is used, the list of categories will be above the list of posts (each lists will occupy 100\% of the width of the container). 

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderContentOn: html
   super renderContentOn: html.
   html tbsContainer: [
      html tbsRow showGrid;
         with: [
            html tbsColumn
               extraSmallSize: 12;
               smallSize: 2;
               mediumSize:  4;
               with: [
                  html render: (TBCategoriesComponent
                    categories: (self blog allCategories)
                    postsList: self) ].
      html tbsColumn
               extraSmallSize: 12;
               smallSize: 10;
               mediumSize: 8;
               with: [
         self readSelectedPosts do: [ :p |
             html render: (TBPostComponent new post: p) ] ] ] ]
\end{displaycode}

You should now obtain the same look as in Figure \ref{nicer}.


\begin{figure}

\begin{center}
\includegraphics[width=0.75\textwidth]{figures/NicerCategories.png}\caption{TinyBlog with a Responsive Design.\label{nicer}}\end{center}
\end{figure}


When you select a category, the list of posts is correctly refreshed.
However, the current category is not highlighted.
To introduce this feature, we modify the following method:

\begin{displaycode}{smalltalk}
TBCategoriesComponent >> renderCategoryLinkOn: html with: aCategory
   html tbsLinkifyListGroupItem
      class: 'active' if: aCategory = self postsList currentCategory ;
      callback: [ self selectCategory: aCategory ]; 
      with: aCategory
\end{displaycode}

The application works well but you must not keep the current implementation of \textcode{TBPostsListComponent \textgreater{}\textgreater{} renderContentOn:}. 
This method is too long and cannot be easily reused. Propose a solution.
\section{Our Solution: Small Methods}
\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderContentOn: html
   super renderContentOn: html.
   html
      tbsContainer: [ 
         html tbsRow
            showGrid;
            with: [ self renderCategoryColumnOn: html.
                  self renderPostColumnOn: html ] ]
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderCategoryColumnOn: html
   html tbsColumn
      extraSmallSize: 12;
      smallSize: 2;
      mediumSize: 4;
      with: [ self basicRenderCategoriesOn: html ]
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostsListComponent >> basicRenderCategoriesOn: html
   ^ html render: (TBCategoriesComponent 
         categories: self blog allCategories 
         postsList: self)
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostsListComponent >> renderPostColumnOn: html
   html tbsColumn
         extraSmallSize: 12;
         smallSize: 10;
         mediumSize: 8;
         with: [ self basicRenderPostsOn: html ] 
\end{displaycode}

\begin{displaycode}{smalltalk}
TBPostsListComponent >> basicRenderPostsOn: html
   ^ self readSelectedPosts do: [ :p | 
        html render: (TBPostComponent new post: p) ]
\end{displaycode}

We are now ready to define a administrative UI for TinyBlog.
\section{Possible Enhancements}
For example, you can:

\begin{itemize}
\item sort categories by name
\item add a link named 'All' in the list of categories to display all visible posts regardless of their category
\end{itemize}

Figure \ref{final} shows the final application you may have developped.


\begin{figure}

\begin{center}
\includegraphics[width=0.85\textwidth]{figures/finalPublicWebPage.png}\caption{Final TinyBlog Public UI.\label{final}}\end{center}
\end{figure}



\ifx\wholebook\relax\else
\end{document}
\fi