This tutorial will show you how to build a Contact Manager application.  It's very similar to the FAQTutorialManual tutorial. The only difference (so far) 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 classes, properties, and objects. It also uses a technique that you may frequently use as the basis for several different kinds of applications.

Also, this tutorial explains things a little differently than the FAQ tutorial. If something here doesn't make sense to you, try going through the FAQ tutorial and maybe it will make more sense.

Prerequisites for following the tutorial

You should have installed XWiki and have a basic understanding of how to use it.

All through this tutorial you should refer to the XWiki Data Model for information on XWiki's data model. You might also use the XWiki Scripting Guide 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 mean a huge variety of web applications.

Make sure that your user is an Advanced user before following this tutorial since you'll need for example to use the wiki editor (Wiki > Edit menu).

Application Overview

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:

personDialog.PNG

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.

Note all the various types of "widgets" shown here:

  • "name" is a single-line text field
  • "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)
  • "address" is a multi-line text field
  • "phone" is a single-line text field (perhaps some validation here, too)
  • "sex" is a drop-down list
  • "married" is a checkbox: the only allowed values are "true" and "false"
  • "image" is actually the name of some image file, but we actually display the image itself. Nice!
  • "age" is Number field, which looks like a single-line text field, but has validation.
  • "related people" is one more more links to other web pages. Very nice!

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:

personTable.PNG

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.

Objects Overview And Terminology

Next, let's summarize the terminology for "Objects". For full details, see Data Model. There is nothing fancy happening here, but it's important to get our terminology straight.

A Person is an example of some "Object" or "class". We will use XWiki to define what "properties" are in a "Person" object. 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).

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.

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.

Overview Of What We Will Do

In this tutorial, we'll do the following steps:

  • Define our Person class, using the XWiki "Data Types" page.
  • Specify the properties of our Person class, using the XWiki "Class Editor" page.
  • Define how a Person instance should be displayed, by creating a "Person Sheet" page.
  • Create a Template and a Template Provider (whatever they are) for our Person class.
  • Create a web page that displays a table of people.
  • Create a web page that displays a Person instance.

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!

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.

Go to the Special "Data Types" Page

The "Data Types" page is a special XWiki page that lets us define classes like "Person". It'sactually hidden by default. To be "unhide" it, go to your profile page, select the Preferences tab, edit the page and choose to view Hidden Documents. 

To find the "Data Types" page, enter a search query for the keyword "D". This should return a document titled "Data Types".

Create the Person Class

  • On the "Data Types" page, under the heading "Create a new data type", in the "Title" field, enter Person as the name of the page to create:

    personClass.PNG

  • 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 prefix the page name with Class (you could also enter PersonClass as the page name directly).
  • 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.

    personLocation.PNG

  • In technical terms you're creating a page named PersonClass (with a title of "Person Class") located in a space also called PersonSpace and thus the technical reference is PersonSpace.PersonClass.
  • Click the "Create this Class" button. You should then see a page with the following content:
    {{velocity}}
    ## Replace the default space with the space where you want your documents to be created.
    ## Replace the default parent with the one of your choice and save the document.
    ##
    #set($defaultSpace = $doc.space)
    #set($defaultParent = $doc.fullName)
    {{/velocity}}

In the code, change the word "$doc.space" with the name of the space where you want your pages to be created: "PersonSpace".
The line of code should look like this:

#set($defaultSpace = 'PersonSpace')

You can also change the default parent of the new FAQ documents that are going to be created. To do so, replace the "$defaultParent" variable with the name of your document.
The line of code should look like this:

#set($defaultParent = 'PersonSpace.WebHome')

Click the "Save & View" button. The class is now created and you should be looking at a page titled "Class: Person" that looks like this:

newPersonClass.PNG

Add Properties to the Class

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." Let's just follow those instructions!

  • Click on the 'class editor' link
  • Note that the link trail in the header is something like "Wiki Home / PersonSpace / Person Class". This shows you that you are indeed on the Person class page in Class edit mode.

Now, we need to specify all the properties of a Person. Let's have the following properties:

Property NameProperty Type
nameString
emailEMail
addressTextArea
phoneString
sexstatic list
marriedBoolean
imageImage
ageNumber
relatedPeoplePage (Multiple)
  • Enter the text name in the "name" field
  • 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.

    name.PNG

  • Click on the "+" icon to expand the options for the newly created property
  • 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 to "Similar People" without actually renaming the property (and thereby probably breaking something).
  • Now repeat this to add each of the properties shown in the table above.
    • 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).
    • If we wanted to, we could add a "Validation Expression" to the "phone" property to make sure it's in a particular format.
    • For the "sex" property, in the "Display Type" field, enter "Select". This will give the user a drop-down menu.
    • As we define the "sex" property as type "static list", we specify the values for the field like this:

      staticList.PNG

    • Note that there is no "Image" type. That's unfortunate. Let's just define it as a String here, and deal with that later.
    • Note that the "size" of our "age" field is 30 digits. Three digits should be plenty, so feel free to change that.
    • For our "relatedPeople" property, we want to allow multiple values, not just one. So find the "Multi Select" checkbox and check it.
  • When you are done, you should see all your properties like this:

    properties.PNG

  • When you are done adding and configuring the properties, click the "Save & View" button

