Copyright © 2009 MELS - Multimedia & E-Learning Services, University of Zurich, Switzerland
Table of Contents
List of Figures
List of Tables
This documentation is intended for OLAT developers. It explains the basic concepts and paradigms used in the open source learning management system OLAT.
First of all you need the java webapp developers base pillars:
Java SDK 1.6.x http://java.sun.com
Eclipse 3.x http://www.eclipse.org
MySQL http://www.mysql.org/downloads/mysql/5.0.html, please read the MySQL part in the techdocu for the setup OLAT Techdocu MySQL
Tomcat 5.x or Tomcat 6.x http://tomcat.apache.org/ (scroll down -> Binary Distribution -> Core -> may be windows service installer )
Maven 2.0.x or higher, only if you need to change or debug the olatcore project
Install those big things on your computer. Now you have to configure Eclipse 3.x to work together with the tomcat. Either you install the Sysdeo tomcat plugin or you use the eclipse Web Tools Platform WTP.
In this description we will use Eclipse 3.x together with WTP.
Follow these steps to have the Web Tools Platform (WTP) at your finger tips:
start eclipse
Help | Software Updates | Find and Install , use this to search for new features and watch out for the Web Tools Platform, select and install it.
typically it is recommended to restart eclipse... do so.
Further eclipse setup:
check that you have a Servers entry under Window | Preferences
activate Installed Runtimes and Add an Apache Tomcat runtime, configure it to fit your pathes.

add a Server entry: add a new server by File | New | Other. Take the default value localhost for server and typically the preselected Apache tomcat for the server runtime.

add TOMCAT_HOME classpath variable to the eclipse preferences (Window | Preferences | Java | Build Path | Classpath Variables)

So far you should have an eclipse with a tomcat at localhost configured server. But you are missing the OLAT source code. Let us then proceed to anonymously checkout the OLAT source from HEAD.
The OLAT source code is splitted in two java eclipse projects: the olatcore project (cvs module: "olatcore") and the LMS (learning management system) project (cvs module: "olat3"). The olatcore project is the framework upon which the LMS is based on. Basically, one only needs the olat3 for LMS development, since per default the olatcore lib is provided as jar in the olat3/webapp/WEB-INF/lib .
For debugging purposes, there is the possibility to checkout the olatcore and link the olatcore source to the olat3 project build path (Eclipse: olat3 project Properties/Java Build Path/Source/Link Source). It is advisable that the olatcore should not be changed during the LMS developing process.
The OLAT project gives you read-only access to its sources. If you encounter bugs or have some interesting hints or questions about parts of the source - do not hesitate to write it to the developers list.
You have to open the CVS Repository Exploring Perspective, which you find via Window | Open Perspective | Other..
create a new repository location: right click to create a new repository location

fill in the needed fields:
host: cvs.olat.org
repository path: /usr/local/cvs
user: anonymous
password: anonymous
connection type: pserver
use default port
validate connectoin on finish
save password
click on finish, this validates if you can connect... you should be connected to internet to have this working.
After successful creation of this CVS repository location you can browse your repository. Just go straight ahead to the HEAD | olat3 right click on it and check out this copies the whole project from the repository server to your eclipse workspace. This may take a while...

As already mentioned, the LMS java project already contains the olatcore jar (olat_core-1.0-SNAPSHOT.jar).
In case you also need the olatcore source:
checkout the olatcore module from HEAD.
Define a M2_REPO classpath variable for your maven repository.
Define your maven repository to eclipse by running the maven command:
$ cd workspace_dir (go to your eclipse workspace directory)
$ mvn -Declipse.workspace=. eclipse:add-maven-repo
This command creates an Eclipse classpath variable M2_REPO that points to your local maven repository.
IF THIS DOESN'T WORK: add M2_REPO class path variable manually into eclipse (olatcore project Properties | Java Build Path | Libraries | Add Variable | Configure Variables… | New)
cd olatcore (change to the olatcore directory you just downloaded)
mvn clean package (download basic libs form maven-repo into local repository)
mvn eclipse:clean eclipse:eclipse
If you want to work/debug in both projects (olatcore and olat3), you must change the default olat3 project settings like described in Section 1.6, “Optional: Use olatcore source code instead of precompiled jar-file ”
There are two ways to create your OLAT MySQL database:
Use the script /olat3/bin/dbAndUserSetup.sh to automatically generate the OLAT database and user. You will be prompted to enter the mysql root password.
Or, create the database and user manually.
# mysql -u root -p On the console run the following SQL-statements: # use mysql; # mysql> create database olat; # mysql> create user 'olat'@'localhost' identified by 'olat'; # mysql> grant all on olat.* to 'olat'@'localhost' identified by 'olat'; # mysql> flush privileges;
Create an "olatdata" folder (e.g. in your Eclipse workspace), its path will be further referred into the userdata.dir parameter of the olat.properties file.
Duplicate the file olat.local.properties and rename it to olat.local.properties (properties are read by spring on startup). Customize the olat.local.properties file according to the diff below. Important are the sections "directories and server and ports". Notice: Even on windows you should use forward slashes "/" instead of windows style backward "\" slashes! Please adapt at least the following parameters:
base.dir=/usr/local/opt/olat/olat3
userdata.dir=/usr/local/opt/olat/olatdata
tomcat.home=/usr/local/opt/tomcat5
server.domainname=localhost
server.port=8080
adminemail=yourname@yourdomain.com
supportemail=yourname@yourdomain.com
smtp.host=smtp.yourdomain.com
db.pass=mypassword
brasato.debug=true
localization.cache=false
user.generateTestUsers=true
log.rootCategory = INFO, syslog, A1
You should have now:
a java project olat3 [cvs.olat.org]
a Tomcat Server @ localhost-config entry filed under Servers
Optionally: a java project: olatcore
The previous step - configure OLAT - did create a server.xml file, which you will find under olat3/conf/. As a last step you copy this server.xml file to the Servers project into the Tomcat Server @ localhost-config. You get asked if you want to overwrite, just do so.
copy olat3/conf/server.xml to Servers/Tomcat Server @ localhost-config
in eclipse Window | Show View | Other browse to Server and choose the Servers
right click on Tomcat Server @ localhost-config and choose Run

the view changes away from the Servers view to the Console view and you see a lot of output from the starting OLAT. It also happens that an "Error" message pops up saying "... Server did not start after 45s".. just click ok, this is not a problem.
in the console view you should find something like INFO: Server startup in .... ms
Congratulations you are done!
Point your browser to http://localhost:8080/olat.
Login to your new OLAT installation by using administrator and olat as default username and password.
If you want to access the olatcore source code you might want to check it out. Here is an explanation of how to use the source code in your Eclipse project instead of the olatcore.jar file. This is especially useful for debugging and to get a better understanding of the olat core functionalities.
Select the Repository view, expand HEAD and right click on olatcore. Choose Check Out.

In order to remove the olatcore from the libraries go back to the Java view and right click on the olat3 project. Select Properties.

Then select the olatcore jar-file from the Java Build Path Libraries list.

Press Remove.
As a next step we will link the olatcore source code into the olat3 project. In the properties window select the source tab. Then choose Link Source... Browse to olatcore/src/main/java and hit Choose. Name the Folder to something more descriptive like corejava or just olatcore.

Repeat the same thing for the source code at olatcore/src/main/resources (Link Source... -> Browse... -> Choose -> Finish). In the Java Build Path Source list you should now have five entries:

What we also have to do is to get rid of the jar-file in the WEB-INF/lib directory. Right click on olatcore-1.0-SNAPSHOT.jar and delete it.

There is one more thing to do. Open the file olat.properties and set the property brasato.src to the path of your workspace followed by /olatcore/src/main/java/ (there is an example in the file). Uncomment the line brasato.src=jar. Your file should now look something like this:
# Where to load the brasato resource. In normal cases this is set to # "jar". For developers that use the brasato project instead of the core # it should be set to the brasato project path, see example below. The # framework uses this path to load resources like velocity pages, i18n # files. # brasato.src must be on windows an URL: e.g. C:/eclipse/workspace/olatcore/src/main/java/ brasato.src=/Users/me/Documents/workspace/olatcore/src/main/java/ # brasato.src=jar
In order for the properties to take effect, run mvn olat:properties
That's it. Now you should be able to restart the Tomcat server and run OLAT from source.
Summary
In this chapter you will learn which interaction design concepts have been implemented in OLAT and about the business entities used. It provides guidance to developers of the open source community to ensure a consistent user interface. Code contributions complying with these concepts are more likely to be accepted for official releases. The technical architecture and the development framework is described in chapter Chapter 3, OLAT development framework overview
![]() |
Conceptual overview of the OLAT interaction design. Shaded entities are not in the scope of this chapter.

The most important layout elements used in OLAT
Since OLAT is a dynamic webapplication and not a static website the whole layout and navigation is generated on the fly and can't be changed by the common user. You can still customize the look and feel of your OLAT installation, this is explained in Customizing. For navigation in OLAT we separate the page in four parts: top navigation, content menu, content area and toolbox actions.
The interaction design is characterized by consistency regarding position (relative and absolute), structure, semantics, and function. I.e. interaction elements of similar types or conglomerates thereof
are at same absolute positions: e.g. site navigation is always on the top of the window, content menu is always on the left of the window, and tools as well as spanning actions are always in corresponding boxes on the right.
are at same relative positions: e.g. close command is always at the bottom of an action box.
have same structures: e.g. hyperlinks have same color, mouse-over behavior etc.
use same semantics: e.g. the search action is always called “Search” (not sometimes “Find”)
trigger same functions: e.g. the search action always returns a list to choose an entry.
The following guidelines do not act as laws of nature, you will certainly find exceptions in OLAT. But (intended) guideline deviation should be well-founded and should result in better usability.
Function: Interactive elements either aim at navigation or at action.
Structure: The two different functions are pointed out with a fundamentally different element design (hyperlinks and buttons). There are some exceptions described later.
Semantics: Interactive elements are labeled with an initial capital letter. There are no "title rules" in the English version, i.e. "Back to search" instead of "Back to Search".
Navigation elements are visualized by hyperlinks (in site navigation, content menu, and continuous text), tabs (in tabbed panes), and icons. Their labelings never include verbs.
Hyperlinks in the top navigation
Structure: They are characterized by the position indicator color: users can identify the active site by the coloring of the tab, inactive sites are labeled differently. Neither is underlined.
Function: Hyperlinks in the static and dynamic tabs always refresh the remaining screen (content menu, content area, and toolboxes). No pop-up windows and/or external sites are launched.
Hyperlinks in the content menu
Structure: They are characterized by the two position indicators formatting and color, i.e. users can identify their position in OLAT by means of bold entries in the menu tree. Inactive locations are characterized by plain entries. Both get underlined on mouse-over.
Function: Hyperlinks in the menu tree always point to the content area of the own site.
Hyperlinks in continuous text
Structure: They have no position indicator and are always plain and underlined (on mouse-over).
Function: Contrary to links in the menu, they can point to external locations, launch the e-mail client or navigate one step back in a workflow (see the following three CSS-classes).

Tabs in tabbed panes
Structure: They are charcterized by the position indicator color , i.e. users can identify the active tab by means of the link and background colors. Tabs in tabbed panes never contain icons.
Function: Links in tabs always open the respective card.
Icons
Structure: Linked icons are neither framed nor underlined. Mouse pointer changes on mouse over. Provide alt texts where the icon is not followed by a descriptive link text.
Function: Aim at visual aid for frequently used textual links, or are used to reduce text load on the GUI.
Action elements are visualized either by dedicated boxes or by three types of buttons (small, main and form buttons). Labelings always include a verb.
Action- and toolboxes
Structure: Always located on the right of the page, wrapped in a colored, entitled box. Entries are preceded by icons.
Function: Each box comprises similar actions. Rule of thumb: whenever possible place actions of spanning nature as hyperlinks in boxes, instead of as buttons in the content area.

Example of an action box.
Buttons
Structure: Very small, small, default and disabled buttons are visualized by one of the the css-classes b_xsmall, b_small and b_disabled together with b_button.
Function: All buttons trigger an action or workflow inside the current content area. Small buttons are used for auxiliary actions, default buttons for actions which are crucial to reach a goal. Form buttons are rendered by the class b_button and used to visualize the common html functions cancel, submit and reset.

Exception: Listings
Actions corresponding to individual entries of a listing are placed as hyperlinks in dedicated columns, i.e. no buttons are used for repetitive actions.

