Last modified by Bastian Triller on 2021/08/07 03:59

From version 8.1
edited by Pascal Robert
on 2012/08/09 03:52
Change comment: There is no comment for this version
To version 18.1
edited by Pascal Robert
on 2012/12/12 07:59
Change comment: Migrated to Confluence 5.3

Summary

Details

Page properties
Content
... ... @@ -1,7 +5,3 @@
1 -{{info}}
2 -Work in progress
3 -{{/info}}
4 -
5 5  So far, we have seen two of the technologies, D2W and ERRest, that Project Wonder offers for viewing and managing the data. In this tutorial, we will show how to do it with the "stateful" way of doing things. Stateful have been around since the beginning of WebObjects in 1996, so it's the oldest way of presenting data and constructing pages.
6 6  
7 7  Stateful means that you don't have to worry about creating sessions and keeping track of data coming from HTML input fields and controls. In fact, D2W is also stateful.
... ... @@ -15,17 +15,17 @@
15 15  
16 16  Let's start by creating a new project in Eclipse. You need to create a **Wonder Application** project type, and name it **StatefulBlog**.
17 17  
18 -[[image:Capture d’écran 2012-08-06 à 04.56.13.png||border="1"]]
14 +[[image:attach:Capture d’écran 2012-08-06 à 04.56.13.png]]
19 19  
20 -Just like the D2W tutorial, you need to link the application with the **BlogCommon**, **Ajax** and **H2PlugIn** frameworks. To do so, right-click on **StatefulBlog** and select **Build Path** > **Configure Build Path**. 
16 +Just like the D2W tutorial, you need to link the application with the **BlogCommon**, **Ajax** and **H2PlugIn** frameworks. To do so, right-click on **StatefulBlog** and select **Build Path** -> **Configure Build Path**.
21 21  
22 -[[image:Capture d’écran 2012-07-29 à 14.25.46.png||border="1"]]
18 +[[image:attach:Capture d’écran 2012-07-29 à 14.25.46.png]]
23 23  
24 -In the **Libraries** tab, click on **Add Library**. Select **WebObjects Frameworks** and click **Next**. Check **Ajax**, **BlogCommon** and **H2PlugIn** from the list and click **Finish**. The **Libraries** tab should look like this:
20 +In the **Libraries** tab, click on **Add Library**. Select **WebObjects Frameworks** and click **Next**. Check **Ajax**, **BlogCommon** and **H2PlugIn** from the list and click **Finish**. The **Libraries** tab should look like this:
25 25  
26 -[[image:Capture d’écran 2012-08-06 à 05.15.32.png||border="1"]]
22 +[[image:attach:Capture d’écran 2012-08-06 à 05.15.32.png]]
27 27  
28 -We are ready to code Open the **Components** folder of the project, and open **Main WO**. In the **Related** view (bottom-right), you see that all related files of the component are listed, and we need to open the Java code associated with the component. To do so, in the **Related** view, double-click on **Main.java** to open the Java class into an editor.
24 +We are ready to code! Open the **Components** folder of the project, and open **Main WO**. In the **Related** view (bottom-right), you see that all related files of the component are listed, and we need to open the Java code associated with the component. To do so, in the **Related** view, double-click on **Main.java** to open the Java class into an editor.
29 29  
30 30  In **Main.java**, we need some Java code to get the list of blog entries so that we can show that list into the component. The following code will do what we need:
31 31  
... ... @@ -74,7 +74,7 @@
74 74  
75 75  {{/code}}
76 76  
77 -[[ERXBatchingDisplayGroup>>http://jenkins.wocommunity.org/job/Wonder/lastSuccessfulBuild/javadoc/er/extensions/appserver/ERXDisplayGroup.html]] is a subclass of WODisplayGroup, a utility that adds multiple actions and logic to a list of objects. One of the best features of ERXBatchingDisplayGroup is that it does real batching (if the RDBMS that you use supports it), so that means that if we specify a batch of 20 objects (//dg.setNumberOfObjectsPerBatch(20)//), it will fetch only the first 20 objects from the database, and if you switch to the next batch, the display group will go to the database to get the next 20 objects. ERXBatchingDisplayGroup is useful if you known that your list will contains hundred of objects.
73 +[[ERXBatchingDisplayGroup>>url:http://jenkins.wocommunity.org/job/Wonder/lastSuccessfulBuild/javadoc/er/extensions/appserver/ERXDisplayGroup.html||shape="rect"]] is a subclass of WODisplayGroup, a utility that adds multiple actions and logic to a list of objects. One of the best features of ERXBatchingDisplayGroup is that it does real batching (if the RDBMS that you use supports it), so that means that if we specify a batch of 20 objects (//dg.setNumberOfObjectsPerBatch(20)//), it will fetch only the first 20 objects from the database, and if you switch to the next batch, the display group will go to the database to get the next 20 objects. ERXBatchingDisplayGroup is useful if you known that your list will contains hundred of objects.
78 78  
79 79  Let's edit the component. Open **Main.wo** and edit the content in the top panel to be:
80 80  
... ... @@ -91,11 +91,11 @@
91 91   <wo:if condition="$displayGroup.hasMultipleBatches">
92 92   <div>
93 93   <wo:link action="$displayGroup.displayPreviousBatch">Previous</wo:link>
94 - | Batch
90 + | Batch
95 95   <wo:str value="$displayGroup.currentBatchIndex" />
96 - of
92 + of
97 97   <wo:str value="$displayGroup.batchCount" />
98 - |
94 + |
99 99   <wo:link action="$displayGroup.displayNextBatch">Next</wo:link>
100 100   </div>
101 101   </wo:if>
... ... @@ -102,9 +102,9 @@
102 102   <wo:loop list="$displayGroup.displayedObjects" item="$blogEntryItem">
103 103   <p><wo:str value="$blogEntryItem.title" /></p>
104 104   <p><wo:str value="$blogEntryItem.content" escapeHTML="false" /></p>
105 - <p>Created by
101 + <p>Created by
106 106   <wo:str value="$blogEntryItem.author.fullName" />
107 - on
103 + on
108 108   <wo:str value="$blogEntryItem.creationDate" />
109 109   </p>
110 110   <hr />
... ... @@ -122,44 +122,93 @@
122 122  
123 123  {{code}}
124 124  
125 - private Author _loggedAuthor;
121 +private Author _loggedAuthor;
126 126  
127 127   public Session() {
128 128   this._loggedAuthor = null;
129 129   }
130 -
126 +
131 131   public Author loggedAuthor() {
132 132   return this._loggedAuthor;
133 133   }
134 -
130 +
135 135   public void setAuthor(Author loggedAuthor) {
136 136   this._loggedAuthor = loggedAuthor;
137 137   }
138 138  
135 +{{/code}}
139 139  
137 +Save the file. Now open Author.java and add the following method:
138 +
139 +{{code}}
140 +public static Author validateLogin(EOEditingContext editingContext, String _emailAddress) {
141 +     Author user = Author.fetchAuthor(ERXEC.newEditingContext(), Author.EMAIL.eq(_emailAddress));
142 + return user;
143 +}
144 +
140 140  {{/code}}
141 141  
142 -Save the file. Next: we need to add a component to present the login form to the user. Right-click on the **Components** folder in the project, and select **New** -> **WOComponent**. Change the name of the component to be **AdminMainPage** and change the superclass to **er.extensions.components.ERXComponent**.
147 +Next: we need to add a component to present the login form to the user. Right-click on the **Components** folder in the project, and select **New** -> **WOComponent**. Change the name of the component to be **AdminMainPage** and change the superclass to **er.extensions.components.ERXComponent**.
143 143  
144 -After the component have been created, open **AdminMainPage.java** and add the following code:
149 +After the component have been created, open **AdminMainPage.java** and override the content of the class with the following code:
145 145  
146 146  {{code}}
147 147  
153 +package your.app.components;
154 +
148 148  import your.app.Session;
156 +import your.app.model.Author;
157 +import your.app.model.BlogEntry;
149 149  
159 +import com.webobjects.appserver.WOActionResults;
160 +import com.webobjects.appserver.WOContext;
161 +import com.webobjects.eoaccess.EODatabaseDataSource;
162 +import com.webobjects.eocontrol.EOEditingContext;
163 +
164 +import er.extensions.batching.ERXBatchingDisplayGroup;
165 +import er.extensions.components.ERXComponent;
166 +import er.extensions.eof.ERXEC;
167 +
168 +public class AdminMainPage extends ERXComponent {
169 +
170 + private ERXBatchingDisplayGroup<BlogEntry> _dg;
171 +
172 + public AdminMainPage(WOContext context) {
173 + super(context);
174 + EODatabaseDataSource dataSource = new EODatabaseDataSource(editingContext(), BlogEntry.ENTITY_NAME);
175 + _dg = new ERXBatchingDisplayGroup<BlogEntry>();
176 + _dg.setNumberOfObjectsPerBatch(20);
177 + _dg.setDataSource(dataSource);
178 + _dg.setObjectArray(BlogEntry.fetchBlogEntries(editingContext(), BlogEntry.AUTHOR.eq(session().loggedAuthor()), BlogEntry.LAST_MODIFIED.descs()));
179 + }
180 +
181 + public ERXBatchingDisplayGroup<BlogEntry> displayGroup() {
182 + return this._dg;
183 + }
184 +
150 150   private String _emailAddress;
151 -
186 +
152 152   public String emailAddress() {
153 153   return this._emailAddress;
154 154   }
155 -
190 +
156 156   public void setEmailAddress(String emailAddress) {
157 157   this._emailAddress = emailAddress;
158 158   }
159 159  
195 + private BlogEntry _blogEntryItem;
196 +
197 + public void setBlogEntryItem(BlogEntry blogEntryItem) {
198 + this._blogEntryItem = blogEntryItem;
199 + }
200 +
201 + public BlogEntry blogEntryItem() {
202 + return this._blogEntryItem;
203 + }
204 +
160 160   @Override
161 - public WOSession session() {
162 - return ((Session)session());
206 + public Session session() {
207 + return ((Session)super.session());
163 163   }
164 164  
165 165   public boolean isLogged() {
... ... @@ -166,4 +166,221 @@
166 166   return ((session()).loggedAuthor() == null) ? false: true;
167 167   }
168 168  
214 + private EOEditingContext _ec;
215 +
216 + public EOEditingContext editingContext() {
217 + if (_ec == null) {
218 + _ec = ERXEC.newEditingContext();
219 + }
220 + return _ec;
221 + }
222 +
223 + private String _errorMessage = null;
224 +
225 + public String errorMessage() {
226 + return this._errorMessage;
227 + }
228 +
229 + public WOActionResults login() {
230 + Author loggedAuthor = Author.validateLogin(editingContext(), _emailAddress);
231 + if (loggedAuthor != null) {
232 + session().setAuthor(loggedAuthor);
233 + } else {
234 + _errorMessage = "Invalid email address";
235 + }
236 + return null;
237 + }
238 +}
239 +
169 169  {{/code}}
241 +
242 +You will notice that we are using a ERXBatchingDisplayGroup again. But this time, when we call _//dg.setObjectArray//, we set the array of objects so that only the blog entries created by the logged author are displayed.
243 +
244 +Open **AdminMainPage.wo** and override all the content between the <body> tag to be:
245 +
246 +{{code}}
247 +
248 +<wo:AjaxUpdateContainer id="main">
249 + <wo:if condition="$isLogged">
250 + <wo:if condition="$displayGroup.hasMultipleBatches">
251 + <div>
252 + <wo:link action="$displayGroup.displayPreviousBatch">Previous</wo:link>
253 + | Batch
254 + <wo:str value="$displayGroup.currentBatchIndex" />
255 + of
256 + <wo:str value="$displayGroup.batchCount" />
257 + |
258 + <wo:link action="$displayGroup.displayNextBatch">Next</wo:link>
259 + </div>
260 + </wo:if>
261 + <table>
262 + <tr>
263 + <th><wo:WOSortOrder displayGroup="$displayGroup" key="title" /> Title</th>
264 + <th>Author</th>
265 + <th><wo:WOSortOrder displayGroup="$displayGroup" key="creationDate" /> Created on</th>
266 + <th><wo:WOSortOrder displayGroup="$displayGroup" key="lastModified" /> Last modified</th>
267 + </tr>
268 + <wo:loop list="$displayGroup.displayedObjects" item="$blogEntryItem">
269 + <tr>
270 + <td>
271 + <wo:str value="$blogEntryItem.title" />
272 + </td>
273 + <td> <wo:str value="$blogEntryItem.author.fullName" /> </td>
274 + <td> <wo:str value="$blogEntryItem.creationDate" dateformat="%Y/%m/%d" /> </td>
275 + <td> <wo:str value="$blogEntryItem.lastModified" dateformat="%Y/%m/%d" /> </td>
276 + </tr>
277 + </wo:loop>
278 + </table>
279 + </wo:if>
280 + <wo:else>
281 + <wo:if condition="$errorMessage">
282 + <span style="text-color: red;">Error: <wo:str value="$errorMessage" /></span>
283 + </wo:if>
284 + <wo:form>
285 + <div>
286 + <label>Email address:</label>
287 + <wo:textField value="$emailAddress" />
288 + </div>
289 + <div><wo:AjaxSubmitButton updateContainerID="main" action="$login" value="Login" /></div>
290 + </wo:form>
291 + </wo:else>
292 + </wo:AjaxUpdateContainer>
293 +
294 +{{/code}}
295 +
296 +Last step: we need a link to the admin page. Open **Main.wo** and just before the </body> tag, add the following:
297 +
298 +{{code}}
299 +
300 +<wo:link pageName="AdminMainPage">Admin</wo:link>
301 +
302 +{{/code}}
303 +
304 +Save everything, run the app and try to login. If login is not successful, you will get an error message. If login is valid, you will see the blog entries that you created.
305 +
306 +For the last part of this tutorial, we are going to add a link on each blog entry in the list that will bring us to a edit page where we can modify a blog entry. We are also going to add a link to create a new blog entry.
307 +
308 +Create a new component, and name it **EditBlogEntry**. Open **EditBlogEntry.java** and override the code with:
309 +
310 +{{code}}
311 +
312 +package your.app.components;
313 +
314 +import your.app.Session;
315 +import your.app.model.Author;
316 +import your.app.model.BlogEntry;
317 +
318 +import com.webobjects.appserver.WOActionResults;
319 +import com.webobjects.appserver.WOContext;
320 +import com.webobjects.eocontrol.EOEditingContext;
321 +
322 +import er.extensions.components.ERXComponent;
323 +import er.extensions.eof.ERXEC;
324 +import er.extensions.eof.ERXEOControlUtilities;
325 +
326 +public class EditBlogEntry extends ERXComponent {
327 +
328 + public EditBlogEntry(WOContext context) {
329 + super(context);
330 + }
331 +
332 + private BlogEntry _blogEntry;
333 +
334 + public BlogEntry blogEntry() {
335 + return this._blogEntry;
336 + }
337 +
338 + public void setBlogEntry(BlogEntry blogEntry) {
339 + if (blogEntry == null) {
340 + this._blogEntry = ERXEOControlUtilities.createAndInsertObject(editingContext(), BlogEntry.class);
341 + Author localUser = ERXEOControlUtilities.localInstanceOfObject(editingContext(), session().loggedAuthor());
342 + this._blogEntry.setAuthorRelationship(localUser);
343 + } else {
344 + this._blogEntry = ERXEOControlUtilities.localInstanceOfObject(editingContext(), blogEntry);
345 + }
346 + }
347 +
348 + private EOEditingContext _ec;
349 +
350 + public EOEditingContext editingContext() {
351 + if (_ec == null) {
352 + _ec = ERXEC.newEditingContext();
353 + }
354 + return _ec;
355 + }
356 +
357 + @Override
358 + public Session session() {
359 + return ((Session)super.session());
360 + }
361 +
362 + public WOActionResults save() {
363 + editingContext().saveChanges();
364 + return pageWithName(AdminMainPage.class);
365 + }
366 +}
367 +
368 +{{/code}}
369 +
370 +Open **EditBlogEntry.wo** and between the <body> tag, add the following:
371 +
372 +{{code}}
373 +
374 +<wo:form>
375 + <div>
376 + <label>Title:</label>
377 + <wo:textfield value="$blogEntry.title" />
378 + </div>
379 + <div>
380 + <label>Content:</label>
381 + <wo:text value="$blogEntry.content" rows="20" cols="80" />
382 + </div>
383 + <div>Author: <wo:str value="$session.loggedAuthor.fullName" /></div>
384 + <div><wo:submitButton action="$save" value="Save changes" /></div>
385 + </wo:form>
386 +
387 +{{/code}}
388 +
389 +We now have a form to edit or create a blog entry. Save the component and the Java class, and open **AdminMainPage.java** to add the following code:
390 +
391 +{{code}}
392 +
393 +public WOActionResults editBlogEntry() {
394 + EditBlogEntry nextPage = pageWithName(EditBlogEntry.class);
395 + nextPage.setBlogEntry(_blogEntryItem);
396 + return nextPage;
397 + }
398 +
399 + public WOActionResults createBlogEntry() {
400 + EditBlogEntry nextPage = pageWithName(EditBlogEntry.class);
401 + nextPage.setBlogEntry(null);
402 + return nextPage;
403 + }
404 +
405 +{{/code}}
406 +
407 +Open **AdminMainPage.wo** and just after <wo:if condition="$isLogged">, add the following line:
408 +
409 +{{code}}
410 +
411 +<div><wo:link action="$createBlogEntry">Create a new blog entry</wo:link></div>
412 +
413 +{{/code}}
414 +
415 +Find this line:
416 +
417 +{{code}}
418 +
419 +<wo:str value="$blogEntryItem.title" />
420 +
421 +{{/code}}
422 +
423 +and replace it with:
424 +
425 +{{code}}
426 +
427 +<wo:link action="$editBlogEntry"><wo:str value="$blogEntryItem.title" /></wo:link>
428 +
429 +{{/code}}
430 +
431 +Save everything, run the app, click on the "admin" link, login and check if you can create or edit a blog entry. Everything should be working, and just created your first stateful Project Wonder application! [[It's time to deploy an application>>doc:Your First Deployment]].