Create the Page Design Sheet

  • After the previous step you are now on the FAQClass page which should look like this:

    personClass2.PNG

  • Click the first button ("Create the document sheet") to create the document sheet (the Page Design Sheet). This sheet determines how your page's objects will be rendered to the user. The document is automatically created.
  • 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.
  • 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 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 FAQTutorialManual 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.
  • Click "Save & View"

Create the Authoring Template

  • Navigate back to the PersonSpace.PersonClass document (you can use the arrows in the breadcrumb to do so). 
  • Click on the "Create the document template" button. The Authoring Template will be automatically created.

Note that earlier, we changed the space name preceding the page name because we wanted all of our Person pages to reside in a space named PersonSpace. Remember that all our documents will be copies of the Authoring Template used as a prototype so the content will be copied in all our Person documents and will execute the Design Sheet code in the context of the current document. See the dedicated page for more information regarding this technique.

Now we need to associate the prototype object with this document to turn it into a true authoring template:

  • If you're on the template page, navigate back to the PersonSpace.PersonClass document.
  • 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 »."
  • Click on "Add a Person object to the template »":

    personClass3.PNG

Next, we want to remove the title for the newly created template:

  • Navigate to the PersonSpace.PersonTemplate document (you can click on the "View the template page (PersonSpace / Person Template)" link for doing that for example.
  • Edit this document in Wiki mode
  • Inside the Title field you have "Person Template" written -> delete this text
  • Save & View

This step is needed so that all of our future entries don't have "Person Template" as their title.

Congratulations: you just created an Authoring Template! You're almost done now.

Create the Template Provider

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.
The template provider is created with some default values, but those can be updated to match the needs.

Create a home page for the FAQ application

You want your users to be able to see a list of all existing questions and answers and to add new questions. The best way to do this is to put the FAQ application in its own space and to use that space's homepage to display existing questions.

Thus we now need to create the FAQ.WebHome page

  • Click on "FAQ" in the breadcrumb to navigate to FAQ.WebHome and notice that the page doesn't exist.
  • Edit it in wiki mode
  • Type in the title "FAQs"

Displaying existing FAQ entries

You have 2 options when it comes to displaying existing FAQ entries:

  1. Use the livetable component
  2. Write custom code in order to display them

Using the Livetable component

In this section, we will show how to use the Livetable Macro to display the existing questions and answers.

The livetable component will give users the ability to easily list and filter through existing FAQ documents. The macro is made of 3 parts:

  • The list of columns: for each entry, we will display the question, the date when it was created and a special column that lets users quickly modify entries
  • The properties of each column: for each column, we will define how it should be displayed, whether it should link to the entry and whether it should be filterable
  • The livetable options: those are options related to the display of the livetable (in this case we will to display a tag cloud and 10 rows by default)

Here is the resulting code:

{{velocity}}
#set($columns = ["question", "doc.creationDate", "_actions"])
#set($columnsProperties = {
    "question" : { "type" : "text", "link" : "view", "html" : "true", "sortable":true },
    "_actions" : {"actions": ["edit","delete"]}
})
#set($options = {
   "className":"FAQ.FAQClass",
   "translationPrefix" : "faq.",
   "tagCloud" : true,
   "rowCount": 10
})
#livetable("faq" $columns $columnsProperties $options)
{{/velocity}}
  • Copy this code and paste it as Wiki content (inside FAQ.WebHome)
  • Click "Save and View"
  • New FAQ entries will now be displayed on the page once you create them

The FAQ.WebHome page should look similar to this:

FAQsLivetable.png

Notice how there are some translation keys displayed inside the livetable. Let's create a translations document and change those keys to actual text:

  • Create a new page inside the FAQ space called Translations, i.e. at FAQ.Translations (using the "+" button)
  • Edit it in Wiki mode and paste this content inside:
    faq.question=Question
    faq.doc.creationDate=Creation Date
    faq._actions=Actions
  • Click "Save & View"
  • Edit it again in Object mode and add a new XWiki.TranslationDocumentClass object to mark the page as a page containing translations. Make sure to select the right scope. Using "Global" means the translations would be visible to all wikis (and it requires having Programming Rights!) and "Wiki" means it's visible to the current wiki only. Choose "Wiki" as the scope.
  • Click "Save & View" again

Now the FAQ.WebHome page should look like this (notice the translations instead of the keys):

FAQsLivetableWithTranslations.png

Using custom code

You will need to write the following code:

  • A XWQL query that will find all your FAQ documents. Check the Query API documentation to know more about it.
    • The XWQL query looks for all documents that have a FAQ.FAQClass object other than the template
    • If no document has been created yet, a warning message is displayed
  • A piece of velocity code to display all those documents
    • The velocity code loops in that list
    • For each item, the full document is loaded in memory so that values can be retrieved from it
    • For each document, the question is retrieved and displayed as a link towards the FAQ entry