As an exception, actions in listing are visualized as hyperlinks.
The following outlines briefly the role/rights concept and the way users are authenticated and authorized within OLAT.
In OLAT there are four basic system roles. Those roles are assigned as a basic set of rights. You can add users to those system roles in the Administration area within OLAT. Additional rights can be defined individually as well in the Administration area. The system roles are, in short:
guest: This is the guest system role with a limited set of rights which allows for example browsing the repository and launching content which is available to guests. All guest accounts are assigned this role.
user: This is the regular olat user role. All registered and authenticated users within the OLAT system are assigned this role. The role allows for the basic functionality such as personal briefcase, browsing the repository, creation of buddy groups and other.
author: This is the author system role. It includes all rights of the user system role but includes the author right which allows for example the creation of content in the repository.
administrator: This is the administrator or superuser system role. Users with this role have the whole set of rights within OLAT. In some workflows, users with this system role are treated differently from the rest of the users, giving them access to administrative functionality.
user-manager: This roles can manage user (create new users, edit user propertiers and delete users).
group-manager: This roles can administer course-comprehensive groups (groups of different courses).
institutional-resource-manager: This roles have author rights for all resource of certain institutional (e.g. has the same university like the author of the resource).
See also Section 2.6, “Groups” to learn more about the differences between groups and system roles.
OLAT provides an abstract mechanism of authenticating users. In the olat_config.xml
configuration file in section LoginModule you'll find a
set of authentication providers which handle authentication and authorization. This is
simply a class which extends AuthenticationController , hence
you'll have full control over the workflow needed to authenticate your users. All
authentication providers listed in the config file (and enabled) will be presented to the
user on the login screen. The user may choose the way she wishes to authenticate herself.
At the time of writing, there are four implementations of AuthenticationController:
OLATAuthenticationController implements a simple
username/password authentication,
ShibbolethAuthenticationController implements authentication based
on Shibboleth 2.0 and the SWITCH Embedded DS (see the Section 2.7, “Shibboleth” for details on this method of
authentication), DefaultShibbolethAuthenticationController just provides a link for
redirecting the requests to the /shib/, and LDAPAuthenticationController
which implements auth. based on an LDAP backend server.
The authentication provider system is also used for other authentication purposes where an authentication is needed.
RSS: The personalized RSS feed is protected using a token.
This might appear in URL's therefore it should not be the same as the users
password. See RSSUtil.RSS_AUTH_PROVIDER)
Jabber: Jabber uses also another password than the login
password since it would not be save to use the other one. See
ClientManager.PROVIDER_INSTANT_MESSAGING)
Summary
The repository is found in OLAT under Learning resources and lists repository entries. These entries are meta information about a contained resource, typically a file. Each resource has an associated handler responsible for implementing the behaviour of the possible actions.
This is working in the same fashion as the filetypes - application association in a browser or a modern operating system. See Figure 2.1, “How the repository looks.” for an example repository listing. The type - handler mechanism is more detailed described in the section called “Types and repositoryhandlers” , and a description of the actions can be found in the section called “Workflows”
First of all the RepositoryHandlerFactory is the central point
where the implementations of the RepositoryHandlers are subscribed
for the types they claim. By design the type should be the string returned from the
OLATResourcable.getResourceableTypeName() .
All resources in the repository refer to a file so far, but the type - handler mechanism is restricted by no means to files. The following table maps file resources and their suffixes to the corresponding handler.
The file validating class is used to check whether a certain file or archive is valid. The Handler class is responsible for a starting
point of GUI workflows for the corresponding file or archive. It inform the RepositoryHandlerFactory about possible
actions (see the section called “Workflows”) and which types it handles.
Table 2.1. Repository file validating classes, suffixes and handlers
| File Validating Class | Suffixes/Type | Handler class | Comment |
|---|---|---|---|
| FileResource | * | WebDocumentHandler |
A generic file resource for files not categorized by the above. |
AnimationFileResource
| SWF | WebDocumentHandler | |
| OLAT Course |
CourseModule.ORES_TYPE_COURSE
| CourseHandler |
The OLAT course is a ZIP file containing a runstructure.xml, editortree.xml and linked other resources like tests, surveys. |
DocFileResource
| DOC | WebDocumentHandler | |
GlossaryResource
| ZIP |
GlossaryHandler
|
Archive must contain either a
glossary.xml
or a
glossary.textmarker.xml
|
ImageFileResource
| JPG, JPEG, GIF, TIFF, IMG, BMP, PBM, ICO, PICT, PNG | WebDocumentHandler | |
ImsCPFileResource
| ZIP | ImsCPHandler |
the extracted archive must contain the imsmanifest.xml which must validate. |
MovieFileResource
|
MPG, MPEG, QT, RM, RAM, AVI | WebDocumentHandler | |
PdfFileResource
| WebDocumentHandler | ||
PowerpointFileResource
|
PPT, PPS | WebDocumentHandler | |
ScormCPFileResource
| ZIP | SCORMCPHandler |
the extracted archive must contain the imsmanifest.xml which must validate. |
SharedFolderFileResource
|
FileResource.SHAREDFOLDER
| SharedFolderHandler | Comment |
SoundFileResource
|
MP3, WAV, RA, MIDI | WebDocumentHandler | |
SurveyFileResource
| ZIP | QTISurveyHandler |
the extracted archive must contain the qti.xml which must validate. |
TestFileResource
| ZIP | QTITestHandler |
the extracted archive must contain the qti.xml which must validate. |
WikiResource
| ZIP | WikiHandler | ZIP File is scanned for a index file. |
XlsFileResource
| XLS | WebDocumentHandler |
Managing the repository involves adding, creating new resources, removing existing ones and update the meta information of a repository entry. Adding involves either uploading a file resource or selecting one from the various folder places. Creating is typically only possible if OLAT offers the appropriate editor.
The concrete implementation of the RepositoryHandler, i.e.
WebDocumentHandler, must provide a
controller for adding the given resource. This controller is returned
when the getAddController(..) method is called.
Before a deletion is issued the handler of the respective resource is asked if the
resource is readyToDelete(..). The deletion is initiated by
cleanUpOnDelete(..) which is expected to do any work necessary,
before removing the resource definitively.
Similiar to the adding of a resource, the handler also may provide the facility to
create or edit the resources meta information. This is the so called details
view and a component for this can be provided in the
method getDetailsComponent(..).
Summary
The course resides in the repository as a learning resource of type course. It is a special kind of learning resource as it has the ability to aggregate other learning resources, except courses.
It is a structural container for the course building blocks. The course is structured as a tree. And this structure is defining a learning path by means of visibility and accessability rules. The structure also offers an assessment and score facility to evaluate the course viewers progress.
It is an administrative container for the course. As it may have associated groups, areas, rights to control and organize course access.
It is important to understand the difference between the run and editor characteristic of a course. And also the ideas of the publish mechanism, which defines the transition from the editor view to the run view.
There is a course configuration with course wide settings.
The course is hierarchly structured - a tree - whereas each course node represents a building block. Such a building block is an encapsulated functionality for the course. These building blocks can be divided into the categories Content and structure , Assessment and Scoring , Collaboration . Each building blocks can control access and visibility with help of the visibility and accessability rules . Following building blocks are implemented so far:
Content and structure
Structure, STCourseNode :
Structure element, noteworthy is the ability to calculate a score or passed value from other assessable course nodes.
External Page, TUCourseNode :
An URL pointing to an external resource is integrated into the LMS without a HTML frames construct. Hence special requirements to the external resource apply.
CP learning content ,
CPCourseNode :
Reference another learning resource of type IMS content packaging from the repository.
Single Page, SPCourseNode :
Simple HTML page to display, either create one with the internal HTML editor or reference one from a storage folder.
SCORM learning content,
ScormCourseNode :
Reference another learning resource of type SCORM package from the repository.
Self-Test, IQSELFCourseNode:
Reference another learning resource of type self test based on IMS QTI from the repository. It is listed here instead of assessment because a self test does generate only anonymized results.
File-Dialog, DialogCourseNode:
Collection of files users can upload and discuss. Uses a forum compoment for each file to be discussed.
Linked List
,
LLCourseNode
: This is contribution of the BPS GmbH. It allows to manage web links.
Collaboration
Folder, BCCourseNode
File exchange between course participiants.
Contact Form, COCourseNode
Send an e-mail to a configured recipient group from the logged in user.
Enrolment , ENCourseNode
As a precondition for the enrolment groups or areas must have been configured for the course. If this is the case an automatic enrolment to the course can be realized here.
Date Enrolment
,
DENCourseNode
: This is contribution of the BPS GmbH. It allows to manage dates and enrolments to this dates.
Forum , FOCourseNode
Discussion forum.
Assessment and Scoring
Assessment , MSCourseNode
Configuring an assessment building block influences the assessment tool which is at disposal to the course tutors. Check the description of the assessment and score facility.
Test, IQTestCourseNode:
Reference another learning resource of type test, based on IMS QTI, from the repository. The results from the test can be accessed in the visibility and accessability rules.
Questionnaire ,
IQSURVCourseNode
Task
,
TACourseNode
: A complex a building block to distribute, collect, assess given tasks.
Topic assignment
,
ProjectBrokerCourseNode
: A complex a building block to manage a list projects (e.g. master-theses).
The CourseFactory ensures that a course is loaded only once
into the RAM. Thus all cours participants, tutors, authors are sharing the same course
object. It is clear that each course user must have its individual course run session
where user specific data is processed. To accomplish this the course has a
UserCourseEnvironment which holds the user specific course run
data. Besides this the CourseEnvironment aggregates runtime course
information which again is shared between all users.
The course is made persistent with the XStreamHelper using XML
object serialization. Because of the sophisticated publish mechanism there is a persistent
valid course editor xml tree structure and a valid course run xml tree structure
co-existing. The course run is the users view to the course, and the course author sees
the course in the editor view.
The author can select specific or all changes in the course editor to be published
into the run structure. This process is defined in the
PublishProcess and controlled from the
PublishController roughly speaking it consists of:
removing nodes marked for deletion from the editor and run treemodels
copy new added nodes from the editor treemodel to the run treemodel
apply changes in existing nodes from the editor to run treemodel.
Each course has associated a course configuration which is persisted as XML file in the same place as the run structure and editor strucuture files.
This settings contain so far configurations for:
Course chat enabled/disabled:
A change in this setting takes effect only in new started course sessions.
Individual course CSS layout option:
A change in this setting takes effect only in new started course sessions.
Specifying a resource folder which is shareable between courses.
Enabling/disabling evidence of achievement:
Changes in this option trigger the deletion of existing achievements, or a recalculation respectively. It is clear that the recalculation may take a while.
If a new configuration must be added one has to follow the guidelines
described in CourseConfig . Special care must be taken to increase
the CourseConfig 's version number, once a change was
active in a productive environment.
This rules are specified like arithmetic expressions, whereas the valid functions and
units are registered in the ConditionInterpreter. Each building
block has associated a visibility and accessability condition which is recursively
evaluated during the users course run. Once a building blocks visibility or accessability
is false the child nodes are not evaluated.
Some of the course building blocks are of type
AssessableCourseNode. The assessable course nodes use the
AssessmentManager from the course environment to set and get
assessment information.
TODO:More info on this will be added soon.
Resources of type IMS QTI accept IMS QTI v1.2.1 compliant tests and surveys. The package
(with the QTI XML source and media files) comes as a simple ZIP file. The QTI XML has to be
in the root of the ZIP an named as qti.xml. This type of resource can
either be launched directly from the repository as a self-test where no results get
persisted or included in a course via test and survey building blocks.
OLAT includes an editor for QTI files. The editor offers single and multiple choice,
fill in blank and free text as type of questions (the latter only in surveys). Since QTI
does not define different types of questions, the editor relies on a nameing scheme to
identifie the type of question. This is done via an encoded item id in the form
QTIEDITOR:SCQ:123 where QTIEDITOR denotes that this item was
created with the OLAT editor, SCQ denotes the type of question (other types are MCQ, FIB
and KPRIM) and finally the number is a file unique ID. See
QTIEditorHelper for details.
It is important to note, that the editor does not allow to work with arbitrary QTI files created by other editors or manually. It can only edit questions which originate from the editor itself. If you do any manual changes to the generated qti.xml file, your changes may get lost when loaded again in the editor or the item may not be editable anymore. The qti.xml is parsed and converted into an internal structure within the editor. Upon saving the file, the qti.xml is generated from scratch based on the internal structure.
The QTI runtime implemented within OLAT can handle almost all features of QTI 1.2.1, much more than the editor can actually handle. So you may not be able to edit files generated manually or by external editors but your files are most probably fine for the runtime.
The runtime allows tests to be resumed in case the user's browser crashes.
For this, a dump of the whole runtime information is written to the filesystem after every
response. These files are stored in
PATH_TO_OLATDATA/qtiser/USERNAME/QTI_RUN_ID/qti.ser . If the user
starts the test again, the workflow will resume based on the information stored in this
file and the user should start again from where she left last time.
The results of a test run are stored in XML files based on the IMS QTI Results
Reporting standard. The source of the questions is added to the results reporting for
further reference. The files are stored in
PATH_TO_OLATDATA/resreporting/USERNAME/QTI_RUN_ID.xml . The
results reporting XML is transformed with a stylesheet to present the results in HTML to
the user. You'll find the stylesheets in the package
org.olat.ims.resource.xsl. The stylesheets come in different
languages where the language specific strings are defined in the prologue. This is the
last bit of XSLT within OLAT.
In a real world test situation, people may not want students to be able to exchange information with each other during the actual test. For this reason, if in a test (this does not aply for self-tests of surveys), all collaboration tools will be disabled. These include the instant messaging system and the buddy groups for the specific user. All collaboration tools will be re-enabled, when the user successfully finishes her test.
OLAT has a quite advanced group management system that can be used in courses to manage learning or right groups. In a special mode the groups can even be used withing several courses. The same course concept is also used completely independent of courses in so-called project goups. In this chapter you will learn how this all works
First it is important to understand which groups we are talking about. There are several places in OLAT where you can add users to a group:
System roles: The system administrator adds users to the
system roles groups like 'authors' or
'administrators'. The system uses some
SecurityGroup objects to store this information. The group
membership is not directly visible to the members of this groups, they
can't contact each other or something, they don't see who else is
in the group. The idea is to manage rights (you can also say roles) on a system wide
level.
Resource owners: Whenever a resource is created someone
must be responsible for this resource. Usually it is handy when this right can be
shared by several people. The resource is then owned by a group of people. The most
obvious place where this happens is in the learning resources repository. Creating a
new learning resource, e.g. a course, creates also a
SecurityGroup for this resource and adds automatically the
creator of the resource to this group. Everybody in this group can now manage the
resource, including the management of the owner group of this resource. Adding
another user to the group makes the other user instantly to an equal owner of the
resource. For example could the other user then remove the creator of the resource
from the resource owners group and he would have no more access to the resource
although he was the original creator. This is very much intended this way - try
trusting your people :-). Compared to the system roles group the group is visible to
the members, however the group offers no groupware features since the only purpose
is to manage access rights. Other examples for such resource owner groups are the
catalog entry owners or the groupmanagement owners (comprehensive group management).
Business groups: Now whenever the group is the center of
your actions, when the group itself is the main business case then we use so-called
BusinessGroup . Business groups server the purpose doing
something together as a group, maybe using groupware tools, maybe supervised by a
coach. This groups have a name and a description and can be started in a runtime
environment to work in the group. This is very different from the system roles or
the resource owners group - those have no name and can't be started in a
runtime environment since the purpose of those groups is only to manage access
rights and not to communicate or collaborate with the members of the group.
In this chapter we talk about the generic business groups concept and where it is used within OLAT.
The BusinessGroup class is a concept for a group that serves
the purpose of collaborating, working or doing something together. The
BusinessGroup is abstract in a way that it generically implements
a set of functions that can be usefull when working in a group. However, in reality we
deal with concrete groups that serve a special purpose and will use only a subset of all
functions available in the generic BusinessGroup. To distinguish
these concrete groups we give the BusinessGroup a field called
groupType .
When thinking object oriented you could create an abstract superclass and use
inheritance to create the concrete implementations. However, when persisting such a
construct using a object-relational mapping layer like hibernate it can easily get
complicated. Therefore we decided to make it differently and implemented one generic
BusinessGroup together with one generic
BusinessGroupEditController and one generic
BusinessGroupMainRunController . The controllers offer the
complete set of functions implemented by the generic BusinessGroup.
When creating these controllers using the BGControllerFactory a
configuration object BGConfigFlags is used to tell the controllers
which functionality is enabled for the concrete group that is displayed or edited. This
way the controllers must not know anything about group types, they only know which feature
to display and which one to hide. Adding new features to groups or changeing features is
only a matter of implementing the generic functionality in the businessgroup and the
controllers and then enabling it in the configuration for all group types you want.
A BusinessGroup uses two SecurityGroup
labled owners and participants. A
SecurityGroup is a generic implementation of a group without any
context or business meaning by itself. The meaning of those two underlying groups in the
BusinessGroup class is that members of the owners security group
have the right to modify / manage the business group wheras members of the participants
security group do not have such a right.
Learning groups are used in the course groupmanagement for
Administrative purpose: Organizing students using the enrolment course building block or by adding them manually to the groups. Adding coaches to a group gives the coaches access to the administrative tools like the assessment tool for those users he is coaching. Note that without having your users organized in at least one learning group in the course you will not be able to see any course results for your participants since you just have no clue who is in your course anyway. Moste courses should have at least one learning group.
Didactical purpose: Students should collaborate using the collaboration tools or the the author want to create a certain learning flow (also called learning experience) using accessability and visibility conditions on the course building blocks using the group membership constraints.
The distinction between those two is usually not so sharp and very often mixed. For both reasons you would create a learning group in a course.
Learning groups use the owner SecurityGroup for the coaches
and the participants SecurityGroup for the students
Learning groups do not use the group rights feature but they use the area feature. More on this later.
Learning groups uses also the IM roster synchronization feature. Can be configured and the default does the synchronization
Right groups are used in the course to give some users certain administrative rights within the course. Usually you also want this users to be able to communicate together using a forum or to share documents.
Example 1: allow the professor to see and modify all assessment data but don't allow him to mess around in the course editor. In this case you would be in the owner group of the learning resource which makes you to the course administrator. The professor would not be in this group but you would create a new right group within the course and add the professor to this group. For this right group you would enable the assessment tool.
Example 2: you want all your coaches be able to manage all groups in the course. By default, coaches do not have full access to the groupmanagement and the assessment tool. The can launch the tools, but they have only access to the groups they are coaching. In some cases you want to give them access to all students, even to the one they don't coach. In this cenario you would create a right group and add all coaches as members to this group. Then you would enable the groupmanagement and the assessment tool for these group.
Right groups do not use the owner SecurityGroup at all. All
users are members of participants SecurityGroup
Right groups use the group rights feature but they don't use the area feature.
Right groups do not use the IM roster synchronization feature.
Buddy groups (in the GUI they are called project groups, in the code they are named buddy group) are groups used for self organized learning or any kind of collaboration. They are created in the groups site by any user and managed by the participants of the group. There is no way to control such a group, therefore it shold not be used in the course context. The idea of buddy groups is that every user in OLAT can create his own peer network and collaborate or share for whatever purpose. This can be a semester work by three people, a discussion room for a clique of climber enthusiasts or a place where someone that is abroad for some month can post all the images to keep in touch with the friends stayed at home.
Buddy groups use the owner SecurityGroup for users that
should be able to invite other users. Regular users are members of the participants
SecurityGroup
Buddy groups do not use the group rights feature nor do they the area feature.
Buddy groups use the IM roster synchronization feature. Can be configured and the default does the synchronization

