Last modified by Simon Urli on 2023/10/10

Hide last authors
Andy Tripp 4.1 1 {{box cssClass="floatinginfobox" title="**Contents**"}}
2 {{toc/}}
3 {{/box}}
Andy Tripp 1.1 4
Simon Urli 43.2 5 This tutorial will show you how to build a Person Manager application. It's very similar to the [[Manual FAQ tutorial>>Documentation.DevGuide.Tutorials.FAQTutorial.FAQTutorialManual]]. One difference is that the FAQ tutorial involves a very trivial object: a FAQ object contains just two things: a ##question## and an ##answer##, while this example uses a Person object that has more properties (##name##, ##age##, ##sex##, etc). This is a very simple application that makes use of XWiki's [[xclasses, xproperties, and xobjects>>xwiki:Documentation.DevGuide.DataModel||anchor="HXWikiClasses2CObjects2CandProperties"]]. It also uses a technique that you may frequently use as the basis for several different kinds of applications.
Andy Tripp 1.1 6
Andy Tripp 24.2 7 Also, this tutorial explains things a little differently than the [[FAQ Tutorial>>Documentation.DevGuide.Tutorials.FAQTutorial.FAQTutorialManual]]. Here, we gloss over a few things like Templates, and cover other things in more detail, like properties. Also, this page sometimes shows other ways to do things: using SOLR instead of XWQL, using a template instead of a custom form, using custom code rather than a livetable. If something here doesn't make sense to you, try going through the FAQ tutorial and maybe it will make more sense.
Andy Tripp 1.1 8
Andy Tripp 4.1 9 = Prerequisites for following the tutorial =
Andy Tripp 1.1 10
Simon Urli 43.2 11 You should have [[installed XWiki>>xwiki:Documentation.AdminGuide.Installation]] and have a [[basic understanding of how to use it>>xwiki:Documentation.UserGuide.Features.WebHome]].
Andy Tripp 1.1 12
Simon Urli 43.2 13 All through this tutorial you should refer to the [[XWiki Data Model>>xwiki:Documentation.DevGuide.DataModel]] for information on XWiki's data model. You might also use the [[XWiki Scripting Guide>>xwiki:Documentation.DevGuide.Scripting]] to get you started with scripting in XWiki and manipulating XWiki objects. In addition, this tutorial will introduce the concepts of Authoring Templates and Page Design Sheets, patterns that you will find particularly useful in creating XWiki applications. Completing this tutorial is a recommended prerequisite for anyone who wants to build custom applications on the XWiki engine. And by "custom application", we really mean any non-trivial, non-static web application. You should be able to build anything from a simple one-page form to accept online orders for your pizza parlor to your own competitor to TurboTax. The focus here, of course, is to build just the "front-end" of the website, and we don't cover "server-side" things like how to get that tax information sent to the IRS.
Andy Tripp 1.1 14
Andy Tripp 4.1 15 {{warning}}
Simon Urli 43.2 16 Make sure that your user is an [[Advanced user>>xwiki:Documentation.UserGuide.Features.PageEditing||anchor="HAdvancedMode"]] before following this tutorial since you'll need, for example, to use the wiki editor (##Wiki > Edit## menu).
Andy Tripp 4.1 17 {{/warning}}
Andy Tripp 1.1 18
Andy Tripp 4.1 19 = Application Overview =
Andy Tripp 1.1 20
Andy Tripp 9.1 21 The Person Manager application will allow users to create a "Person object" by entering data (name, age, sex, etc) into a simple form and then submitting the form. Let's sketch out what roughly what those two pages should look like on a "napkin", using [[Balsamiq>>https://balsamiq.com/]]:
Andy Tripp 1.1 22
Andy Tripp 24.2 23 {{image reference="personDialog.PNG" width="650px" caption="A Page For Creating a Person"/}}
Andy Tripp 1.1 24
Andy Tripp 4.1 25 Our page won't look exactly like that, but the point is that a website user can create a Person by filling out a "form" page like this.
Andy Tripp 1.1 26
Andy Tripp 14.1 27 Note all the various types of "widgets" shown here:
28
29 * "name" is a single-line text field
30 * "email" is also a single-line text field, but we'd like it to be validated (e.g. give an error if it doesn't have a "@" in it)
31 * "address" is a multi-line text field
32 * "phone" is a single-line text field (perhaps some validation here, too)
33 * "sex" is a drop-down list
34 * "married" is a checkbox: the only allowed values are "true" and "false"
35 * "image" is actually the name of some image file, but we actually display the image itself. Nice!
36 * "age" is Number field, which looks like a single-line text field, but has validation.
37 * "related people" is one more more links to other web pages. Very nice!
38
Andy Tripp 4.1 39 The Person then appears in a table with all other Person objects that have been previously created. Users can click on the Person in the table to see all the information about the Person. He may also edit that information or delete the Person. The table might look something like this:
Andy Tripp 1.1 40
Andy Tripp 24.2 41 {{image reference="personTable.PNG" width="650px" caption="A Page With a Table of Person Objects"/}}
Andy Tripp 1.1 42
Andy Tripp 4.1 43 When the user clicks on a row in the table, he will get a page that shows the information about the Person, which will look similar to the "Create Person" page, but without the ability to change anything.
Andy Tripp 1.1 44
Andy Tripp 4.1 45 == Objects Overview And Terminology ==
Andy Tripp 1.1 46
Andy Tripp 37.1 47 Next, let's summarize the terminology for "Objects". For full details, see [[Data Model>>xwiki:Documentation.DevGuide.DataModel.WebHome]]. There is nothing fancy happening here, but it's important to get our terminology straight.
Andy Tripp 1.1 48
Andy Tripp 24.2 49 A Person is an example of some "Object" or "**class**". We will use XWiki to define the "**properties**" in a "Person" class. For example, we will say that there's a property called "name" of type "String". There's also a property called "age" of type "Number", and an "address" property that's type "TextArea" (a string that can be multiple lines).
Andy Tripp 7.1 50
Andy Tripp 24.2 51 When a user creates a new Person, we call that an "**instance**" of the "class". So we might say something like "I've created an instance of the Person class, with name 'Joe Smith'". And we would say that our table shows all the instances of the Person class. And instances can not only be created, but also edited or deleted.
Andy Tripp 1.1 52
Andy Tripp 7.1 53 We, the creators of the website, define the "Person" class. We define that once, and we're done. Our users use our website to create, edit, and delete instances of our Person class.
54
Andy Tripp 4.1 55 == Overview Of What We Will Do ==
Andy Tripp 7.1 56
Andy Tripp 4.1 57 In this tutorial, we'll do the following steps:
Andy Tripp 1.1 58
Andy Tripp 9.1 59 * Define our Person class, using the XWiki "Data Types" page.
60 * Specify the properties of our Person class, using the XWiki "Class Editor" page.
Andy Tripp 4.1 61 * Define how a Person instance should be displayed, by creating a "Person Sheet" page.
62 * Create a Template and a Template Provider (whatever they are) for our Person class.
Andy Tripp 24.2 63 * Create a web page that displays a table of Person instances.
64 * Create several example pages, each containing a Person instance.
Andy Tripp 1.1 65
Andy Tripp 7.1 66 Note that we don't need to define a page for creating or editing a Person, just a page for *displaying* a Person. XWiki will automatically do that for us!
Andy Tripp 1.1 67
Andy Tripp 24.3 68 Once we are done with these steps, our application will be finished. A user of our website can see a page containing the table of Person objects, view the page for an existing Person, add a new Person, edit an existing Person, or delete a Person.
Andy Tripp 1.1 69
Andy Tripp 7.1 70 = Go to the Special "Data Types" Page =
Andy Tripp 1.1 71
Sergei Kulagin 40.1 72 The "Data Types" page is a special XWiki page that lets us define classes like "Person". This page is actually hidden by default. To "unhide" it, go to your profile page, press the Edit button, select the Preferences tab and select **Yes** for the **DISPLAY HIDDEN PAGES** option.
Andy Tripp 4.1 73
Sergei Kulagin 41.1 74 To find the Data Types page, enter **XWikiClasses** in the search of your wiki. This will find the **XWikiClasses** page titled "Data Types".
Andy Tripp 7.1 75
Andy Tripp 4.1 76 = Create the Person Class =
77
Andy Tripp 24.3 78 * On the "Data Types" page, under the heading "Create a new data type", in the "Title" field, enter ##Person##:(((
Andy Tripp 24.2 79 {{image reference="personClass.PNG" width="650px"/}}
Andy Tripp 4.1 80 )))
Sergei Kulagin 42.1 81 * As you can see in the Breadcrumb below the new page will be created at location ##XWiki > Person##. In practice, the Data Types page will automatically add a postfix **##Class##** to the name of the page(you could also enter ##PersonClass## as the page name directly).
Andy Tripp 7.1 82 * Now it would be nice to have it created in a new location such as ##PersonSpace > Person Class##. Since the ##PersonSpace## parent doesn't exist we cannot use the Tree picker button. Thus click the Pencil button as shown in the following image and replace ##XWiki## by ##PersonSpace##.(((
Andy Tripp 24.2 83 {{image reference="personLocation.PNG" width="650px"/}}
Andy Tripp 4.1 84 )))
Andy Tripp 24.3 85 * XWiki has now created a "**space**" called PersonSpace. A "space" is a directory (or "folder") where pages live. All of our pages will go in this space.
Andy Tripp 24.4 86 * In technical terms you're creating a page named ##PersonClass## (with a title of "Person Class") located in a space called ##PersonSpace## and thus the technical reference for the page is ##PersonSpace.PersonClass##.
Andy Tripp 4.1 87 * Click the "Create this Class" button. You should then see a page with the following content:(((
Andy Tripp 24.5 88 {{code language="none"}}
Andy Tripp 4.1 89 {{velocity}}
90 ## Replace the default space with the space where you want your documents to be created.
91 ## Replace the default parent with the one of your choice and save the document.
92 ##
Andy Tripp 1.1 93 #set($defaultSpace = $doc.space)
94 #set($defaultParent = $doc.fullName)
Andy Tripp 4.1 95 {{/velocity}}
96 {{/code}}
97 )))
Andy Tripp 1.1 98
Andy Tripp 24.4 99 In this code, change "$doc.space" to the name of the space where you want your pages to be created: "PersonSpace".
Andy Tripp 1.1 100 The line of code should look like this:
101
Sergei Kulagin 38.1 102 {{code language="velocity"}}
Andy Tripp 7.1 103 #set($defaultSpace = 'PersonSpace')
Andy Tripp 4.1 104 {{/code}}
Andy Tripp 1.1 105
Andy Tripp 24.2 106 You can also change the default parent of the new Person documents that are going to be created. To do so, replace the "$defaultParent" variable with the name of your document.
Andy Tripp 1.1 107 The line of code should look like this:
108
Sergei Kulagin 38.1 109 {{code language="velocity"}}
Andy Tripp 7.1 110 #set($defaultParent = 'PersonSpace.WebHome')
Andy Tripp 4.1 111 {{/code}}
Andy Tripp 1.1 112
Andy Tripp 24.4 113 The ".WebHome" here is the XWiki naming convention for a "non-terminal" page (a page with children).
114 Click the "Save & View" button. The class is now created and you should be looking at a page titled "Person Class" that looks like this:
Andy Tripp 1.1 115
Andy Tripp 24.2 116 {{image reference="newPersonClass.PNG" width="650px"/}}
Andy Tripp 1.1 117
Andy Tripp 4.1 118 = Add Properties to the Class =
Andy Tripp 1.1 119
Andy Tripp 24.4 120 Under the page title, you should see the words "The class does not have any properties yet. You can use the //__class editor__// to define them." Click on that link.
Andy Tripp 9.1 121 Now, we need to specify all the properties of a Person. Let's have the following properties:
Andy Tripp 12.1 122
Andy Tripp 24.5 123 (% style="width:50%" %)
Andy Tripp 14.1 124 |=Property Name|=Property Type
Andy Tripp 9.1 125 |name|String
126 |email|EMail
127 |address|TextArea
128 |phone|String
129 |sex|static list
130 |married|Boolean
Sergei Kulagin 42.2 131 |image|String
Andy Tripp 9.1 132 |age|Number
Andy Tripp 14.1 133 |relatedPeople|Page (Multiple)
Andy Tripp 1.1 134
Andy Tripp 24.5 135 Follow these steps to define our properties of the Person class:
Andy Tripp 26.2 136
Andy Tripp 9.1 137 * Enter the text //name// in the "name" field
Andy Tripp 14.1 138 * Choose "String" for the type of the property and then click on "Add". By using a String type, when a user goes to enter the //name// of a new Person, he will be prompted with a single-line text field.(((
Andy Tripp 24.2 139 {{image reference="name.PNG" width="650px"/}}
Andy Tripp 4.1 140 )))
141 * Click on the "+" icon to expand the options for the newly created property
Andy Tripp 24.5 142 * Change the value of the "Pretty Name" field to "Name"(capital N). With this done, the user sees the label "Name" rather than "name" when prompted for the name of a Person. This doesn't make a huge difference for this property, but when it comes to the property with the name "relatedPeople", it's nice to show the user something a little more friendy like "Related People". Also, you could later decide to change that label without actually renaming the property (and thereby probably breaking something).
Andy Tripp 9.1 143 * Now repeat this to add each of the properties shown in the table above.
Andy Tripp 12.1 144 ** Note that the "EMail" type is like a String, except that it has a "Validation Expression" (e.g. to make sure it has an "@" character).
145 ** If we wanted to, we could add a "Validation Expression" to the "phone" property to make sure it's in a particular format.
Andy Tripp 24.5 146 ** For the "sex" property, in the "Display Type" field, enter "Select". This will cause the user to see a drop-down menu.
Andy Tripp 14.1 147 ** As we define the "sex" property as type "static list", we specify the values for the field like this:(((
Andy Tripp 24.5 148 {{image reference="staticList.PNG" width="650px"/}}
Andy Tripp 12.1 149 )))
Andy Tripp 14.1 150 ** Note that there is no "Image" type. That's unfortunate. Let's just define it as a String here, and deal with that later.
Sergei Kulagin 42.2 151 ** Note that the "size" of our "age" field is 30 digits. People never live longer than 999 years, so feel free to change that to 3.
Andy Tripp 14.1 152 ** For our "relatedPeople" property, we want to allow multiple values, not just one. So find the "Multi Select" checkbox and check it.
153 * When you are done, you should see all your properties like this:(((
Andy Tripp 24.2 154 {{image reference="properties.PNG" width="650px"/}}
Andy Tripp 12.1 155 )))
Andy Tripp 24.5 156 * When you are done adding the properties, click the "Save & View" button
Andy Tripp 1.1 157
Andy Tripp 4.1 158 = Create the Page Design Sheet =
Andy Tripp 26.2 159
Andy Tripp 24.5 160 Next, we will create a "Page Design Sheet" to specify what a Person should look like when displayed on a page.
Andy Tripp 1.1 161
Andy Tripp 24.2 162 * After the previous step you are now on the PersonClass page which should look like this:(((
163 {{image reference="personClass2.PNG" width="650px"/}}
Andy Tripp 4.1 164 )))
Andy Tripp 24.5 165 * Click the first button ("Create the document sheet") to create the document sheet (the Page Design Sheet).
Andy Tripp 18.1 166 * You should see a warning message with the text "The sheet is not bound to the class so it won't be applied automatically when a page that has an object of this class is displayed". Click the "Bind the sheet to the class" link that appears after the text. Basically, this ties the Person Class to the Person Sheet.
Sergei Kulagin 42.3 167 * Now click on "View the sheet document". This takes you to the ##PersonSpace.PersonSheet## page which you can edit in wiki mode and see its default content. This content is Velocity code which simply goes through all the Person properties and displays each one. For example, it will see that our Person class has a "married" property of type "Boolean", and will show a checkbox with a label of "married" (or perhaps "Married" if we specified that as our "pretty name" for the property). See the [[Creating a FAQ Application (Manual)>>Documentation.DevGuide.Tutorials.FAQTutorial.FAQTutorialManual]] tutorial for more details about this code. This code is actually very close to working as-is. The only thing that would be ugly is that our "image" property would display as just a String, whereas we probably want to display the image itself, not some URL.
Andy Tripp 1.1 168
Andy Tripp 24.5 169 * This code is fine for now, so click "Save & View"
Andy Tripp 1.1 170
Andy Tripp 4.1 171 = Create the Authoring Template =
Andy Tripp 1.1 172
Andy Tripp 16.1 173 * Navigate back to the ##PersonSpace.PersonClass## document (you can use the arrows in the breadcrumb to do so).
Sergei Kulagin 42.4 174 * Click on the "Create the template" button in the **Class Template** section of the page. The Authoring Template will be automatically created.
Andy Tripp 1.1 175
176 Now we need to associate the prototype object with this document to turn it into a true authoring template:
177
Andy Tripp 16.1 178 * If you're on the template page, navigate back to the ##PersonSpace.PersonClass## document.
179 * At the bottom of the page, look for the following warning message: "The template does not contain an object of type PersonClass. Add a Person object to the template »."
180 * Click on "Add a Person object to the template »":(((
Andy Tripp 24.2 181 {{image reference="personClass3.PNG" width="650px"/}}
Andy Tripp 4.1 182 )))
Andy Tripp 1.1 183
184 Next, we want to remove the title for the newly created template:
185
Andy Tripp 18.1 186 * Navigate to the ##PersonSpace.PersonTemplate## document (you can click on the "View the template page (PersonSpace / Person Template)" link for doing that for example.
Andy Tripp 4.1 187 * Edit this document in Wiki mode
Andy Tripp 18.1 188 * Inside the Title field you have "Person Template" written -> delete this text
Andy Tripp 4.1 189 * Save & View
Andy Tripp 1.1 190
Andy Tripp 18.1 191 This step is needed so that all of our future entries don't have "Person Template" as their title.
Andy Tripp 1.1 192
193 Congratulations: you just created an Authoring Template! You're almost done now.
194
Andy Tripp 4.1 195 = Create the Template Provider =
Andy Tripp 1.1 196
Andy Tripp 20.1 197 After the template was created and the object was added, a new section appears with a button to create a template provider to use the existing template. Click that button.
Andy Tripp 1.1 198
Andy Tripp 20.1 199 (((
Andy Tripp 24.2 200 {{image reference="personClass4.PNG" width="650px"/}}
Andy Tripp 20.1 201 )))
Andy Tripp 1.1 202
Andy Tripp 24.5 203 = Create a Home Page for the Person Manager application =
Andy Tripp 1.1 204
Sergei Kulagin 42.4 205 Whew! Take a deep breath, grab a cup of coffee, and pat yourself on the back. We are now finished defining our Person class and all its properties, and those mysterious Templates. Now it's time to create some web pages. Recall that we only have two pages to create:
Andy Tripp 1.1 206
Andy Tripp 20.1 207 * A "main" page that contains a table of Person objects.
208 * A "Person" page that displays a single Person
209 * (We don't need to create a "form" page for creating a new Person or edit an existing person - XWiki does that for us)
Andy Tripp 4.1 210
Andy Tripp 22.1 211 Our "main" page will be the ##PersonSpace.WebHome## page.
Andy Tripp 1.1 212
Andy Tripp 26.2 213 * Click on "PersonSpace" in the breadcrumb to navigate to ##PersonSpace.WebHome## and notice that the page doesn't exist yet:
214 {{image reference="pageDoesntExist.PNG" width="650px"/}}
Sergei Kulagin 42.4 215 * Click on the "edit this page" link.
Andy Tripp 26.2 216 * Type in the title "People". Note that a page's **title** (what the user sees, e.g. "People") does not have to match the page's **name** (the unique id of the page, part of the URL e.g. "PersonSpace.WebHome")
Andy Tripp 1.1 217
Andy Tripp 26.2 218 == Displaying Existing Person Entries In a Table ==
Andy Tripp 1.1 219
Andy Tripp 26.2 220 You have 2 main options when it comes to displaying existing Person entries:
Andy Tripp 1.1 221
Andy Tripp 4.1 222 1. Use the livetable component
Andy Tripp 20.1 223 1. Write custom code in order to display them in a table
Andy Tripp 1.1 224
Sergei Kulagin 42.6 225 We will only cover the "custom code" option here, because it's actually very easy to write a little code to display the table the way we want to. See the "Using the Livetable component" section of the [[Creating a FAQ Application (Manual)>>https://www.xwiki.org/xwiki/bin/view/Documentation/DevGuide/Tutorials/FAQTutorial/FAQTutorialManual?#HUsingtheLivetablecomponent]] tutorial page for instructions to use a "livetable" instead.
Andy Tripp 1.1 226
Simon Urli 43.2 227 To create our table, we need to think about Objects, not web pages. As people use our application, they create new instances of our Person class - they create Object instances. Let's say there have been three "Person" instances created so far. That means there will be three rows in our table. But how do we write code for this table? We will need to "query the XWiki database" - essentially tell XWiki "get me all the instances of the PersonClass that exist". XWiki provides [[several different ways>>extensions:Extension.Query Module]] to do this query:
Andy Tripp 1.1 228
Andy Tripp 20.1 229 * XWiki Query Language (XWQL)
230 * Hibernate Query Language (HQL)
231 * Solr Query Language (SOLR)
Andy Tripp 1.1 232
Sergei Kulagin 42.5 233 The [[Creating a FAQ Application (Manual)>>Documentation.DevGuide.Tutorials.FAQTutorial.FAQTutorialManual]] tutorial page tells you how to use XWQL, but here, let's use [[SOLR>>https://lucene.apache.org/solr/]]. Why is SOLR "better" than XWQL? It's a bit of a standard, it's very well documented, very flexible and fast. It's also pretty simple.
Andy Tripp 1.1 234
Andy Tripp 20.1 235 Regardless of which of these techniques we use to query, we will be writing code in the [[Velocity Template Language>>https://velocity.apache.org/engine/1.7/user-guide.html]].
Andy Tripp 1.1 236
Andy Tripp 4.1 237 === Using custom code ===
Andy Tripp 1.1 238
Andy Tripp 26.2 239 We will need to write code to do the following:
Andy Tripp 1.1 240
Andy Tripp 20.1 241 * Display a header above the table that says "People"
Andy Tripp 26.2 242 * Perform a SOLR query to get all documents (i.e. pages) containing an instance of PersonClass, with a "name" property of any value.
Andy Tripp 20.1 243 * Let's limit the number of results to 1000 because the default for SOLR is 10 and that's too low.
Andy Tripp 26.2 244 * Let's use XWiki syntax to create a table with three columns: Name, Email, and Phone (note that we don't bother to show all properties of a Person as columns in our table, but we could if we wanted to).
245 * The query will return multiple Documents. Each Document contains all the data on the web page. It's similar to the **[[DOM>>https://en.wikipedia.org/wiki/Document_Object_Model]]** that many developers know about. Loop through each Document, doing the following:
Andy Tripp 20.1 246 ** find the PersonClass instance on the page. Set a variable called $object to that.
247 ** get the "name" property from the instance and display that in the first column of the table.
248 ** get the "email" property from the instance and display that in the second column of the table.
249 ** get the "phone" property from the instance and display that in the third column of the table.
Andy Tripp 1.1 250
251 Here is the resulting code:
252
Andy Tripp 4.1 253 {{code language="none"}}
254 {{velocity}}
Andy Tripp 20.1 255 = People =
256 #set ($className = 'PersonSpace.PersonClass')
257 #set ($attr = 'name')
258 #set ($queryStatement = "property.$className.$attr:*")
259 #set ($query = $services.query.createQuery($queryStatement, 'solr'))
260 #set ($discard = $query.bindValue('rows', '1000'))
261 #set ($searchResponse = $query.execute()[0])
262 |=Name|=Email|=Phone
263 #foreach ($searchResult in $searchResponse.results)
264 #set ($documentReference = $services.solr.resolveDocument($searchResult))
265 #set ($d = $xwiki.getDocument($documentReference))
266 #set ($object = $d.getObject("$className"))
267 #if ($object.getProperty('name').getValue() != '')
268 |$object.getProperty('name').getValue()##
269 |$object.getProperty('email').getValue()##
270 |$object.getProperty('phone').getValue()
271 #end
Andy Tripp 1.1 272 #end
Andy Tripp 4.1 273 {{/velocity}}
274 {{/code}}
Andy Tripp 1.1 275
Andy Tripp 26.2 276 Some things to note about this code:
Andy Tripp 22.1 277
Andy Tripp 26.2 278 * The lines starting with "#" are Velocity
279 * Once all of this is processed by the XWiki engine, the result will be XWiki syntax for a table like this:(((
280 ~= People =
281 ~|=Name|=Email|=Phone
282 ~|Joe Smith|joe@gmail.com|999-555-1212
283 )))
284 * The "~#~#" characters at the end of the lines showing rows in the table are required because XWiki wants all table data for a row to be on a single line.
Andy Tripp 20.1 285
Andy Tripp 26.2 286 You may be thinking "How on earth would I possibly know to write that code? What is all that stuff?" Great questions! We'll cover that in the next section, but for now:
287
Andy Tripp 20.1 288 * Copy this code and paste it as Wiki content inside ##PersonSpace.WebHome##
Andy Tripp 26.2 289 * Click "Save & View"
Andy Tripp 20.1 290 * New Person entries will now be displayed on the page once you create them.
Andy Tripp 1.1 291
Andy Tripp 20.1 292 At this point, your ##PersonSpace.WebHome## page will just display as an empty table because no instances of PersonClass have been created yet.
Andy Tripp 1.1 293
Andy Tripp 26.2 294 == Overview of XWiki Scripting ==
295
296 In this section, we'll get you started on scripting in XWiki by explaining some of the code shown in the previous section. If you don't care about these details and just want to see something on your Person Table, just skip this section.
297
Andy Tripp 37.1 298 For details about writing scripts like this, see [[Scripting API Guide>>xwiki:Documentation.DevGuide.Scripting.APIGuide.WebHome]].
Andy Tripp 26.2 299
300 Let's look closely at the code in the previous section. This line:
301 ~{~{velocity}}
302 is simply telling XWiki that everything here is part of a Velocity script. As such, we can just put in some any text we want (we want [[XWiki Syntax>>Documentation.UserGuide.Features.XWikiSyntax.WebHome]]). But we can also have lines of Velocity that start with "#".
303
304 This line...
305
Andy Tripp 36.3 306 {{code language="none"}}= People={{/code}}
Andy Tripp 26.2 307 ...is just XWiki syntax saying "Give show me some "header" text with a big, bold font, that says "People".
308
309 These lines...
310
Andy Tripp 36.3 311 {{code language="none"}}
Andy Tripp 26.2 312 #set ($className = 'PersonSpace.PersonClass')
313 #set ($attr = 'name')
314 #set ($queryStatement = "property.$className.$attr:*"
Andy Tripp 35.3 315 {{/code}}
Andy Tripp 26.2 316
317 ...are just Velocity code setting some variables. Plain text in single quotes, double quotes needed when the text contains references to variables.
318
319 Now we come to this line:
320
Andy Tripp 36.3 321 {{code language="none"}}
Andy Tripp 35.4 322 #set ($query = $services.query.createQuery($queryStatement, 'solr'))
323 {{/code}}
Andy Tripp 26.2 324
Sergei Kulagin 42.8 325 We are calling some "createQuery()" method (or "function") on some "$services.query" variable. That's odd, because this script never set any variable called "$services.query". This is a "special variable" that's automatically set for you by XWiki. The **Scripting API Reference** section on the [[XWiki Scripting API Reference>>xwiki:ScriptingDocumentation.WebHome]] page contains a list of all these "special variables" that XWiki has set for us. Scroll down in that list and find "$services.query", and click on it. You'll come to a "javadoc" page that shows information about "Class QueryManagerScriptService", including a method called "createQuery()". This method takes two parameters: a "statement" and a "language". We passed in 'solr' for the "language", and that "you just have to know" that that's how XWiki has set things up.
Andy Tripp 26.2 326
Andy Tripp 37.1 327 Where can you learn about how to create a "SOLR statement"? I haven't found any good documentation. The [[SOLR Tutorial>>https://lucene.apache.org/solr/guide/8_5/solr-tutorial.html]] is way too complicated.
328 The XWiki-specific [[SOLR Schema>>xwiki:Design.SolrSchema]] page is very much for XWiki developers, not users, but I found it useful. The main thing here is to understand that we can use this SOLR statement...
Andy Tripp 26.2 329
Andy Tripp 36.3 330 {{code language="none"}}
Andy Tripp 35.4 331 property.PersonSpace.PersonClass.name:*
332 {{/code}}
Andy Tripp 26.2 333
334 ..to query for all the instances of PersonClass in the PersonSpace space that have a "name" property set to anything.
335
336 With this line...
337
Andy Tripp 36.3 338 {{code language="none"}}
Andy Tripp 35.4 339 #set ($discard = $query.bindValue('rows', '1000'))
340 {{/code}}
Andy Tripp 26.2 341
342 ...we add to our SOLR query the fact that we only want to get a maximum of 1000 results. We save the value returned in a variable called "$discard", but never use that value. Then why save it? Because Velocity syntax does not have a way to simply "execute a statement", you have to "set a variable". Annoying, I know.
343
344 Next, we execute the SOLR query, get a list of returned values, but only care about the first returned value, and save that in a variable called "$searchResponse":
345
Andy Tripp 36.3 346 {{code language="none"}}
Andy Tripp 35.4 347 #set ($searchResponse = $query.execute()[0])
348 {{/code}}
Andy Tripp 26.2 349
Andy Tripp 37.1 350 ...but now what? The javadoc page for [[Query>>https://www.xwiki.org/xwiki/bin/view/ScriptingDocumentation?url=http:%2F%2Fnexus.xwiki.org%2Fnexus%2Fservice%2Flocal%2Frepositories%2Fpublic%2Farchive%2Forg%2Fxwiki%2Fplatform%2Fxwiki-platform-query-manager%2F11.10.2%2Fxwiki-platform-query-manager-11.10.2-javadoc.jar%2F!%2Forg%2Fxwiki%2Fquery%2FQuery.html]] shows the execute() method returns a List of "<T>", but what are the details of T? What fields does this object have? Again, the SOLR documentation is not helpful, and the XWiki documentation on it is sparse. But [[this page>>https://extensions.xwiki.org/xwiki/bin/view/Extension/Solr%20Search%20Query%20API]] at least shows us that there is a "results" field, which we can loop through. So we do:
Andy Tripp 26.2 351
Andy Tripp 36.3 352 {{code language="none"}}
Andy Tripp 26.2 353 #foreach ($searchResult in $searchResponse.results)
354 #set ($documentReference = $services.solr.resolveDocument($searchResult))
355 #set ($d = $xwiki.getDocument($documentReference))
Andy Tripp 35.4 356 {{/code}}
Andy Tripp 26.2 357
358 We call "resolveDocument()" on a "special" #services.solr variable to get a [[DocumentReference>>https://www.xwiki.org/xwiki/bin/view/ScriptingDocumentation/?url=http:%2F%2Fnexus.xwiki.org%2Fnexus%2Fservice%2Flocal%2Frepositories%2Fpublic%2Farchive%2Forg%2Fxwiki%2Fplatform%2Fxwiki-platform-model%2F11.10.2%2Fxwiki-platform-model-11.10.2-javadoc.jar%2F!%2Forg%2Fxwiki%2Fmodel%2Freference%2FDocumentReference.html]] object, and call getDocument() on that to get a[[Document>>https://www.xwiki.org/xwiki/bin/view/ScriptingDocumentation/?url=http:%2F%2Fnexus.xwiki.org%2Fnexus%2Fservice%2Flocal%2Frepositories%2Fpublic%2Farchive%2Forg%2Fxwiki%2Fplatform%2Fxwiki-platform-oldcore%2F11.10.2%2Fxwiki-platform-oldcore-11.10.2-javadoc.jar%2F!%2Fcom%2Fxpn%2Fxwiki%2Fapi%2FDocument.html]] object.
359
360 We did some hand-waving here, but at least now we know we're looping through the Document for each web page. We have this massive Document object that has what seems like hundreds of methods to choose from. For example, we can call the getTitle() method to get the title of the page. So this is very powerful. But in our case, all we care about is the Person instance on the page. We store that in a variable:
361
Andy Tripp 36.3 362 {{code language="none"}}
Andy Tripp 35.5 363 #set ($object = $d.getObject("$className"))
364 {{/code}}
Andy Tripp 26.2 365
366 And then we get the "name", "email", and "phone" properties of that object, and display them as columns in our table:
367
Andy Tripp 36.3 368 {{code language="none"}}
Andy Tripp 26.2 369 |$object.getProperty('name').getValue()
370 |$object.getProperty('email').getValue()
371 |$object.getProperty('phone').getValue()
Andy Tripp 35.5 372 {{/code}}
Andy Tripp 26.2 373
374 In summary, our page uses Velocity code to create the XWiki syntax for a table. We do a SOLR query for all pages that have a Person instance that has a "name" property with any value. We execute the query and get back something that contains a list of results. We loop through those results, extracting first a DocumentReference (whatever that is), get a Document (like a DOM) from that, and then get the instance of PersonClass from that. We then output XWiki table syntax to display the "name", "email", and "phone" properties of that Person instance as the columns in a table.
375
376 We've learned a little XWiki syntax, Velocity syntax, SOLR syntax, learned to navigate through some XWiki javadoc pages. It wasn't easy or pretty, but we did it.
377
378 Finally, let's create a few Person instances so we can see them in our table.
379
Andy Tripp 20.1 380 == Creating new Person instances ==
Andy Tripp 1.1 381
Andy Tripp 20.1 382 There are 2 ways for you to let your users create new instances of our PersonClass:
Andy Tripp 1.1 383
Andy Tripp 20.1 384 1. Declare the Person as a template
Andy Tripp 4.1 385 1. Add a custom creation form
Andy Tripp 1.1 386
Sergei Kulagin 42.5 387 The [[Creating a FAQ Application (Manual)>>Documentation.DevGuide.Tutorials.FAQTutorial.FAQTutorialManual]] tutorial page describes how to create a custom form if you want to do that. But the simpler way is to use a template, and so that's what we'll do. Remember earlier, on the Person Class page, we clicked on a button to create a template, and clicked on another button to create a "Template Provider"? That's all we needed to do! 
Andy Tripp 1.1 388
Andy Tripp 27.1 389 With that done, let's go ahead and create an instance of PersonClass. To be specific, we'll create a new page and add an instance of PersonClass to that page. Go to your PersonSpace.WebHome page (the one with the table), and click the "Create" button to create a child page. You will be prompted for a page title and page type. In the list of Types, under "Template" you should see "Person":
Andy Tripp 24.2 390 {{image reference="createPerson.PNG" width="650px"/}}
Andy Tripp 1.1 391
Andy Tripp 22.1 392 Fill in the person's name as the page title, choose the "Person" type, and click "Create."
393 You will then be prompted for all the information about your the Person instance that you want to create:
Andy Tripp 24.2 394 {{image reference="createPerson2.PNG" width="650px"/}}
Andy Tripp 1.1 395
Andy Tripp 26.2 396 Fill in all the values and press "Save & View".
Andy Tripp 1.1 397
Andy Tripp 24.1 398 Now go back to the PersonSpace.WebHome page, refresh your browser, and you should see your new instance as a row in the table.
Andy Tripp 1.1 399
Andy Tripp 27.1 400 Create a few more pages, each with a Person instance, and see them in the table, like this:
401 {{image reference="personTable3.PNG" width="650px"/}}
Andy Tripp 24.1 402
Andy Tripp 30.1 403 == Finishing Up the Table ==
Andy Tripp 24.1 404
Sergei Kulagin 42.8 405 Had we chosen to use the "livetable" feature, we'd be done. Clicking on a row in the table would take us to that user's page, and the table would also allow us to edit or delete a Person. But we chose to write "custom code" using Velocity and XWiki syntax to show our table. So now we need to make it so that clicking on the user's name in the table takes us to that user's page.
Andy Tripp 24.1 406
Sergei Kulagin 42.8 407 Go back to the PersonSpace.WebHome page and click "Edit" to modify it. Change the line that shows the Person's name:
Andy Tripp 24.1 408
Andy Tripp 36.3 409 {{code language="none"}}
Sergei Kulagin 42.8 410 |$object.getProperty('name').getValue()##
Andy Tripp 35.6 411 {{/code}}
Andy Tripp 24.1 412
Sergei Kulagin 42.8 413 to this:
414
415 {{code language="none"}}
416 |[[PersonSpace.$object.getProperty('name').getValue()]]##
417 {{/code}}
418
Andy Tripp 24.1 419 All we've done here is change the first column in our table to display a link rather than simple text. Press "Save & View" and verify that the links work.
420
Andy Tripp 30.1 421 == Finishing Up the Sheet ==
Andy Tripp 31.1 422
Andy Tripp 35.7 423 Currently, our page showing a Person looks pretty bland:
Andy Tripp 30.1 424 {{image reference="personPage1.PNG" width="350px"/}}
425
426 Recall that we defined a "Sheet" page for our PersonClass, which defined how **any** Person instance should be displayed. Also recall that we just decided to use the default code that XWiki provided, which simply loops through every Person property and displays it.
427
428 There are two problems left to solve:
Andy Tripp 31.1 429
Andy Tripp 30.1 430 * It sure would be nice to see the image itself, not just the URL to the image.
431 * We're not seeing anything for the "Related People" property yet. We should see multiple links to other pages.
432
Andy Tripp 35.2 433 Additionally, let's customize this page in a few ways:
Andy Tripp 32.2 434
Andy Tripp 31.1 435 * Let's put it all into a visual "box"
436 * Let's put the image at the top
437 * Let's rearrange properties, putting "phone" above "email", so that the order of properties we display does not match the order that we defined them in the Class Editor.
438
Andy Tripp 32.2 439 After these changes (and after changing Joe Smith's image to be the URL for an elephant and specifying multiple "relatedPeople" for him), it should look something like this:
Andy Tripp 35.2 440 {{image reference="personPage2.PNG" width="650px"/}}
Andy Tripp 31.1 441
Andy Tripp 32.2 442 Go to the Person Sheet page, edit it, and paste the following:
Vincent Massol 36.2 443
Andy Tripp 36.3 444 {{code language="none"}}
445 {{velocity}}
Andy Tripp 32.2 446 #set($person = $doc.getObject('PersonSpace.PersonClass'))
447 (% id="infobox" style="flex: 0 0 300px" %)
448 (((
449 {{box}}
450 (% style="float:right; clear:right;" %)
451 |(% colspan="2" %) [[image:$person.display('image', 'view')||width="100"]]
452 |Name:|$person.display('name', 'view')
453 |Phone:|$person.display('phone', 'view')
454 |Email:|$person.display('email', 'view')
455 |Address:|$person.display('address', 'view')
456 |Sex:|$person.display('sex', 'view')
457 |Married:|$person.display('married', 'view')
458 |Related:|##
459 #foreach ($other in $person.getProperty('relatedPeople').getValue())
460 |[[PersonSpace.$other]]##
461 #end
462 {{/box}}
463 )))
Andy Tripp 36.3 464 {{/velocity}}
465 {{/code}}
Andy Tripp 32.2 466
Andy Tripp 35.7 467 Press "Save & View", go to the "Joe Smith" page, and it should now look the way we want.
Andy Tripp 35.3 468
Andy Tripp 32.2 469 Some things to note about this code:
Andy Tripp 35.8 470
Andy Tripp 35.3 471 * We called the "getObject()" method on the "special XWiki variable" $doc to get the Person instance on this page.
Andy Tripp 37.1 472 * We used the [[XWiki Box>>extensions:Extension.Box Macro]] to put everything in a box.
Andy Tripp 35.3 473 * We included some CSS here that didn't do much, but could have been any arbitrary CSS:
Andy Tripp 36.3 474 {{code language="none"}}(% style="float:right; clear:right;" %){{/code}}
Andy Tripp 37.1 475 * We wanted the CSS line to apply to what could potentially be many lines of following code. We used the [[XWiki Groups syntax>>xwiki:Documentation.UserGuide.Features.XWikiSyntax.WebHome]] of ~(~(~( ... ~)~)~) for that.
Andy Tripp 32.2 476 * To make the image span two columns of our table, we used an XWiki [[Parameterized Table>>https://www.xwiki.org/xwiki/bin/view/Documentation/UserGuide/Features/XWikiSyntax/?syntax=2.1&section=Tables]]
477 * For the "relatedPeople" property, we didn't want to just display the (multiple) Person names, we want them to be links. So we loop through them, creating a link for each.
Sergei Kulagin 43.1 478 * We are careful to put "~#~#" at the end of each line when we want to put multiple things in a table column, so as not to confuse XWiki.
Andy Tripp 35.8 479 * By using this code rather than blindly looping through every property, we're able to easily:
480 ** Not display the "age" property at all
Andy Tripp 35.3 481 ** reorder the properties in any way we like
Andy Tripp 32.2 482 ** use any labels we want, not be stuck with the "Pretty name" for each property (though this is more of a drawback than an advantage)
483
Andy Tripp 35.3 484 As you can see, this code to customize how we display a Person is extremely straightforward:
Andy Tripp 35.8 485
Andy Tripp 35.3 486 * We easily got the value for the Person instance on the page.
487 * The Velocity syntax is simple and doesn't get in our way. We could someday choose Groovy if Velocity is not enough.
Andy Tripp 35.8 488 * We're able to inject CSS as needed. We could also have injected raw HTML.
Andy Tripp 35.3 489 * We chose to use XWiki syntax, which is even simpler than HTML.
490
Andy Tripp 4.1 491 = Conclusion =
Andy Tripp 1.1 492
Andy Tripp 24.1 493 You've done something very special here, taking advantage of the [[Power Of XWiki>>https://propaganda-tactics.com/xwiki/bin/view/About/The%20Power%20of%20XWiki/]] by using data-driven web pages.
Andy Tripp 1.1 494
Andy Tripp 35.8 495 If we had "gone cheap" and naively created a static HTML (or Wiki) page for each Person, we would have had to "hard-code" the table, containing links to them all. And maintaining that table would be a nightmare because it would need to be updated each time a new Person is added or each time any of the data that it displays is changed on the other page. With XWiki, whenever we add a new Person instance, it will automatically show up in our table because it's automatically generated. In addition, if we hadn't done it this way and we wanted to change the look of the pages, we would likely have to change every one of them (even if they share common CSS).
Andy Tripp 1.1 496
Andy Tripp 35.8 497 At the other end of the spectrum, suppose we had been willing to spend a lot of time and money and decided on a typical three-tier high-end architecture. We would have to hire a whole team or even several teams. A "client" team would build pages using Javascript-plus-some-framework. And a "server" team would handle requests from the client to the server to store and retrieve the data. We'd have a database guy (or whole team) store the data in some relational database. Lots of time and energy would go into defining the (perhaps REST) API that the server provides and the client would use. And lots of effort would go into defining the database schema, database creation, and code (perhaps Hibernate or some other ORM tool) to get the data from the database into the server code and back again. All of this is perhaps 100x the amount of work needed compared to doing the same thing with XWiki.
Andy Tripp 24.1 498
Andy Tripp 35.8 499 Using XWiki in this way, the pages *are* the database, because they don't contain static HTML - they contain Objects - data. And those pages can be queried easily and flexibly, just like a relational database. The client side here is written in XWiki syntax, which is simpler than HTML and far simpler than Javascript. But we could also use HTML and CSS (and even Javascript) as needed, if we need more flexibility. The client also contains some simple Velocity code to do things like looping to create the rows in a table. There is no comparison between the simple Velocity code we've just seen and the equivalent Javascript-plus-the-latest-JS-framework code. And even this Velocity code can often be replaced by something even simpler (like "livetable").
Andy Tripp 24.1 500
Sergei Kulagin 43.1 501 It should be obvious here that what we are building is a full, web-based **application**, not just a wiki. So don't be fooled by the name "XWiki". We are using it as a platform to build applications, like [[CUBA>>https://www.cuba-platform.com/]] but far simpler. Our client side can get nearly as complex as we want. If we do, someday, want some shiny bells and whistles offered by the latest Javascript+framework, we can incorporate that later without too much pain.
Andy Tripp 24.1 502
Andy Tripp 35.8 503 In short, by using XWiki to build a data-driven application, we get nearly the flexibility and maintainability of a full three-tier architecture, while only investing nearly the same small effort needed to build just the client in static HTML.

Get Connected