PhenoTips » Developer Guide » Best practices

Best practices

Java APIs

Data Model

XDocument and XObject manipulation should be done using the DocumentAccessBridge. This allows the XWiki data model APIs to be used without incurring a build dependency on the xwiki "oldcore" module, which is very large. This XWiki doc page has more information.

Some examples using this approach: (along with other controllers)


Naming migrators (e.g. public class R54693PhenoTips1500DataMigration):

- First 3 digits are the current XWiki version
- 4th is always 9 for us
- 5th is a counter (e.g. first migration wihin the same release may be 71290, second is 71291)
- the number after PhenoTips is the issue number

Reading XObjects under a document

This code is for reading the XObjects "RemoteMatchingServiceConfiguration" under the document XWikiPreference that is found in the space "XWiki". The order of the code lines is only for illustration.
See and for the full code.

  1. Define EntityReference for space and document
    EntityReference XWIKI_SPACE = new EntityReference("XWiki", EntityType.SPACE);
    EntityReference XWIKI_PREFERENCES_DOCUMENT_REFERENCE = new EntityReference("XWikiPreferences", EntityType.DOCUMENT, XWIKI_SPACE);
  2. Get an XWikiContext and get document
    Provider<XWikiContext> contextProvider;
    XWikiContext context = this.contextProvider.get();
    XWikiDocument prefsDoc = context.getWiki().getDocument(ApplicationConfiguration.XWIKI_PREFERENCES_DOCUMENT_REFERENCE, context);
  3. Define reference to Object and read objects.
    EntityReference PHENOMECENTRAL_SPACE = new EntityReference("PhenomeCentral", EntityType.SPACE);
    EntityReference REMOTE_CONFIGURATION_OBJECT_REFERENCE = new EntityReference("RemoteMatchingServiceConfiguration", EntityType.DOCUMENT, PHENOMECENTRAL_SPACE);
    List<BaseObject> remotes = prefsDoc.getXObjects(ApplicationConfiguration.REMOTE_CONFIGURATION_OBJECT_REFERENCE);

Frontend Components

Javascript files

XWiki has two primary ways of including Javascript files: on the filesystem and as XObjects (of the JavaScriptExtension class). Best practice is for the files to reside on the filesystem, in order to

  • Enable a natural editing experience with amenities like linting and syntax-highlighting
  • Streamline the edit-debug-commit cycle, since using the JavaScriptExtension approach necessitates a XAR export and mvn xar:format before committing

In order to have the file available on the filesystem for use, do the following:

  1. Add a new project to your component's maven project, with a name ending in "-resources", e.g. "patient-measurements-resources" for the "patient-measurements" project.
  2. In the new project, add your JS files under src/main/resources/resources/uicomponents/phenotips (there are purposely two "resources" directories in that path). Files here will automatically be placed at webapps/phenotips/resources/uicomponents/phenotips in PT's "base-war" distribution after you complete this procedure.
  3. Add your package to the base WAR's POM, at components/base-war/pom.xml. There are two entries to make here, see the commitdiff below for an example.

After completing this procedure, you can now include the JS files using

$xwiki.jsfx.use('uicomponents/phenotips/[filename].js', true)

The second "true" argument enables parsing of velocity in the file.

A full example from patient measurements can be found at this commitdiff.

Can I add a dependency on a cool library I found?

  1. Is it LGPL compatible? Even if we use AGPL ourselves, dependencies should not be GPL or AGPL, if we want to be able to offer non-AGPL licenses
  2. Is it popular? Still maintained with recent releases, and a developer community that's larger than one person?
  3. Do you really need it? Don't add a dependency for just one function that could easily be written by us.

Web Services

Creating a REST API endpoint

  1. Create a new interface with a @Path("/path/to/endpoint") annotation
  2. Create an implementation of that interface that extends XWikiResource, with an @Component annotation and @Named("[the fully qualified class name]")