Correlation between group context, groups, areas and courses
Every BusinessGroup has a reference to a
BGContext and the BGContext has references
to OLATResource. The idea of the business group context is to
define the context in which the group is used.
Groups of type BuddyGroup are an exception since they have no
dependecies to any OLATResource, thus they have not context defined (NULL)
The BGContext serves as a container for all groups that belong
together. It has a type which means that a BGContext can only have
groups of one type in it. BGArea, as we see later, do also belong
to a BGContext.
Group contexts are used in course to manage groups. Every course has at least one
group context for the type LearningGroup and one for the type
RightGroup. When creating a new course a new learning and right
group context is generated and labeled as a default context. A
default context means that this context has been generated automatically for the purpose
of the groupmanagement within this course. It is only good for this course.
In most situations this is fine and everything works as expected by using this default group contexts. But in some cases it makes sense to use the same groups in several courses. This is also called course comprehensive groupmanagement.
To achieve this it is possible to create BGContext that are not
labled as default context. These group contexts are created manually in the
groupmanagement site that is only available to users with the system role administrator or
groupmanagement. Those BGContext do use a
SecurityGroup to manage the owners of this context, to define who
is allowed to manage this group context. To those contexts the owners can add one or more
OLATResource (meaning courses) where this groups should be used.
Note that you can have more that one group context added to a course. When launching the groupmanagement in the course you then have to select which group context you want to manipulate. Everyone who has access to the groupmanagement within a course can modify the groups as if it where a default group context containing course internal groups. They do not need to be in the contexts owner security group.
A BGContext can have so-called BGArea
relations. An area has a name and description and is used to associate many groups to a
certain area of a course. In the GUI we call it learning area.
You can see areas as a group of groups, however a group can be in more than one areas, thus it is not really a group of groups. You could also see an area as a group attribute, a tag that you stick on a group.
From a paedagogical perspective it makes sense to ask “are you in group red,
blue or green”, then do something. However, this gets very complicated with may
groups. It would be much easier to ask “are you in a color area” and then add
all your groups to this color area. This is what the BGArea is good
for: address and organize groups that fullfill the same purpose.
Example: create an are enrollment and add your groups Class Monday, Class Friday to this area. In the enroment course building block use the area instead of the group names to define which groups can be used for enrolment. OLAT automatically offers the user all groups of the defined learning area to enroll into.
You can also use the areas to define learning activities and associate groups to this activities. In the course building blocks you then restrict access and visibility of certain blocks to groups that participate in the named activity.
Areas are only used by business groups of type LearningGroup
Group rights can be associated to a BusinessGroup. All possible
rights are defined in CourseRights, a right is nothing more than a
string that has a business meaning. Using the BGRightManager it is
possible to ask OLAT wheather a user has a certain right in a certain group contex.
Currently group rights are used in course right groups to grant full access to the course editor, the assessment tool, the groupmanagement or the archive tool.
Rights are only used by business groups of type RightGroup
Since the whole BusinessGroup codebase is completely
contextless implemented and uses the same code for buddy, learn and right groups a special
issue must be covered and this is localization. On screen the generic code must have group
type dependent locales. In some locale strings only the group type differs, in others it
makes sense to have different sentences like when sending mails after beeing added by
someone in a group
To solve the problem of one generic code and multiple translations depending on the
type we use the translator fallback mechanism. Each Translator can
have its fallback translator in case the key is not found. This way it is possible to
build a chain of translators. The GUI controllers of the group package use the
BGTranslatorFactory to generate a package translator that use a
group type translator in the first position of the translator chain.
In the package org/olat/group/ui/run a certain key is used to
display someting in a group, e.g. a title. As soon as you define the same key in the
package org/olat/group/ui/buddygroup or
org/olat/group/ui/learninggroup the key from the group type
specific package has precedence and is used for translation.
Each BusinessGroup has
CollaborationTools that are used in the groups run time environment
to communicate and share data. The collaboration tools are generated using the
CollaborationToolsFactory and are not group type specific.
The group owners or administrators can enable and disable the collaboration tools in the edit mode of the group on a group level.
Currently we have the following tools available:
Forum for group discussions. The forum supports notification subscriptions about modifications using email and RSS.
Folder for file sharing. The folders are mounted as group folder in the personal WebDAV folder that can be mounted like a local folder on your desktop and allows manipulating the files using drag and drop. The folder supports notification subscriptions about modifications using email and RSS.
Chat for synchronous discussions. This is only available when the Instant messaging module is enabled.
Contact form to contact all group members
Info message to display a welcome or info message that is shown when starting the group in the runtime environment.
Members list shows the list of members.
Wiki a Wiki used only in the group context
The BusinessGroup allows synchronization of group members with
your Instant Messaging roster list. This features is only available when the Instant
messaging module is enabled.
Synchronized groups appear in the top navigation night next to the IM status. In brackets two figures are show: number of online buddies and number of total buddies. Buddies in this terminology are users that are in a buddygroup that you participate.
IM roster synchronization is only enabled for business groups of type
BuddyGroup and can not be configured.
OLAT comes with two Shibboleth 2.0 compliant authentication providers. This means, OLAT integrates with your existing Shibboleth infrastructure for authentication and you may use the attributes released by your Shibboleth Identity Provider to restrict access to courses.
OLAT needs a Shibboleth Service Provider (SP) to protect the resource , and to provide the attributes for the authenticated identities. For an introduction about how install and configure a Shibboleth Service Provider 2.0 with Apache, mod-shib, shibd (Shibboleth daemon), mod_jk see OLAT Installation & Administration Documentation.
OLAT uses the SWITCH provided Embedded Discovery Service, which gets configured via the shibbolethlogin.html.
OLAT retrieves the shibboleth attributes directly from the request in org.olat.shibboleth.ShibbolethDispatcher.
Just set the loglevel to DEBUG for the org.olat.shibboleth.ShibbolethDispatcher to get the Shib attribute Map for an authenticated user in olat.log.
OLAT requests a minimum set of attributes from the Identity Provider. The required attributes are: Shib-SwissEP-UniqueID, Shib-InetOrgPerson-mail,
Shib-Person-surname, Shib-InetOrgPerson-givenName, and Shib-SwissEP-HomeOrganization.
When a user first successfully authenticates herself via Shibboleth, she must register with OLAT. The user is asked for a username which identifies the user within OLAT. A user profile is generated and the email address provided by the Identity Provider's Attribute Authority is automatically added to the profile. This implies that an Identity Provider's Attribute Authority must at least provide an email address attribute. Furthermore, a unique identifier is needed which is configurable per site in the olat_config.xml. After accepting the disclaimer, the user is forwarded to the home screen and registration is completed.
Shibboleth SP provides attributes of a user to OLAT. These attributes are propagated within OLAT upon each successful authentication. The attributes can be used within course building blocks to define access and visibility rules. Note that these attributes are not persisted, except for the unique identifier (used to associate an authenticated user to its OLAT user profile) and the email address.
For easier handling of attributes, you may define a set of attribute translations in
olat_config.xml. Attributes will be available by their translated name (outName) within OLAT.
For example, a standard attribute defined by Shibboleth is
Shib-InetOrgPerson-givenName which is both hard to
remember and enter into form fields. With the attribute translation map, you can translate
this attribute's name to givenName and reference it in
your accessibility and visibility rules with its translated name.
DefaultShibbolethAuthenticationController just provides a link for
redirecting the requests to the /shib/. The user is farther redirected to the central WAYF, and upon
selection of the IdP, to the IdP authentication page. Upon successful authentication the user is redirected again to OLAT.
Login sequence with DefaultShibbolethAuthenticationController:
OLAT Login page: Shibboleth Login - DefaultShibbolethAuthenticationController > Redirect to /shib/ > mod_shib > Redirect to WAYF, choose IdP > Redirect to IDP >
IdP Authentication > redirect to the Service Provider that protects this Resource (see SP configuration in shibboleth2.xml) > OLAT (ShibbolethDispatcher - retrieves the attributes)
![]() |
SWITCH Central WAYF
![]() |
Authenticate to your IdP
ShibbolethAuthenticationController uses the SWITCH Embedded DS. It basically works like the one above, except
that the user already gets directly the WAYF on the login page.
Login sequence with ShibbolethAuthenticationController:
OLAT Login page: Shibboleth Login Embedded WAYF, Choose your IdP (ShibbolethAuthenticationController) > Redirect to IDP > IdP Authentication >
redirect to the Service Provider that protects this Resource > OLAT (ShibbolethDispatcher - retrieves the attributes)
![]() |
SWITCH Embedded WAYF
![]() |
Authenticate to your IdP
The home site is the most personlized place in OLAT. This site is owned by the user and gives the user access to his personal things. Most things in the home are personal lists of something: list of bookmarks, notifications, notes and a list of efficiency statements from courses the users participates.
In the settings area of the home site the user can personalize OLAT. This means language, font size (three different CSS for small, medium, large), instant messaging preferences and personal attributes like birth date. A language change will only be activated at the next login. See also
If you need more user properties see olat_userconfig.xml for custom configuration of user properties.
In WEB-INF/olat_config.xml in the section
org.olat.user.UserModule
offers some more configuration options for user settings.
The home site has a portal entry page. The portal can be configured. This means the portal elements, so-called portlets, can be moved around in the portal and portlets can be activated / deactivated. The state is persisted for each user in the GUI preferences. See the section called “GUI preferences” for more info about this.
The portal, the portlets and the default portal configuration is defined in
WEB-INF/olat_portals.xml . In the PortletFactory part
all known portlets are defined giving them an identifyer called name.
In the second section, the PortalFactory, two portals are defined:
the homeportal and the guestportal
Every portal has a map called portletsConfigurations. In this map
all portlets allowed in this portal are listed. The key identifies them within the portal.
The values are portlet configuration values that are handed over to the portlet when
launched. One special value is called portletName which referres to
the name of the portlet defined in the PortletFactory.
Every portal has a list called portalColumns. This is the default list of activated portlets used whenever the user has no personalization made. The list contains lists (the portal columns) which contain references to the portlets configurations above.
Every user has a personal space to store files. In the users personal folder two virtual subfolders are present, the private and the public folder.
The private folder is only visible and accessable to the owning user. The public folder can be accessed by all OLAT users using the search workflow also located in the home site. Other users have only read only access to other users public folders. To share data in both directions one can use the project groups which does also feature a folder.
The personal folder can be mounted using WebDAV, see next chapter on this.
Every user has his own visiting card to expose more or less of his personal data to all registred OLAT users. He can upload an image and enter a text in Wiki syntax to appear on his card. In addition his public folder is visible to other users und he can be contacted by email via the contact form. If the user posts a message to the forum, the uploaded image will be integrated into the message header. Via the usersearch form a user, respectively his visiting card can be found.
The visiting card should be embedded as link wherever more information on a person
should be displayed like for example in a forum post. To generate such a link together
with the users portrait foto use the DisplayPortraitController.
OLAT has a fully compatible SCORM 1.2 runtime environement. Due to some SCORM concepts it differs a lot from the IMS content packaging viewser also integrated in OLAT. Each content object (Sharable Content Object [SCO]) communicates with OLAT over a separate channel with backend while the user is browsing the content. The API each SCO is looking for consits out of 5 javascript functions that need to be present in the SCO content.To communicate with the backend we relay on the not yet so commonly used XMLHttpRequest javascript object. It allows us to do asynchronous SCORM API call without the need for an Java Applet or other plugin technolgies.
The SCORM backend consits of three parts. First part is a standard OLAT controller which listens to the SCORM API calls and passes them further to the SCORM backend. The controller delivers also the content which is displayed in an IFrame. The API calls are passed to the API Adapter which is used in other projects as well (Ilias and ATutor).
All folders accessible through the GUI within OLAT are managed by the
FolderRunController. The actual file operations are further
wrapped by a Virtual Filesystem (VFS). See the section on the section called “Virtual Filesystem (VFS)” VFS for
further details. Folders can have security restrictions such as read/write restrictions
and quotas. All this is implemented through the functionality provided by the VFS
abstraction layer.
All of a user's folder are exported via WebDAV through a single unique
mountpoint at webdav://your.server.com/olat/webdav/. The user
authenticates via an OLAT authentication provider (see section on authentication) and is
presented its personal briefcase, and any coursefolders/groupfolders/sharedfolders she
might have access to.
After log in the home site is activated. However, it is also possible to directly jump
into a course, forum in a group etc. When creating new sites or dynamic tabs this is
handled via ControllerFactory.createLaunchController(). When the
site or dynamic tab already exists the activation is done through the
Activateable interface that must be implemented by such sites.
OLAT should be easy to use without the need to read help. However, the functionality of OLAT is so big and some of it is quite complex that it is not possible to make everything self explanatory. The context sensitive help is used wherever some help might be needed. The context sensitive help is appears with a small symbol. By clicking the symbol a pop up window opens containing the help.
The context sensitive help is placed in the html files using the velocity helper
rendering method velocityRenderDecorator.help(). The context
sensitive help is written using the translation tool explained in Customizing.
The comprehensive help in OLAT is accessed via the top navigation tools. The help opens in a new window and is organized as a regular OLAT course. This is very handy since the help has also a forum. Make sure you register to this forum notifications!
The OLAT help course is imported when starting OLAT. Modify the lines in the module
CourseModule in the WEB-INF/olat_config.xml file if you want to
have your own help course used instead of the default one.
Some GUI elements in OLAT offer configuration / personalization options. These options usually have a very local scope and do not influence the whole system. In contrary the users settings that can be changed in the home site (see the section called “Settings”) have a global scope and affect OLAT as a whole for this user.
Every user has one object GuiPreferences that can be retrieved
from the users user session
ureq.getUserSession().getGuiPreferences(). The
GuiPreferences object offers then methods to get and set values for
this user.
The users list in the group management for example features much more data than is shown in the default view. However, presenting all data would make the table very large and would force you to scroll vertically. In this table a small icon right on top of the table lets you customize this tables view. Some other tables also use this feature, e.g. the listing of learning resources has an additional date field that is not visible in the default view.
Another place where the GUI perferences are used are the menu and toolbox state. Menus and toolboxes can be put into a dynamic / hidden state. This state information is stored in the GUI prefereces.
Another example is the home site portal configuration. The users personal settings are also stored in the GUI preferences.
summary
Would you like a brief summary about how OLAT works in a technical way and how its architecture is? Here we are!
The OLAT Application is primarily a servlet application which simply needs a servlet container (e.g. tomcat) to run. It uses a lot of opensource frameworks to get its job done. The most wellknown are
hibernate for the o/r database mapping
velocity for the layouting process
spring for configuration issues
The GUI framework is a self-developed framework, which is component-based, follows the MVC (Model-View-Controller) paradigm, and focuses on reuse of component and also of workflows. It is a lot like Java Server Faces, but more powerful. Programming the GUI is very similar to developing with SWING or SWT. We have panels, Containers with layouting, pre-built components like links, forms, tables, and so on. The workflows are grouped into reusable controller classes, so that e.g. a user search (a search form, then a list of found matches) can be reused all over the places
The framework can support any language since it saves both localization and user data in utf-8. Even mixed content (content packaging with some encoding other than utf-8) works fine. There is an online translations tool within olat so that translating into a new language is a piece of cake (but still a lot of work!!) See Customizing
The OLAT programming concept can summarized as follows: Swing like - Component based (MVC), Business task (Controllers), Business logic (Managers), use of well established open source libraries
Here is a very general overview of OLAT and its concepts.