Here is the resulting code:

= Existing FAQ entries =

{{velocity}}
#set($xwql = "from doc.object(FAQ.FAQClass) as faq where doc.fullName <> 'FAQ.FAQTemplate'")
#set($results = $services.query.xwql($xwql).execute())
#if($results.empty)
  No FAQ has been created yet!
#else
  #foreach ($item in $results)
    #set($faq = $xwiki.getDocument($item))
    * [[${faq.display("question").replace("<p>", "").replace("</p>", "")}>>${item}]]
  #end
#end
{{/velocity}}
  • Copy this code and paste it as Wiki content inside FAQ.WebHome
  • Click "Save and View"
  • New FAQ entries will now be displayed on the page once you create them

The FAQ.WebHome page should look similar to this:

FAQsCustomCode.png

Creating new FAQ entries

There are 2 ways for you to let your users create new FAQ entries:

  1. Declare the FAQ as a template
  2. Add a custom creation form

Using a Template

You will have to define a template provider as explained on this page

Go to your wiki's administration interface, in the "Templates" section (Administration -> Content -> Templates). Create a new template provider in the
FAQ space and name it FAQTemplateProvider

You can then use the following values:

  • Provider name: FAQ Template Provider
  • Template name: New FAQ entry
  • Template to use: FAQ.FAQTemplate

If you'd like, you can restrict FAQ entries so that they're created only in the FAQ space. Once you're done, click "Save & View". Your template is now ready to be used! Users who click on the "Add >> Page" button will now have the option to create a new page using the FAQ template.

Custom creation form

If you choose this option, you will need to write some code to let your users create new FAQ entries. To do this, you will have to create a form in which the user can enter the name of the questions she wants to create. Once typed in, the form calls the same page to trigger the new document creation based on the parameters entered by the user:

  • The first part of the code checks whether the page has a parameter. If so:
    • The name of the document that will be created is computed
    • A check is done to verify the document doesn't exist yet
    • If everything's ok, the user is sent to the new document in inline edition mode
  • The second part of the code is the actual FAQ creation form
    • It builds the name of the document to create it in the FAQ space
    • It sets the document parent as being the current document
    • It defines the template to use to create the new document
{{velocity}}
#if("$!request.docName" != '')
  ## Request for creating a new instance
  #set($docName = ${request.docName})
  #set($targetDocName = "${request.spaceName}.${docName}")
  #if(!$xwiki.exists($targetDocName) && $xwiki.hasAccessLevel('edit', $xcontext.user, $targetDocName))
    $response.sendRedirect($xwiki.getURL($targetDocName, 'inline', "template=${escapetool.url($request.template)}&parent=${escapetool.url($request.parent)}"))
    ## Stop processing, since we already sent a redirect.
    #stop
  #end
#end

= Add a new question =

#if("$!targetDocName" != '' && $xwiki.exists($targetDocName))
  {{warning}}The target document already exists. Please choose a different name, or [[view the existing document>>$targetDocName]]{{/warning}}
#elseif("$!targetDocName" != '')
  {{warning}}You don't have permission to create that document{{/warning}}
#end

{{html}}
  <form action="" id="newdoc" method="post">
    <div>
      <input type="hidden" name="parent" value="${doc.fullName}"/>
      <input type="hidden" name="template" value="FAQ.FAQTemplate"/>
      <input type="hidden" name="sheet" value="1"/>
      <input type="hidden" name="spaceName" value="FAQ"/>
      Document: <input type="text" name="docName" value="Enter your question here" class="withTip" size="50"/>
      <span class="buttonwrapper"><input type="submit" value="Create this FAQ" class="button"/></span>
    </div>
  </form>
{{/html}}
{{/velocity}}
  • Copy this code and paste it as Wiki content inside FAQ.WebHome, below the code that's already there
  • Click "Save and View"
  • A form to create new FAQ entries is now available on the page:

    FAQsWithForm.png

Test the Application

Now let's just create a new document in our application to test it out.

If you previously chose to use a "Custom creation form" for creating new FAQ entries, follow these steps:

  • Go to FAQ.WebHome
  • Below the "Add a new question" header, enter a question (which will also be used as the document title) in the Document field
  • Click Create this FAQ
  • You can then enter your question in longer form using the Question field on the template, like this:

    FAQSheetEdit.PNG

  • Click Save & View and then you will see the newly created document, like this:

    FAQSheetView.PNG

  • Go back to the FAQ.WebHome page (you can use the breadcrumbs) to see the list of existing questions

    FAQsWithEntry.png

Conclusion

This tutorial has taught you how to use the Class Wizard app and it has detailed the concepts of classes, objects and properties and introduced the authoring templates and page design sheets.

You may also have learned a little bit about Velocity scripting in documents. You can use these basic concepts to build custom applications at the document or presentation layer of XWiki without having to compile or deploy code.

As always, please take the time to make this document better for other users if you find ways that it can be improved as you read it for the first time.

Tags:
   

Get Connected