Wiki source code of Your First D2W Project

Last modified by Bastian Triller on 2013/01/12 00:43

Hide last authors
Pascal Robert 3.1 1 {{info}}
Steve Peery 18.1 2 Work in progress
Pascal Robert 3.1 3 {{/info}}
4
Steve Peery 18.1 5 Now that our data model is in a framework and that we have a basic REST application working, we are going to build a DirectToWeb application to manage the blog system. But first, what is DirectToWeb (D2W)? D2W is a rules-based application model where you can change the behavior of the application by rules. D2W is perfect for "admin" apps, for applications that share a common model or for "**CRUD**" (**C**reate **R**ead **U**pdate **D**estroy) applications.
Pascal Robert 3.1 6
Pascal Robert 6.1 7 So like I said, we are going to build a D2W app that will allow us to update and create blog entries and authors. Let's start by creating a //Wonder D2W Application// in Eclipse:
Pascal Robert 3.1 8
Steve Peery 18.1 9 [[image:attach:Capture d’écran 2012-07-29 à 14.22.22.png]]
Pascal Robert 6.1 10
Steve Peery 18.1 11 Name it **D2WBlog**. The next step is to link the **BlogCommon** framework to the D2W app. To do so, right-click on **D2WBlog** and select **Build Path** -> **Configure Build Path**.
Pascal Robert 6.1 12
Steve Peery 18.1 13 [[image:attach:Capture d’écran 2012-07-29 à 14.25.46.png]]
Pascal Robert 6.1 14
Steve Peery 18.1 15 In the **Libraries** tab, click on **Add Library**. Select **WebObjects Frameworks** and click **Next**. Check **BlogCommon** and **H2PlugIn** from the list and click **Finish**. The **Libraries** tab should look like this:
Pascal Robert 6.1 16
Steve Peery 18.1 17 [[image:attach:Capture d’écran 2012-07-29 à 14.32.51.png]]
Pascal Robert 3.1 18
Steve Peery 18.1 19 It's time to run the app. Right-click on **Application.java** and select **Run As** -> **WO Application**. By default, a D2W app will always display a login form and the login method is connected to a DirectAction that should handle the authentification. The default implementation of that DirectAction (the loginAction method in the DirectAction class) don't do anything special:
Pascal Robert 6.1 20
21 {{code language="java"}}
22
23 public WOActionResults loginAction() {
24 String username = request().stringFormValueForKey("username");
25 String password = request().stringFormValueForKey("password");
26 return D2W.factory().defaultPage(session());
27 }
28
29 {{/code}}
30
31 Just click the **Login** button and you will get into the application. Navigate in the application, but you will see that it don't do anything special and you don't see data. You can terminate the app (by clicking the red stop button in Eclipse's console).
32
33 The first thing we will do is to make authentification to work. We don't have a password in our data model so authentification will only be done on the email address (yes, we know it's not very secure). The first thing we need to do is to store the logged author into the session so that we can access it from all pages. To do so, open the **Session** class and add the following code:
34
35 {{code language="java"}}
36
37 import er.extensions.foundation.ERXThreadStorage;
38
39 private Author _author;
40
41 public Author author() {
42 return _author;
43 }
44
45 public void setAuthor(Author author) {
46 _author = author;
47 ERXThreadStorage.takeValueForKey(author(), "author");
48 }
49
50 {{/code}}
51
52 Open the **DirectAction** class and rewrite the **loginAction** method like this:
53
54 {{code language="java"}}
55
56 import er.extensions.eof.ERXEC;
57 import er.extensions.foundation.ERXStringUtilities;
58 import your.app.model.Author;
59
60 public WOActionResults loginAction() {
61 WOComponent nextPage = null;
62 String email = request().stringFormValueForKey("username");
63 String errorMessage = null;
64
65 try {
66 Author user = Author.fetchAuthor(ERXEC.newEditingContext(), Author.EMAIL.eq(email));
67 ((Session) session()).setAuthor(user);
68 nextPage = ((Session) session()).navController().homeAction();
69 }
70 catch (NoSuchElementException e) {
71 errorMessage = "No user found for that combination of username and password.";
72 }
73 catch (Exception e) {
74 // TODO: handle exception
75 }
76 if (!ERXStringUtilities.stringIsNullOrEmpty(errorMessage)) {
77 nextPage = pageWithName(Main.class);
78 nextPage.takeValueForKey(errorMessage, "errorMessage");
79 nextPage.takeValueForKey(email, "username");
80 }
81 return nextPage;
82 }
83
84 {{/code}}
85
86 The **loginAction** logic is quite simple: we check the value from the form, we check in the data source if the email address exists, if that's the case we store it in the session and we redirect the user to the default (//navController().homeAction()//) page, if now the user stays on the Main page.
87
88 We are done with the login logic, so we can move to the next step: working on the navigation. The navigation is quite simple: we want two tabs, //Posts and Authors//, when one of the two tabs is selected, we display the list of objects for the selected tabs, and we have two links: one to create a new blog entry or a new author, and the other to query (search) for matching objects.
89
Steve Peery 18.1 90 The navigation structure is done in a "plist" file. If you are a Mac OS X guy, you probably know what is a plist, but if not, a plist is like a JSON structure to hold key/values. The plist file is located in the **Resources** folder, the file's name is **NavigationMenu.plist**. By default, Eclipse will open the file in Xcode, which might not be what we want. To open it in a text editor in Eclipse, right-click on **NavigationMenu.plist** and select **Open With** -> **Text Editor**.
Pascal Robert 6.1 91
Steve Peery 18.1 92 [[image:attach:Capture d’écran 2012-07-30 à 06.18.43.png]]
Pascal Robert 6.1 93
94 Edit the file with the content is like this:
95
96 {{code}}
97
98 (
99 {
100 name = Root;
101 directActionClass = DirectAction;
102 directActionName = default;
103 children = "session.navigationRootChoice";
104 childrenChoices = {
105 home = (
106 Posts,
107 Authors,
108 );
109 };
110 },
111 {
112 name = "Posts";
113 action = "session.navController.listPostsAction";
114 children = ("CreatePost","SearchPosts");
115 },
116 {
117 name = CreatePost;
118 action = "session.navController.createPostAction";
119 },
120 {
121 name = SearchPosts;
122 action = "session.navController.searchPostsAction";
123 },
124 {
125 name = Authors;
126 action = "session.navController.listAuthorsAction";
127 children = ("CreateAuthor","SearchAuthors");
128 },
129 {
130 name = CreateAuthor;
131 action = "session.navController.createAuthorAction";
132 },
133 {
134 name = SearchAuthors;
135 action = "session.navController.searchAuthorsAction";
136 }
137 )
138
139 {{/code}}
140
Steve Peery 18.1 141 Add the following method in the **Session** class for the navigation root.
Pascal Robert 7.1 142
Steve Peery 16.1 143 {{code}}
144
145 public String navigationRootChoice() {
146 return "home";
147 }
148
149 {{/code}}
150
151 The first array in the plist defines what the top level navigation is going to be, and this is where we define the two tabs (the //childrenChoice// dictionary). After that, we define the other parts of the navigation. You see references to //action = session.navController//. This is the action (method) that will be called for the specified navigation element, so let's create those methods in the **MainNavigationController** class.
152
Pascal Robert 7.1 153 The first method we will implement in **MainNavigationController** is a generic method to list objects for a specific entity.
154
155 {{code language="java"}}
156
Pascal Robert 10.1 157 public WOComponent listPageForEntityName(String entityName) {
Pascal Robert 7.1 158 ListPageInterface listPage = D2W.factory().listPageForEntityNamed(entityName, session());
159 EODataSource dataSource = new EODatabaseDataSource(session().defaultEditingContext(), entityName);
160 listPage.setDataSource(dataSource);
161 return (WOComponent) listPage;
162 }
163
164 {{/code}}
165
166 The controller already have methods to query objects (//queryPageForEntityName//) and create new ones (//newObjectForEntityName//), so the next step is to create the methods for our two entities.
167
168 {{code language="java"}}
169
Pascal Robert 10.1 170 public WOComponent listPostsAction() {
171 return listPageForEntityName(BlogEntry.ENTITY_NAME);
Pascal Robert 7.1 172 }
Pascal Robert 10.1 173
Pascal Robert 7.1 174 public WOComponent listAuthorsAction() {
Pascal Robert 10.1 175 return listPageForEntityName(Author.ENTITY_NAME);
Pascal Robert 7.1 176 }
Pascal Robert 10.1 177
Pascal Robert 7.1 178 public WOComponent createPostAction() {
179 return newObjectForEntityName(BlogEntry.ENTITY_NAME);
180 }
Pascal Robert 10.1 181
Pascal Robert 7.1 182 public WOComponent createAuthorAction() {
183 return newObjectForEntityName(Author.ENTITY_NAME);
184 }
185
186 public WOComponent searchAuthorsAction() {
187 return queryPageForEntityName(Author.ENTITY_NAME);
188 }
Pascal Robert 10.1 189
Pascal Robert 7.1 190 public WOComponent searchPostsAction() {
191 return queryPageForEntityName(BlogEntry.ENTITY_NAME);
192 }
193
194 {{/code}}
195
196 We also need to change the //homeAction// method so that when an user log ins, he see the list of blog entries.
197
198 {{code language="java"}}
199
Pascal Robert 10.1 200 public WOComponent homeAction() {
Pascal Robert 7.1 201 return listPageForEntityName(BlogEntry.ENTITY_NAME);
202 }
203
204 {{/code}}
205
Pascal Robert 10.1 206 Save the file and run the app.
Pascal Robert 7.1 207
Steve Peery 16.1 208 After login, you will see the blog entries and if you click the **Authors** tab, you see the list of authors. Each item in the list has 3 actions by default: **Inspect** (view the object), **Edit** (modify the object) and the red X button to delete the object.
Pascal Robert 7.1 209
Steve Peery 16.1 210 Click **Edit** on an author, and you will see that it displays not only the author's details but also blog entries created for that user. You can even create a new blog entry directly from the author.
Pascal Robert 7.1 211
212 Now, edit a blog entry. If you click on the field next to the **Creation Date** or **Last Modified**, a calendar widget appears because those two fields are marked as dates in the data model, that's EOF and D2W magic at work.
213
Steve Peery 17.1 214 Viewing or editing a blog entry can be improved. The three things we want to customize:
Pascal Robert 7.1 215
216 * to remove the email address of the author when viewing the blog entry
217 * to make the **Content** field to be a text area instead of a input field, and even better: attach TinyMCE to the text area
Pascal Robert 10.1 218 * modifying the order of attributes when viewing or editing a blog entry so that the title field is the first field, followed by content, the two dates and the author
Pascal Robert 7.1 219
220 Those two customizations can be done by adding D2W rules. The D2W rules are in two files, located in the **Resources** folder, **d2w.d2wmodel** and **user.d2wmodel**. To edit the files, make sure you installed RulesModeler, a Mac application that manages D2W rule files. If RuleModeler is present, you can simply double-click on **d2w.d2wmodel** and the file will open in RulesModeler.
Pascal Robert 11.1 221
Steve Peery 18.1 222 The first rule we need to add is a rule to specify that the //richTextMode// will be _ simpleRichTextMode_, which is a built-in definition that will put TinyMCE in a simple configuration. Create the **New** button in RulesModeler, and the rule must looks like this:
Pascal Robert 11.1 223
Steve Peery 18.1 224 [[image:attach:Capture d’écran 2012-08-05 à 07.22.43.png]]
Pascal Robert 11.1 225
Steve Peery 18.1 226 The next rule will specify that we want to use the [[EREditHTML>>url:http://jenkins.wocommunity.org/job/Wonder/lastSuccessfulBuild/javadoc/er/directtoweb/components/strings/ERDEditHTML.html||shape="rect"]] component for the //content// attribute. Again, click **New** in RulesModeler, and add this rule:
Pascal Robert 11.1 227
Steve Peery 18.1 228 [[image:attach:Capture d’écran 2012-08-05 à 07.29.12.png]]
Pascal Robert 11.1 229
230 Last rule: when we want to inspect or edit the blog entry, we want to change how the attributes are displayed, so add this new rule:
231
Steve Peery 18.1 232 [[image:attach:Capture d’écran 2012-08-05 à 07.30.15.png]]
Pascal Robert 11.1 233
Steve Peery 18.1 234 We are done. Save the file in RulesModeler, and run the application again. Try editing a blog entry and you will notice that we know have a rich editor for the content and that the ordering of the fields is different, without having to change the HTML or the Java code!
Pascal Robert 11.1 235
Bastian Triller 21.1 236 Congratulations, you are done with the D2W tutorial! [[The next tutorial is about creating a stateful application>>doc:WEB.Home.Getting Started.Your First Stateful Project.WebHome]].