A Component is a visual representation of e.g. a Link, Form, a Table
and so on. One ore more Controller can listen to Event which are
fired when the user e.g. submits a form. Each component must provide its HTML-Renderer
(method getHTMLRendererSingleton (interface
ComponentRenderer) which know how to render its state in HTML.

Business logic is encapsulated in Controller classes (and the
Manager classes they use), whereas the layout can be controlled
by mainly modifying CSS. Almost all images can be configured using CSS also. In case you
really would like to dig even more into the layout, you can easily change the
HTML-Fragments which is HTML-Code with some Velocity commands in it
The GUI Framework has mainly two things to do upon each browser-click from a user:
Dispatch the request to the component which was clicked in the browser
find the component (with the component-id of the url) in the component tree of the contentpane of the window
call dispatchRequest(UserRequest ureq) so the
component can adjust its state
the component will fire an event to the listening controllers if appropriate (e.g. "Treenode-clicked" with the id of the clicked node)
the controller(s) which receive the event update their business logic
as a result, the controller updates some of the components it owns (e.g.
advance in a wizard step) and might fire an Event to listening
"parent" controllers. (e.g. an
UserChosenEvent
Render the new state of the screen into HTML.
Or deliver the Resource (see MediaResource Type and its implementations) to the browser if it is e.g. a PDF-Document or such (non-html)
The rendering is done by accumulating the output of the components in a StringBuffer while traversing the whole component tree. At the end, the whole StringBuffer is sent to the client's webbrowser. This uses some temporary memory, but has the advantage that the user never sees those half-baken screen in case of an error. The page will either work completely or produce a decent error screen with a reference number for the support staff.
With the release 5.2.0 OLAT introduced a significant improvement in case of user experience and architectural change. The component architecture allowed us to easily switch to a mode where the components come in play up to the browser client.
In non ajax mode all components where rendered on the server and concatenated to a big html file and delivered to the client which rendered the whole page for each click. The ajax mode allows now to just send the components that are dirty to the client and the client renders the html fragments into the GUI by dom replacement. As we do this on component level the whole process is transparent for the developer and therefore very little special effort is needed by the developer to achive this.
To make sure everyone gets the best OLAT experience the ajax mode only gets automatically enabled for fully tested browsers. See the olat-config.xml file for a list of supported browsers. The not supported browser get just the normal request based full layout page with each click.
The ajax mode has a polling feature where you can send
periodically a request to the server to check for dirty
components, that need to be rerendered on the user
screen. This is e.g. used in the
InstantMessagingMainController
to inform users about new incomming messages even if
they did not click in the OLAT GUI. See the
JSAndCSSComponent
constructor for an argument called pollintervall. If
several pollivervalls are requested by controllers the
server sets the poll intervall to the lowest available
value which can be changed in the livetime of the
controller by calling setPollIntervall. If you enabled
the Instant Messaging feature you will have a polling
channel for the whole GUI as the
InstantMessagingMainController
is always present in the upper right corner.
In OLAT ajax mode all requests to the server are send to an hidden iframe target. The answer comes back to this target as well and is processed by javascript (see: a_invoke in functions.js file). The transport between the server and the client is handled by json a lightweight data-interchange format. Json was choosen to further minimize the amount of data that gets interchanged between the server and the client.
So you have your controller which handles you clicks and
form events, but how can you listen to external events
like an incoming instant message from an other server?
There is a interface called
GenericEventListener
which you can implement for such purposes. This
interface offers you an additional generic event method
you can call yourself from outside your controller. See
again the
InstantMessagingMainController
which has the generic event interface implemented. The
generic event method for this controller gets called by
the
ClientManager
which handles connections and message listeners for all
the instant messaging clients and sends events to the
contollers.
Basically nothing special has to be done to make your
controllers work in ajax mode but by a good design you
can increase the user experience a lot. E.g. while
chatting, you may type and receive messages at the same
time. With a good design you can decouple both actions
so the user GUI gets updated while still typing. To do
this you have to split you interface into panels which
can be easily updated independently as a panel is a
component which can be adressed and setted dirty. If you
study the
InstantMessagingMainController
and the velocity files you will get an idea how to do
this.
There are a few limitations that you have to keep in mind when programming for the ajax mode.
When opening a new window by a link, you
should disable the ajax mode for this link
otherwise the link gets posted to the
background into the iframe. See
Link
component for how to disable the ajax mode
for a single link.
If you like to debug the ajax mode to see what is sended
between the server and the client use either firebug
firfox extensio where you can capture the net traffic or
the linux util tshark or wireshark which is a
packetsniffer. In OLAT code have a look into the
org.olat.core.gui.control.pushpoll
package where you find the
AjaxController
or the
JSServerController
which server the json answer by calling the
extractMediaResource method.
OLAT supports starting with the release 5.2 to support a few web 2.0 ajax libs which will be present for a longer time period, which means you can depend on and use it in your code.
Prototype from prototypejs.org . Prototype is a JavaScript Framework that aims to ease development of dynamic web applications.
script.aculo.us
is build on top of prototype and delivers very
nice effects for autocompletion and drag and
drop.
effects.js
dragdrop.js
controls.js
Extjs
(Extend the web) is our rich client javascript
Framework which offers very nice widgets like
floating panels. Extjs is also build on top of
prototype. Currently we only use tooltips and
floating panels from the ext library. See the
testing->GUI demo tab for examples of the
tooltip stuff. If you want a nice floating panel
like in the instant messaging you have to create
instances of
FloatingResizableDialogController
OLAT helps the developer with several built-in features. These should be disabled in a productive environment.
enable the GUI debug mode by starting OLAT in debug mode. Adjust olat.properties file with "brasato.debug=true" and restart OLAT. You will find a red "bug"in the upper left corner of OLAT. By clicking it you get a new inline window which has several different debug modes. The debug button shows OLAT in a view where all the components and its nesting are visible. By hoovering over some parts you will get the corresponding Controller and it's Velocity file. The functionality should also always be enabled on the demo.olat.org website (Login as Adminstrator).
refresh localized strings
this is enabled by setting
localization.cache=true
in the olat.properties.in
configure velocity engine to refresh files
this is enabled by setting
velocity.cache.pages=true
in the olat.properties.in
online translation tool, see online translation tool
The GUI debug mode helps in understanding the GUI stack structure. It is then easy to find out why something does not show up where it was intended to, see Figure 3.2, “GUI debug stack” . Refreshing the velocity pages and the refreshing localized string properties enable the developer to change/fix broken layouts and translation keys on the fly. The yellow highlightend component, visible in Figure 3.3, “GUI debug listeners box” , produces a floating box with additional information:
type, the java class name of the component.
listeners, a list of event listeners attached.
cListeners, a list of controller event listeners
translator, shows where the keys come from and in which language. Clicking on the link brings up a new browser window with the possibility to interactively fix translation keys.
nesting level, a counter for nested div's tables respectively.
The OLAT framework works with the unchecked exception principle. This means that the developer should not care about exceptions in general. Exceptions go there long way up to the top level of OLAT, where they are catched and translated into a red screen, see Figure 3.4, “Red screen of an unchecked exception.”
In a productive environment a user only gets the Error code and the Date and time information, which she should present to the support. The system administrator can retrieve within OLAT the associated stacktraces and errormessages for further measures.
The basesecurity is a little like Java Security.
A policy protects olatresources allowing permissions (e.g. access, read, hasRole, etc.) to given securitygroups
No permissions to users, if they aren't in securitygroups.
Rights are positiv and additiv.
If you have no type rights at all and someone assigns you as owner of a certain resource, you have instance rights for only this resource. Addionaly you can create other resources of this type. But if all resources to which you have instance rights are deleted, you are not more able to create one. In the opposite if you have type rights to a kind of resource, you always can create instances of this kind.
existing rights in the system:
+---------------+
| permission |
+---------------+
| hasRole |
| access |
| admin |
| nologin |
| coach |
| participant |
| read |
| bgr.archive |
| bgr.assess |
| bgr.editor |
| bgr.groupmngt |
+---------------+
create 2 security groups -> ownerGroup, -> partipiciantGroup
create a buddyGroup with name, description, introMsg and the 2 security groups
2 policies, ownerGroup -> PERMISSION_ACCESS -> buddygroup. partipiciantGroup -> PERMISSION_READ -> buddygroup
To get an overview and also have some good starting points for sample code login to OLAT as adminstrator and check if you see the main tab (GUI Demo). This tab shows you most of the visual elements of the OLAT framework in action. To access the sourcecode just click the view source code link (source shows you the java source and velocity the html fragments). If you cannot see the tab it is not enabled. To enable it go to te olat_extensions.xml file and uncomment the spring bean: olatsites_guidemo and restart OLAT. You can also debug the whole GUI by starting OLAT with the brasato.debug=true in the olat.properties file and restart OLAT. The green bug in the upper left corner lets you explore the GUI and lets you view the underling source by clicking the links. See also chapter:GUI debug stack.
For easy reference the OLAT demo server should also be setup with the settings above.
The (HTML) attribute values (see Velocity pages or in Renderers) could contain DB stored data (e.g. long course name becames an alt attribute) or a translated string (found in LocalString*.properties).
The HTML attribute values (in Velocity pages or in Renderers) must be HTML escaped (using org.apache.commons.lang.StringEscapeUtils.escapeHtml), else could break the javascript:
- use example in Velocity page: alt="$r.translateInAttribute("import.example")"
- use example in renderer: sb.append("\" title=\"" + StringEscapeUtils.escapeHtml(title) + "\">");
See org.apache.commons.lang.StringEscapeUtils and org.olat.core.gui.render.velocity.VelocityRenderDecorator.
We must avoid using HTML attribute values with single quotes, since it not possible to escape using the StringEscapeUtils methods. The translated strings used in non-attribute values doesn't need escapeHtml. (see GUIMessage.renderError) See also org.apache.commons.lang.StringEscapeUtils.escapeJavaScript.

The OLATServlet is the main entry point for user requests from the browser. This
servlet uses Dispatchers to further process a request. This is done by the class
DispatcherAction. Furthermore, the OLATServlet is responsible
for initializing and configuring OLAT. This is done by calling the
ConfigurationManager at startup.
DispatcherAction analyzes the first part of the incoming request
URI and decides which Dispatcher is repsponsible for further dispatching the request.
These Dispatchers are namely:
DMZDispatcher: Responsible for all requests of users
who are not yet authenticated. DMZDispatcher processess all URIs starting with
/dmz/, namely the login process.
AuthenticatedDispatcher: Responsible for all requests
of users who have been previousely authenticated. AuthenticatedDispatcher will
check each incoming request if it has an authenticated user associated. If not,
the user is redirected to the login screen. AuthenticatedDispatcher catches all
requests starting with /auth/.
ShibbolethDispatcher: This Dispatcher catches all
requests comming asynchronuousely from IdentityProviders. Its assigned sub-URI is
"/shib/", please refer to the Shibboleth Protocol for further
infos on Shibboleth.
Besides OLATServlet, there are a few more servlets used within OLAT. Find a brief description of the functionality they provide below:
SecureWebdavServlet: This is the servlet handling all
WebDAV requests. The servlet is mapped at URI /webdav/. This URI is the same for
every user. The WebDAV contents are assembled for every user after authentication.
StaticsServlet: This servlet is handling requests for
statics such as imges or CSS and Javascript files. StaticsServlet is mapped to the
sub-URI /secstatic/. This sub-URI is further followed by a handler name (i.e.
/secstatic/HANDLER/. According to the handler name, SecstaticsServlet passess the
request to the handler which registered for the given handler name. Handlers can be
configured in olat_config.xml. A handler must implements
PathHandler and an alias must be given under which requests
are dispatched. See the configuration of the StaticsModule in olat_config.xml namely
its Path entries for an example.
RSSServlet: This servlet pretty much does what it says.
It provides RSS feeds for subscribers and is mapped at /rss/.
OLATDevServlet: This servlet is used for development
purposes. It implements workflows usefuly to the development of OLAT. With this
servlet, developers can edit Velocity pages on the fly by enabling debugging in the
GUI. Furthermore a translation tool helps in translating OLAT to other languages.
The translation tool is accessible through the administration page.
Each User has a UserSession which stores the windows amongst
other things. The Windows contains at least one
Window. A window is the server-side representation of a browser
window. A window has a content-pane (like Swing) to store the "real"
content. Each window is owned by a ChiefController which is
responsible for the navigation the windows offers, but not for its content. So e.g.
after logging in, the tabs on the top ("home",
"groups", ...) are controlled by the chiefcontroller.
A Controller is a responsible for a certain business logic /
workflow
Each controller must provide an initialComponent, which is the visual representation of the controller at any given time. The component will be rendered later into a HTML fragment which is part of the whole HTML page. You could think of a Swing Panel which is embedded into some place in the webbrowser window. The code creating a controller and asking for its initialComponent can determine where to visually put the component
Managers within OLAT, by definition, are classes that implement basic services
useful for implementing the actual business logic. The
OLATResourceManager for instance provides the basic functionality
for handling OLATResources such as creating, reading, updating and deleting. Any
Controller within OLAT that has to deal with an OLATResource should use the
OLATResourceManager and not directly imlement basic functionality. In particular,
Managers wrap all database access.
OLAT provides its own implementation of a filesystem abstraction layer, called Virtual Filesystem (VFS). The VFS represents a tree structure with folders and files within folders much like a regular filesystem. Folders are called containers and files are called leaves. There are different implementations of containers and leaves serving different purposes. These offers the flexibility to change the implementaion of the file and folder classes and may switch to an other storage solution (like database) without rewriting code in the file handling classes.
Both VFSContainer and VFSLeaf inherit
from VFSItem with generic attributes and methods common to both
such as a name, methods to query wether delete or write is allowed and others. The most
important method defined on VFSItem is resolve(String name). This method allows to query
a VFSItem to resolve a path (i.e. /home/user/readme.txt). A path is much the same as a
path on a regular filesystem. Resolve() returns a VFSItem which can be either a
VFSContainer or a VFSLeaf.
The second most important feature of VFSItem is its
VFSSecurityCallback. A security callback defines what operations
are allowed on the item. VFSSecurityCallback defines whether read/write/list/copy and
delete operations are allowed. If a specific item has no VFSSecurityCallback defined,
all operations are allowed by default. VFSSecurityCallbacks get inherited by all
children attached to an item. So for example, if a container has a security callback set
that denies write operations, the container itself and all of its children would not be
writable. VFSSecurityCallback also allows to define quotas on a object. The
VFSSecurityCallback is checked by the VFS implementation. I.e. if you perform a write
operation, the security callback is checked and an exception is thrown if the security
callback denies write opertaions on the specific item.
There are various implementations of VFSContainer and
VFSLeaf already in place. The most handy probably are
LocalFolderImpl and LocalFileImpl which
are an abstraction of a local filesystem. These are probably the place of start for all
your VFS tasks. Simply create a LocalFolderImpl with a File pointing to a local folder
as constructor asgument. You can then resolve any file or folder within this local
folder through the resolve() method as described above. You can create new folder or
files through the respective methods of VFSContainer (createChildContainer and
createChildLeaf).
A special type of implementation are all implementations based on AbstractVirtualContainer. Those will be described briefly below.
NamedContainerImpl
: This is useful if you want a given container to show up under a
different name. Note that this name will also be used to resolve the container.
This works much like a symlink on a regular fielsystem.
MergeSource
: This is a powerful but maybe a bit difficult to
understand type of virtual container. It is also the
reason why we chose to do our own implementation of
a virtual filesystem instead of using for example
Apache commons VFS. A MergeSource combines several
containers or leaves into a single container. To
give you an equivalent on a local filesystem, you
would create a new folder, and then symlink folders
or files into this folder to get the same effect. A
MergeSource can merge containers in two different
ways provided by two different methods.
addContainer
will show the given container as a child of the
MergeSource.
addContainersChildren
will show the given container's children as
children of the MergeSource. To avoid naming
conflicts, only one container can be addad via
addContainersChildren
. Furthermore you must define whether it is allowed
to write to this container. If someone wants to
write to a MergeSource, this is only possible, if a
container was previousely added via
addContainersChildren
with the write flag set to true. This feature is
manly used in the WebDavProvider to show folders of
different courses a user is owner in one place which
makes it handy to upload files for different
ressources. This folders are spread on the local filesystem and the MergeSource allow us to see them in one place.
See
StreamedImpl
: This is useful, if you just want to decide on the fly, what the
contents of a file is. StreamedImpl simply directs its InputStream and
OutputStream to whatever you provide at construction time.
With these implementations, you are quite flexible in presenting a filesystem the way you want. Note that you may also combine different implementations of VFSContainer or VFSLeaf. The WebDAV implementation in OLAT makes use of most of the features provided by VFS. At its root there is a merge source. The merge source combines VFSContainers which themselves are wrapped by NamedContainerImpls. This way, the different types of directories exposed by WebDAV can be presented in a sensible way to the user.
See javadoc of the package org.olat.core.util.vfs to get some code examples on the general usage
See VFS javadoc for more information.
OLAT uses the hibernate relational persistence framework to map objects to relational database tables. See the hibernate documentation to learn how to use this framework.
Every persitable class in OLAT inherits from or extends
org.olat.persistence.PersistentObject . This guarantees that
every class has the following common attributes:
private Long key = null
private Date creationDate
private Date lastModified
To make a class persistable by the hibernate layer one needs to implement getter and setter methods for every attribute added to the class; this is a hibernate convention. Getters and setters can be private if they should not be used by code outside the class.
Every class must also have an interface if it wants to use the lazy initialization options. See the hibernate documentation for more information on this. These are the naming conventions when using interfaces:
Interface:
ClassName.java
Implementation:
ClassNameImpl.java
Every persistable class must have a hibernate mapping description. This mapping file (an XML file) must be stored in the same package as the mapped class. See the hibernate documentation to learn how to write the mapping file. There are several strategies how to write the mapping.
You should never access the OLAT database directly as you bypass hibernate and all its logic which could let to problems. To access the OLAT database you shoule always work with the provided manager classes. To get you an idea of what kind of manager there are available have a look at the relational database schema: OLAT Database Schema v6.2.0
OLAT provides a extension mechanism similar to eclipse's plugin mechanism. It is of course never that advanced, but serves as the main purpose to allow developers to extend OLAT without touching the original source code
If you like to develop some extensions or just play with the sourcode and debug some workflows of a running OLAT you will need to run OLAT out of the Eclipse IDE. You can download the great Eclipse IDE including the necessary web tool plugins here
A step by step guide on how to set up Eclipse with OLAT can be found here.
To extend OLAT, you add one line in the olat_extensions.xml file pointing to your
extension, and place your extension.jar file in the WEB-INF/lib directory. For starters,
see the package ch.goodsolutions.demoextension or com.xyz.demoextension in the normal OLAT source code
distribution.
The following extension points are definded so far (see table below). There is an detail step by step example that includes the ActionExtension. The ch.goodsolutions.demoextension shows the usage of the SitesCreator extension element.
Table 3.1. Extension points
| Extension interface | Extension point | Classes applying the extension | Description |
|---|---|---|---|
|
org.olat.extensions.globalmapper MapperProvider | org.olat.dispatcher.DispatcherAction | org.olat.dispatcher.DispatcherAction | the extension can obtain a Mapper and get to know
the path it is associated with |
| org.olat.extensions.action.ActionExtension | org.olat.home.HomeMainController | ... | the extension can provide a link with a text and a description and define the action that happens when the link is clicked |
| org.olat.extensions.css.CSSIncluder | org.olat.gui.components.Window | org.olat.gui.css.CSSGenerator | the extension can provide a path (which it owns by using a
Mapper extension where its stylesheet resides that
should be included in every OLAT main window. Since all CSS definitions from
all extensions share the same name space, it is useful to prefix the css
classes with some short name of the extension. CSS class definitions should
not start with "o_" since that prefix is used for the OLAT
base system |
| org.olat.extensions.hibernate.HibernateConfigurator | org.olat.persistence.DB | org.olat.persistence.DB | the extension can add additional hibernate mappings here. Keep in mind that creating tables is quite difficult to do here. So this needs a separate sql script in the database dialect you are using. Any suggestions for a cool solution here are greatly welcomed! |
| org.olat.extensions.sitescreator.SitesCreator | org.olat.gui.control.generic.dtabs.DTabs | org.olat.FullChiefController | if the extension wants to add one or more new sites (like
"Home", "Groups", "Learning
resources"), then a SitesCreator needs to be
offered by the extension which in turn must return a list of
SiteDefinition objects, each of which is responsible
to create a SiteInstance. |
| org.olat.login.SupportsAfterLoginInterceptor | org.olat.login.AfterLoginInterceptorController | org.olat.user.UserModule | Modules which want to present a workflow right after login (e.g. ChangePasswordController) can add a controller
by using the AfterLoginInterceptionManager.addAfterLoginControllerConfig(). The configuration
(which controller to use, when to show again, should user be forced) is distributed by
each module itself. (e.g. serviceconfig/org/olat/user/_spring/olatdefaultconfig.xml)
No central configuration will be needed. See also the comments in AfterLoginInterceptionManager. |
While the extension points provide a mechanism to add your own software components to offer new functionality to users, typically having a GUI element, the multi user event (MUE) infrastructure can be used to listen to certain events within the system and execute some code when such events happen. This type of extension typically does not have a GUI for endusers, but enables you to react to new conditions.
When operating a cluster, the multi user events are distributed to the entire cluster. Therefore this concept is often used to signal about changes in module configurations.
Example: your code could register as a listener to multi user events in the identity event channel. Whenever something happens there, the event() method of your code will be called and you have a chance to do whatever you need to do. E.g you could check if the event was of type NewIdentityCreatedEvent and if so, you could add this user to a predefined set of courses that you want each user to be enrolled in.
Be aware that all OLAT events are executed synchronously. This means: when your code takes a long time to execute, you should use an executor thread to do the long-running task and release the event method as soon as possible (otherwhise the user might be waiting...).
Example code:
// In your module, listen to channel. Your module must implement the GenericEventListener interface.
coordinator.getEventBus().registerFor(this, null, PersistingManager.IDENTITY_EVENT_CHANNEL);
// Listen to events:
/**
* Receive the event after the creation of new identities
*/
public void event(Event event) {
if (event instanceof NewIdentityCreatedEvent && autoSubscribe) {
NewIdentityCreatedEvent e = (NewIdentityCreatedEvent)event;
Identity identity = ManagerFactory.getManager().loadIdentityByKey(e.getIdentityId());
doWhateverYouNeedToDoWithNewUser(identity);
}
}
Table 3.2. Multi User Events
| Event Channel | Event | Description | Available since |
|---|---|---|---|
|
| NewIdentityCreatedEvent | Fired whenever a new identity has been created (self registration, admin or batch creation like during an LDAP sync | OLAT 6.3 |
|
| MultiUserEvent | Fired when the info message has been changed. The message is in the event and can be retrieved with event.getCommand() | OLAT 6.1.1 |
For complete list : search for references of class MultiUserEvent in Eclipse.
In short: write your new code (ch.goodsolutions.demoextension or com.xyz.demoextension), deploy the code as
a jar, add a line in the olat_extensions.xml, restart olat.
This section will cover the task how to implement a new workflow or change an existing workflows that starts with an user request (e.g. clicking a link in the browser gui or submitting a form with the browser). There are two ways to extend the functionallity in OLAT. First use the extension mechanism. A good example for the extension mechanism is the ch.goodsolutions.demoextension (To enable it uncomment the demoextension in the webapp/WEB-INF/olat-extension.xml and you will get a new tab in the main navigation called "demo-site"). The advantage of extensions is that they can be deployed as a single jar file containing all resources they need (properties files, translation files, images and classes). The other possibility to extend OLAT is to modify existing workflows. The main differeces between the two ways are that the resources of the existing stuff is spread in several locations and loaded automatically upon a request. Resources of existing workflows are located at:
classes --> YOURPACKAGENAME/yourclass.java content (velocity templates) --> YOURPACKAGENAME/_content/yourcontent.html css files (static content) --> YOURPACKAGENAME/_static/css/yourcss.css image files (static content) --> YOURPACKAGENAME/_static/css/img/yourimage.png js files (static content) --> YOURPACKAGENAME/_static/js/yourjs.js translations (properties) --> YOURPACKAGENAME/_i18n/LocalStrings_en.properties
The resources of content and translations are automatically reloaded if you switched off caching of velocity pages in the olat.properties file (velocity.cache.pages=false) and the language files (localization.cache=false).If you set up OLAT in Eclipse the IDE can also do hot code replace and replace java method bodies without restarting Tomcat. This is handy during development as the changes show up without restarting tomcat. Therefore is might be faster to develop your extension with your files in the different locations above and if you finished work move it to an extension directory where all staff is bundled and can get exported into a jar. We will first cover adding new content without the extension mechanism and later put the stuff into an extension.
Basically the following steps are necessary to create a new site with interaction in
OLAT. Create a class that inherits from the BasicController
class. By overriding the event methods you can listen to you events like your
buttons or your created links and react according to the event. The controller class
works together with a template file you specitfy as VelocityContainer you created. See the index.html file in the demo extension or
any other template file under PAKCAGENAME/_content as guide to your content. The special
variable "$r" gives you access to the framework methods for rendering
componets ($r.render("myLink")) and variables created in your
controller ($variableName).The class
VelocityRenderDecorator gives you a list of methods that can be
used in all velocity pages.
We will now go through all steps in detail for a simple example (The famous Hello world example!) with a link that changes to your username instead of "world" and shows an image after clicking. You will find this example in the source code of olat with all needed resources: search for ch.xyz.demoextension in the source.
Go to the sourcedirectory ch.xyz.demoextension and create a new class that extends
from BasicController. We did this for you and named the class
HelloWorldController.java. See the comments inside the class that give you helpful
hints about the code. You have now a Controller class that listens to a template
page. The HTML page (container) it listens to is specified as class var in the top of the class.
myContent = createVelocityContainer("helloworld");
The "helloworld.html file" you have to create in YOURPACKAGE/_content folder. And will be resolved as helloworld.html This file contains HTML and Velocity template code (For a reference of the Velocity template language see: VTL reference ). Inside the velocity template you can reference variables from your java class and also complete other components from other controllers. See for example:
myContent.contextPut("myContentVariable", myString );
The variable "myContentVariable" is accessible it the "helloworld.html" by typing "$myContentVariable" (without the capitals!)
Now we cover the PackageTranslator (translator) object which got automatically created for our controller. It is responsible for translating the content to the language the logged in user choosed. The translator looks for an other file that you have to create in YOURPACKAGE/_i18n. You can access it everywhere in your controller by calling getTranslator().translate("yourKey") LocalStrings_en.properties . This are java properties files where you can specify "key = value" pairs for the string you like to have translated.
You should now have all needed stuff: A controller class where you listen to your action. A velocity page that produces HTML output and a LocaleStrings_**.properties file for each language.
As there is no direct URL that you could enter in the browser to access you newly created content we have to "enable" it somewhere else. Excursus: OLAT URL's explained. Each link in OLAT is unique and gets dynamically generated for every page. The URL's contain the dynamically added component id and also an timestamp that gets incrementet with each click. So every OLAT URL is only valid once. This makes development of applications much easier as you do not have to worry about actions sended twice.
For testing purposes we add our content to the menu in the Home. To turn on this enable it in by uncommenting it in the "olat-extension.xml" file and restart OLAT. Make sure you also enabled the OLAT debug mode in the olat.properties file. You find the olat-extensions.xml file under /webapp/WEB-INF/olat-extensions.xml. You should now see the menu entry "Hello World!" . Clicking this link will create our controller and show our content in the content area of OLAT. But how do I add a link to this menu?
Sure, we have to find the appropriate controller and add a link there and listen to the event the link produces and create our own controller. But first we need to find the right controller out of many. There is a handy feature in the debug mode of OLAT that helps in this case. If you started OLAT with the brasato.debug=true enabled in olat.properties you will see a green bug in the upper left corner, open the overlay and you can hover over the content to get usefull information about each component. Hover over the leftside menu top title and a layer will appear. Look for listener and you will find org.olat.test.HomeMainController. This controller listens to the links in the left side menu. This mean that this is the right place to add our "Hello World" link.
If you open HomeMainController you will see that it consists off the same structure as our own controller. You will find also an event method that listens for events to this controller. See inside the source and you will find the place where the link is added to the menu tree and where the event is handeld.
Simply pack your olat extension project into a jar file (e.g. in Eclipse File-Export-JarFile)
To change our "Hello World" example into an extension that can be deployed as a singe jar to following steps are necessary:
Create file structure
xy.test.demoextension
controller
_content
_i18n
_static
HelloWorldExtension.java
Copy files to folders. The HelloWorldController to the controller folder and the properties files to the _i18n folder. Make sure you rename the "LocalStrings.properties" files to "LocalStrings_xy.properties" where "xy" stands for the country code like "en" for english as they are now all in the same folder. The images and optional css (Stylesheet) files belong to the _static folder.
Let's now create the Extension class. The extension class is
responsible for creating an instance of our HelloWorldController and also to provide
a visible link in the layout. Every Extenstion has therefor to implement the
Extension interface. The only interface method "getExtensionFor()"
is looking in a Hashmap for our extension that is is registered in the
olat_extension.xml file. See below for an example. To add our extension to the
ExtensionElements we have to add it first. We create a ExtensionElements object,
that holds only objects of type ExtensionElement (see the table the section called “Extension points” at the beginning of the chapter for a list of the know
ExtensionElement's). The HelloWorldExtension uses the ExtensionPoint
ActionExtension which adds a link to the menu in the user
"Home". The ActionExtension in our example is done with an inner
class for brevity.
The only thing left is to export the extension as a jar file put it in the olat lib directory and enable your extension in the global olat_extension.xml file (see below) and restart olat and your done! If you consider sharing your extension with the OLAT community send your jar to the developer mailinglist and we will include it in OLAT or provide a download link on www.olat.org. Your help is very welcome and do not hesitate sending us your extension or ask questions on how to create one!
Consider the file olat_extensions.xml in the WEB-INF directory. Add your bean to
the olat extensions by adding a line where the comments says to add. The class attribute
must point to your class which implements the Extension
interface.
<!-- generic OLAT extensions -->
<bean id="extmanager" class="org.olat.extensions.ExtManager" singleton="true">
<property name="extensions">
<list>
<!-- classes implementing the Extension interface -->
<!-- add your extensions here! -->
<bean id="demoextension" class="ch.goodsolutions.demoextension.DemoExtension" singleton="true" />
</list>
</property>
</bean>
You have to implement a class XXXCourseNode which extends GenericCourseNode. The XXXCourseNode class will be serialized into 'runstructure.xml'. Define non-persisting attributes as transient e.g. log . The XXXCourseNode must implement : createEditController, createNodeRunConstructionResult, calcAccessAndVisibility and isConfigValid. (see Section 2.4, “Resource Course”)
You may implement informOnDelete, cleanupOnDelete, archiveNodeData, exportNode and importNode. The default implementations does nothing.
Design decisions for building block data :
Do you will have data in the database ? Where do you stored the entry point to the building-block data. As an example : the project-broker store the project-broker-ID in the CoursePropertyManager.
Do you will have data on the file-system ? Where should the files be stored ?
The files must be cleanup in method 'XXXCourseNode.cleanupOnDelete'. Do not forget to implement cleanup file-data for user-deletion!
To access the course-base-container use : courseEnvironment.getCourseBaseContainer().getRelPath() .
You want to use the assessment-tool ? => Your course-node must implement 'AssessableCourseNode'. Be careful with addional data-structure e.g. like project-broker. The assessment-tool navigate currently only to course-nodes and not to the sub-elements.
Configuration : The static building-block configuration can be stored in the ModuleConfiguration
(NodeEvaluation.getCourseNode().getModuleConfiguration()). This configuration will be stored
in the file 'runstructure.xml' of the course and will be persisting over export/import of the building block.
The dynamic building-block configuration can be stored in the CoursePropertyManager (course.getCourseEnvironment().getCoursePropertyManager()).
This data will be removed by an export of the building block.
Could the new course-node implement as learning-resource ? A learning-resource is more general and could be linked-into a course.
Does the course-node need cluster-wide synchronization ? Does the course-node need caches ? (see Section 3.6, “OLAT Clustering and Scalability Concepts”)
A controller is a reusable business-workflow (see also above 'Layers in short'). A controller has the following responsibilities:
create the initialcomponent upon construction
dispose the controller and subcontrollers upon a call of
doDispose
A controller can use:
the Manager classes to do the work (database/filesystem)
other controllers to delegate tasks to (e.g. to to delegate a user search to the UserSearchController)
The method getWindowControl() to obtain the possiblity
to send a info/warning/error msg to the screen and to push a new gui-layer above the
current layers (kind of like a modal dialog, e.g. for a user search) and to pop the
layer if a subcontroller's work is done.
Please see the javadoc of each controller to see what it is for. Briefly there are:
generic GUI-Controllers (Layout, Wizard)
generic Business-Controllers (e.g. UserSearchController, FolderRunController, ForumController)
specific Business-Controllers (PublishControllers)
See the java classes extending the
Defaultcontroller class
Managers within OLAT are conceptually responsible for providing the basic functionality to the workflows (i.e. controllers implementing the actual workflows). Managers usually provide persistence functionality such as storage in the database or on the file system. In general, no workflow should directly deal with the filesystem or the database, it's all handled through a dedicated manager implementing this functionality.
We could have used ResourceBundle from the Java SDK, but you cannot clear the cache which is annoying for translation. Since we use java.util.Properties, the encoding is assumed to be iso_8859_1 (see java.util.Properties). However, if you use the online translation tool (Administration / Online translation tool) you can work with input in any encoding (just submit the form...)
The i18n system is described in a separate document "Understanding the Internationalization (I18n) and Translation System"
The browser always sends it page in utf-8. encodings from resources like html files from content packages are converted automatically
If The browser always sends it page in utf-8. encodings from resources like html files from content packages are converted automatically
A Mapper is used to route a certain url directly to some place
in code. This is useful if you want to e.g. deliver static resources.
user-local mappers: the url routing only applies to the current user
global mappers: the url routing applies to all users
Properties are used in very many places in OLAT und persisted in the table o_property. A property is a data container and has at least one name apart from its id and lastmodified date. Addionally it has several fields (category, floatvalue, stringvalue, textvalue, etc.) to store more information depending on the context it's used. If some of this fields are not used, they can be null. Creating a new property, you have to think about how to retrieve it. Adding several properties to a already used or new specified category, you can easily retrieve them.
The DB is used as the main and only entry point for all
database operations on the low level. It uses a threadlocal variable to keep the current
hibernate session and the transaction state. At the end of each UserClick/Servlet
Response, the session must be closed by using DB.getInstance().close(), before it can be
returned into tomcat's threadpool!
Controllers should only use the beginTransaction(),
commit() and some helper functions to reload an entry from disk if
the associated session has been closed (e.g. transactions which would last several
userclicks)
Manager classes should encapsulate business logic and thus be the only ones that use the find/save/update methods of the DB class
The
org.olat.core.util
package provides helper classes and their functionality used
throughout the framework, the managers and the controllers.
Following you'll find a brief outline of utility
classes which might be helpful if you are developping with
OLAT.
org.olat.core.util.bulk
: Package which provides the abstract class
BulkAction
. Extend it when you have to handle a list of users
with the same action. To understand how it's
implemented have a look at the
BulkAssessmentWizardController
org.olat.core.util.cache
: Package which provides some implementation of
caches used by the DB abstraction layer, the group
management and the base security package. See the
section on caches for details.
org.olat.core.util.component
: Package which provides visitors and traversers
org.olat.core.util.controller
: Provides everywhere useful controllers
org.olat.core.util.event
: Package which provides a simple event handling
system, described in more detail in the section on
events.
org.olat.core.util.i18n
: This package handles all i18n within OLAT. See the
section on i18n for details.
org.olat.core.util.locks
: Package which provides a manager to handle locks.
A workflow may set/remove locks to prevent multiple
users from accessing some functionality within OLAT.
Locks are persisted accross sessions.
org.olat.core.util.mail
: Contains
Emailer
, a class providing basic email functionality.
org.olat.core.util.memento
: Provides the interface for the memento pattern
org.olat.core.util.nodes
: Provides classes to represent a simple node
structure and a helper class
NodeHelper
to manipulate such structures.
org.olat.core.util.prefs
: Package whcih provides structures persistence
classes for handling various persistent preferences.
org.olat.core.util.radeox
: Package extending the Radeox wiki markup.
org.olat.core.util.rss
: Helper classes for handling RSS data.
org.olat.core.util.traversal
: Package providing helpder classes for traversing
structures.
org.olat.util.tree
: Package providing helpder classess to work on tree
structures.
org.olat.util.vfs
: Implementation of the virtual filesystem. See
section on virtual filesystem for details.
org.olat.core.util.xml
: Provides helper functionality to easy handle xml
stuff
This chapter describes clustering and scalability concepts introduced to OLAT 6.1. These concepts help to distribute OLAT over several tomcats with a load balancer in front. It is not designed for high availability, sessions on a node which goes down are kicked out. But it allows to go beyond the 2-3GB JVM RAM boundary by running multiple tomcats each with 2.5GB JVM RAM assigned.
Note that OLAT can either be run in singleVM or in cluster mode. Nevertheless the developer needs to know about clustering mechanisms even if he or she would not run an OLAT in cluster mode.
Hence when you use Caches, EventBus or any type of locking or synchronization read this section carefully to ensure that your code works in both singleVM and cluster mode deployments
When resources are used from within a single Java VM and are to be protected by serializing access to them, the synchronized keyword is used or the classes from the java.util.concurrent package. Since all calls go through the same JVM, this works fine.
When running OLAT in cluster mode (see chapter about the OLAT cluster installation), then several JVM share access to the same database, the same filesystem, and the same Instant messaging server.
It is of course necessary to protect the access to those resources that are shared between JVMs in order to guarantee a consistent and stable system.
Since a developer should not have to care whether or not its OLAT is running in singleVM or cluster mode, several „infrastructure tools“ (java interfaces) have been introduced to alleviate the life of a developer.
All those tools can be accessed through the Coordinator, which can be obtained via
CoordinatorManager.getInstance()
The four tools provided are:
Syncer : Synchronize access to OLAT-Resourceables
Locker: Acquire and release Locks, mainly for GUI-Locking
Cacher: a hierachical cluster-capable CacheManager
EventBus: for sending messages on channels and distributed cluster-wide
The next section explains what those tools are for
The Syncer allows cluster-wide synchronization on a given OlatResourceable. That means that for each given OlatResourceable, at most one thread of all JVMs executes the „execute“ method of the provided SyncerCallback or SyncerExecutor concurrently.
public <T> T doInSync(OLATResourceable ores, SyncerCallback<T> action);
if you don't need a return object, then use the SyncerExecutor instead
public void doInSync(OLATResourceable ores, SyncerExecutor action);
An OlatResourceable is considered to be equal to another when both its type (a string) and its Id (a Long) are equal.
PS: In the current Syncer implementation the DB transaction is committed before doInSync returns. That means that you will have multiple commits during the handling of a request
PPS: In the current Syncer implementation doing so-called nested-doInSyncs is not allowed. Nested doInSync is when you are within a doInSync and call doInSync again. This will throw an Exception. The reason for not allowing this is to avoid potential, distributed deadlocks.
In SingleVM mode, the OlatResourceable is converted to a String and then kept in a Map to serve as a monitor for the synchronized command.
public void doInSync(OLATResourceable ores, SyncerExecutor e) {
synchronized (DerivedStringSyncer.getInstance().getSynchLockFor(ores)) {
e.execute();
}
}
In Cluster mode, first the same as in the SingleVM is done to limit concurrent
select-for-update tries to one per JVM .
Then the pessimistcLockManager is used, which obtains a write lock on the row in
table Plock representing the olatresourceable. if the olatresourceable does not yet exist
in the table, it is created first using another select for update on a row that is
guaranteed to exist on system startup. (a global lock)
public void doInSync(OLATResourceable ores, SyncerExecutor e) {
String asset = OresHelper.createStringRepresenting(ores, null);
synchronized(DerivedStringSyncer.getInstance().getSynchLockFor(ores)){
DBFactory.getInstance().beginTransaction();
// acquire a db lock with select for update which blocks other
// db select for updates on the same record
// until the transaction is committed or rollbacked
PessimisticLockManager.getInstance().findOrPersistPLock(asset);
e.execute();
// now release the lock - only if previously no other
// transaction was spawned.
// commits only if the beginTransaction above was the first
// nested - otherwise the commit reaching level floor will
// actually commit it.
// the transaction bracket is opened and closed here so that
// also non-db synchronized actions such as file system
// operation can be safely synced.
DBFactory.getInstance().commit();
}
}
This is the replacement for the old LockManager class .
It allows to obtain a lock on a given OlatResourceable.
Both non-persistent and persistent Locks can be aquired.
A non-persistent Lock is cleared when either the JVM crashes or (more often hopefully ;)) when the user that is the owner of a Lock logs off or when the controller that technically holds the lock releases its lock, e.g. in the dispose() method. Non-persistent locks are used for GUI-Locking.
A persistent Lock survives JVM startup/shutdowns and is used to protect access to resources where an explicit release of the lock is needed. (e.g. when editing a QTI document)
Try to avoid persistent locks in favor of non-persistent locks.
non-persistent locks:
In SingleVM mode, a lock is essentially a LockObject held in a Map of the SingleVMCacher singleton.
In Cluster mode, the lock is written to the database to be cluster-capable.
The persisting locks are backup by a database table for both the singleVM and the cluster mode.

The Cacher is a hierachical cache structure to cache data
If, for example, you would like to obtain a cache per identity per course (to e.g. be able to cache the assessment data for the user), then you must build the hierarchy as depicted above. The CourseEnvironment creates an AssessmentManager instance per Course which then obtains a CacheWrapper from the Cacher, and e.g. will create child CacheWrappers for its Course and request CacheWrappers for each identity of this course (normally lazily, that is only when data needs to be read). For each level the ehcache configuration is read from the olatcoreconfig.xml (e.g. a „Course“ entry is then applied to all child CacheWrappers which are created with an OLATResourceable which is of type „Course“)
Configuration parameters are:
max time to live
max time to idle
max elements
See also the Hints and cookbook section.
The hierarchy is only used for namespacing reasons. To e.g. obtain a cache for a certain user in a certain course, it is recommended that the responsible manager class holds a root-CacheWrapper and then for each course it creates a child CacheWrapper which in turn is used to create a CacheWrapper per user by using the appropriate method on the course CacheWrapper. The actual cached data for an identity resides in the CacheWrapper obtained for the Identity.
PS: Make sure to not store objects retrieved from the Cacher in instance variables. Otherwise an object might have been replaced with a newer version in the Cacher yet you don't notice as you're still holding the old object. Also it would violate the idea that the Cacher can control how many objects are kept in memory (memory management)
PPS: Note that there is currently an issue (OLAT-4424) where you can potentially run into a nested doInSync exception due to a cached object having to be reloaded
The CacheWrapper class in OLAT is mainly a wrapper around the EHCache from the ehcache library.
The Cluster mode version is the same as the SingleVM version, except that it invalidates elements that were „put“ into or „removed“ from a cache by sending an ClusterCacheWrapperEvent to all other cluster nodes.
The EventBus is used to broadcast messages which normally affect more than one user.
It offers channels (identified by a OLATResourceable) to separate between different topics of interests. Code can subscribe/register and unsubscribe/deregister.
If your code subscribes to the channel, it is also responsible to unsubscribe at the end of your object's lifecycle (e.g. in the dispose() method of your controller).
The event bus has a topic or publish/subscribe architecture – that is the sender of a message does not know
if there is anybody listening at all on a particular channel
when its message will arrive
The recepient on the other side should:
only take a short time to do its tasks
only invalidate data, not reload it (we can postpone that before it is rendered, if it even gets rendered at all)
do not synchronize on data (possibility of deadlocking)
know which channels it is interested in and only register to those

In SingleVM mode, the event bus is mainly a Map per OLATResourceable which implements a listener/sender (observer) pattern.
In cluster mode, the JMS provider is used to serialize those messages and send them asynchronously to all cluster nodes. The messages to its own JVM are sent „inhouse“, that is, using a direct method call (this is more efficient and allows for one node to still run if all other nodes and the JMS bus is down).
Description of the problem:
Enrollment can be configured to only accept a certain number of people in a group.
When two users subscribe to the same group at the same moment with only only place being left, only one user can be accepted, and the other one must be rejected.
Solution: the following operations need to be performed and need to be serialized.
read how many users are already in a group
if the maximum is not reached yet, insert the user into the group
The 2nd point happens by adding an entry in the relationship table between identities and groups.
A common read_committed isolation is not enough here, since the database does not offer a constraint such as „the number of entries in the n-n table must not exceed the number which is described in the group“
How it was solved in SingleVM OLAT: with synchronized(olatresourceablestring) {...}
How it is solved in cluster mode olat: the Syncer uses a select for update on a special olat database table.
What effectively happens on the database in cluster mode is the following: (SQL is in pseudocode)
Table 3.3.
| Thread 1 | Thread 2 |
|---|---|
| select for update from plock where asset=“businessgroup:12345“ | select for update where asset=“businessgroup:12345“ |
| select count(*) from group g, gr2ident g2i identity i where g2i.group = g and g2i.identity = i and g.id = xx | - assuming thread 1 acquired the pessimistic lock before thread 2, then thread 2 has zu wait till thread 1 either rollbacks or commits. |
| if (count < allowed max) -> insert new tupel into gr2ident table (=adding a user to this group), else send message to the user's screen. | |
| commit the transaction | thread 2 now gets the lock on the row with asset=“businessgroup:12345“ and continues with the select count(*)... as shown in thread 1 |
Next is a list of things to avoid in coding, please add more to it.
Using ehcache's classes directly: always use Cacher interface, (or otherwise improve it)
Calling doInSync from within doInSync - aka nested doInSync - this causes an Exception to be thrown in the current Syncer implementation
Using too many doInSyncs or using doInSyncs frequently with the same OlatResourcable. Note that doInSync can become a bottleneck if many users/requests want to synchronize on the same OlatResourcable. Therefore try to use an OlatResourcable which is as specific as possible (hence can be potentially used in parallel by as few as possible users/requests).
Doing expensive operations within a doInSync. Remember that within a doInSync you are holding a cluster-wide synchronization lock and that you prevent any other user/request from calling into the doInSync with the same OlatResourcable. Therefore time spent within doInSync is very precious and should not be wasted. Do as few things in doInSync as possible.
Using Cacher.put when you actually wanted to have other cluster nodes be informed - use update/updateMulti instead
The above is true the other way round as well: Try to use Cacher.put where possible - as it doesn't require other Caches to be informed.
Keeping a reference to an object retrieved from the Cacher in an instance variable. Fetch the object from the cache instead of storing it somewhere in a field
Synchronous event calls: by design the EventBus is asynchronous. This is a good thing as it eliminates a potential performance problem whereby a thread needs to wait for a reply to its request. Always try to stay on the asynchronous side when using EventBus and don't implement anything synchronous.
We describe a few tips, hints, and a kind-of-a-cookbook for the four tools explained in the previous chapter.
Think about caching in general before using the cacher
when to use it („prove“ a cache is needed (you can use a cache in code, but set maxelements to 0 if not needed)
calculate the size per element to estimate memory usage
the single element to cache should have a certain size (tara/netto, payload)
put versus update versus updateMulti:
be vary about methods offered: in cacher, there are three ways of storing something in the cache
put: This puts a key/value pair into the cache - without notifying any other cluster cache (if in cluster mode).
update: This puts a key/value pair into the cache - notifying any other cluster cache of the change (if in cluster mode).
updateMulti: Same as update but with multiple key/value pairs. This is speed optimized compared to calling update n times!
Cache configuration:
in the file olatcoreconfig.xml there is a bean named org.olat.core.util.cache.n.impl.cluster.ClusterCacher and in the single JVM mode the bean is in the same file, but named org.olat.core.util.cache.n.impl.svm.SingleVMCacher The property „rootConfig“ with its children config describes how the caches are configured. The sample below means that the NewCachePersistingAssessmentManager has a cache to create subcaches per course (entry with key „CourseModule“) and from this cache a cache per identity (entry with key „Identity“ in this course is created. How does this now look from the Java side? In the class NewCachePersistingAssessmentManager, there is one static field
private static CacheWrapper assessmentMainCache = CoordinatorManager.getCoordinator().getCacher().getOrCreateCache(NewCachePersistingAssessmentManager.class, null);
when an instance is created per course, then also a cache is created.
private NewCachePersistingAssessmentManager(ICourse course) {
this.course = course;
courseCache = assessmentMainCache.getOrCreateChildCacheWrapper(course);
}this cache per course („coursecache“) is now used not for caching, but only to create subcaches for each Identity in the course.
private CacheWrapper getCacheWrapperFor(Identity identity) {
OLATResourceable ores = OresHelper.createOLATResourceableInstanceWithoutCheck("Identity", identity.getKey());
CacheWrapper cw = courseCache.getOrCreateChildCacheWrapper(ores);
return cw;
}The sample configuration:
<bean class="org.olat.core.util.cache.n.impl.cluster.ClusterCacher" ...>
...
<property name="rootConfig">
<bean class="org.olat.core.util.cache.n.CacheConfig">
<property name="childrenConfig"><map>
<entry key="org.olat.course.assessment.NewCachePersistingAssessmentManager">
<bean class="org.olat.core.util.cache.n.CacheConfig">
<property name="timeToLive" value="1" />
<property name="timeToIdle" value="1" />
<property name="maxElementsInMemory" value="1" />
<property name="childrenConfig"><map>
<entry key="CourseModule">
<bean class="org.olat.core.util.cache.n.CacheConfig">
<property name="childrenConfig"><map>
<entry key="Identity">
<bean class="org.olat.core.util.cache.n.CacheConfig">
<property name="timeToLive" value="0" />
<property name="timeToIdle" value="60" />
<property name="maxElementsInMemory" value="1000" />
</bean>
</entry>
</map></property>
</bean>
...
If no configuration for a certain class is given, then, upon request of such a cache, the configuration of its parent is taken (which again may be inherited from its parents)
In previous OLATs, configurations from the cache were either coming from the file ehcache.xml (now deprecated) or were hardcoded in code.
Configuration parameters:
Table 3.4.
| parameter | Equivalent in Ehcache | Description |
|---|---|---|
| timeToLive | timeToLiveSeconds | From the ehcache docu: Sets the time to live for an element before it expires. i.e. The maximum time between creation time and when an element expires. Is only used if the element is not eternal. Optional attribute. A value of 0 means that and Element can live for infinity. The default value is 0. |
| timeToIdle | timeToIdleSeconds | From the ehcache docu: Sets the time to idle for an element before it expires. i.e. The maximum amount of time between accesses before an element expires. Is only used if the element is not eternal. Optional attribute. A value of 0 means that an Element can idle for infinity. The default value is 0. |
| maxElementsInMemory | maxElementsInMemory | From the ehcache docu: Sets the maximum number of objects that will be created in memory The Ehcache within the OLAT CacheWrapper is only used as a RAM-Cache and never written to disk (overflowToDisk=false, diskPersistent=false) |
The Ehcache within the OLAT CacheWrapper is only used as a RAM-Cache and never written to disk (overflowToDisk=false, diskPersistent=false)
Don't simply replace all „synchronized“ with
CoordinatorManager.getCoordinator().getSyncer().doInSync(...);
everywhere!
Only those which have synchronized resources which -would- affect the state of several JVM when in cluster mode.
Not those which synchronize on any GUI operation or similar, e.g. within usersession or such.
The Syncer will -always- be used when synchronizing within manager classes.
Try to make the synchronized code block as small and as fast as possible
make sure that a sync'ed block never takes longer than at most 10 seconds or such (special solutions are thus needed for gzipping large data directories), otherwise the database will throw a LockWaitTimeoutExceeded Exception.
For each synchronized keyword you introduce, explain that and why it does not have to be a doInSync. This can be done via
//o_clusterOK by:<person>
Make sure to use GUILocks where that would be a better fit than doInSync!
if possible, take an already existing and generic MultiUserEvent
a Multiuserevent must be serializable and should be small (network traffic) -> preferable only use base java types and list or sets of base types.
only send one multiuserevent within one call (at least from within one manager)
calls to the event bus should only happen from Manager classes (with register helper method incoming calls are then sent back to the controller)
You will find more information about the design details in the OLAT Javadoc. Use this link as an entry-point to the Javadoc : OLAT javadoc
In this chapter we discuss some of the framework elements that you can use to build your code. Specifically we describe how to use some of the GUI components and reusable controllers.
Note that since this documentation is work in progress, not all available compontens or controllers are described. Since this is a new section in the documentation it will take some time to describe the older components as well. Your help is apprechiated, please help us making the documenation complete by writing articles about components!
Whenever you have a little text fragment that you want to display on
screen you can use the
TextComponent
.
The following features are supported:
Display raw text (provide a string)
Display i18nified text (provide an i18n key and a translator. Translation happens at render time)
Wrapp in a DIV or SPAN element
Style by providing an optional CSS class name that is added to the wrapper DOM element
When the text (raw or i18n) and CSS class changed on an instantiated
object using the appropriate setter method the text element will be
redrawn on screen. This means that when you put a
TextComponent
in a
VelocityContainer
, you can change the displayed text without making the container dirty.
Only the text component itself will be redrawn. This has many
advantages when building a commplex GUI and you don't want to redraw
everything just because a litte text element has changed.
Usage examples can be found in the
GuiDemoLinksController
. To create a
TextComponent
object, use the
TextFactory
. There are two methods:
createTextComponentFromString
createTextComponentFromI18nKey
Here is a picture to give you a better idea of how version control and branching works in OLAT:

The OLAT source code is held in a CVS repository. The main development is done on the HEAD stream. For some extensive features, however, a feature branch can be created. It is merged back into HEAD after completion.
Feature branches must be named using the following scheme:
Application name: OLAT
Source branch: HEAD or name of a release branch, e.g. 6_1
Owner: UZH, FRENTIX, BPS... (your name or company here)
Jira Issue that describes the feature you implement: 1234. See the olat Jira server
Example: OLAT-HEAD-FRENTIX-3674, OLAT-HEAD-UZH-2385...
During the development phase, the branch is updated on a regular base from the source branch (normally HEAD). Do never merge something from a feature branch back to the HEAD unless your feature is finished. Only one back-merge is possible!
When updating your feature branch from HEAD, make sure you follow step-by-step this feature branch merge tutorial!
Moreover, there is a release branch that holds the former and current releases. The idea of a release branch is that after releasing the HEAD is opened again for unstable development activities. A release branch however should always be stable and is only used to do important bug fixing on the release.
Release branches must be named using the following scheme:
Application name: OLAT
Release base: 6_1_x
Indication that this is a branch: BRANCH
Example: OLAT-6_0_x-BRANCH, OLAT-6_1_x-BRANCH...
We do not make release branches for every single release. We maintain a release branch for each feature release. (6.0, 6.1 etc). The bugfix releases (6.0.1, 6.0.2 etc) are tags on those release branches.
As a developer one must take care of merging bugfixes back to the HEAD development. Each developer is responsible to do this. Best is to do it immediately after committing a fix to the release branch.
If you have any questions about the code or the usage of OLAT please contact us using one of our mailinglists. You will find them at http://lists.olat.org
Please follow the usual netiquette on mailing lists. The more detailed, polite and patient you are, the faster and better is the answer you will receive.
For personal questions you can contact us via info@olat.org. Please do not ask questions on this address that are of general interest, use the mailinglists for this instead.
For professional OLAT support, hosting, training (developers, administrators or authors) or customization please visit the homepage of frentix.
Frentix GmbH is a spin-off company from the University of Zurich that has great experience in developing and deploying the LMS OLAT.