public class MySamplePanel extends BasePanel {
public MySamplePanel(String id) {
super(id);
add(Attribute.appender("class", "my-sample"));
}
}
GUI Development Guide
MidPoint GUI is based on Apache Wicket web framework. The styles (CSS) and page templates are based on Bootstrap framework and AdminLTE template.
The good starting point is to look around Wicket documentation and tutorials. There are plenty of them. Just follow the links from the Wicket home page.
MidPoint GUI Extensibility
MidPoint GUI was originally built in quite a monolithic fashion. However, recently we have been refactoring and cleaning up the GUI code and also introducing mechanisms for GUI extensibility. Currently there are several ways how to extend the GUI:
-
Customize GUI styles, e.g. color scheme, fonts and so on. (SCSS, see Look & Feel Customization HOWTO)
-
Create custom launchers for "home" page in midPoint GUI (declarative, see Admin GUI Configuration Type)
-
Create custom menu links (declarative, see Admin GUI Configuration Type)
-
Create custom tabs in the object details forms. These are created as Wicket panels (Java development)
In the future we plan the following mechanisms:
-
Create custom GUI pages (Java development)
-
Use custom GUI forms in existing pages (declarative)
MidPoint GUI API
MidPoint GUI is currently contained in a single component admin-gui
. Some parts of the classes in this component are meant to be used as an API by third-party developers and for the purposes of GUI customization.
These classes will be maintained in backward-compatible way.
Other classes are considered private implementation and we make no guarantees about their stability.
The drawback is that now there is no clear way how to distinguish API classes and implementation classes in the GUI.
Other components have a very clear distinction, but as the GUI code was not originally designed for this kind of reuse there is no such distinction in the GUI component.
However, we are working on this.
There is a new package that contains GUI API classes:
com.evolveum.midpoint.gui.api
If you use classes and interfaces in this package you should be safe. These classes will only change in backward-compatible manner. If there is something that we would like to remove it will be marked as deprecated several midPoint releases before it is actually removed. The things in this package are considered to be stable. Use them.
The things in com.evolveum.midpoint.gui.impl
are considered to be private implementation.
Do not use these.
Any class or interface found in this package is considered private and it is subject to change any time without any warning.
It you depend on the classes in this package you do it at your own risk.
It is likely that midPoint upgrades will fail in a spectacular way and your customizations will not survive.
You have been warned.
If there is any class or interface in this package that you think may be useful as reusable component please contact the midPoint team.
We will consider moving it to the API.
The classes in com.evolveum.midpoint.web
are unsorted.
Some of these are API, some of these are implementation.
There is no reliable way to tell them apart and this is currently a gray zone.
These classes will eventually move either to API or impl (or disappear entirely).
Therefore if you depend on them it is sure that you will have to change your code eventually.
If you are lucky, you will only have to update package name.
If you are not lucky you will have to rewrite the code.
Anyway, if you think about using some components in this package it might be a good idea to contact us and discuss it.
We might move the class to the API immediately and save you a lot of time in the future.
Base Page and Utility Classes
All midPoint pages share the same things: header, footer, navigation bar, title, stylesheets, ability to display menu, etc. All of these traits are implemented in a common superclass:
com.evolveum.midpoint.gui.api.page.PageBase
All self-respecting midPoint pages should extend this class. This class also contains a lot of utility methods that are available to all subclasses.
There are several general-purpose utility classes and interfaces in package:
com.evolveum.midpoint.gui.api.util
Two most prominent classes are:
-
WebComponentUtil
: contains utility methods that are often used in Wicket components (panels, forms, …) -
WebModelServiceUtil
: contains utility methods that interact with midPoint IDM Model Interface (ModelService
,ModelInteractionService
) and other midPoint components.
MidPoint GUI Components
Apache Wicket is a component-based framework so naturally midPoint GUI has a lots and lots of components. Some of these components are a special-purpose things meant to do just one job. But most components are meant to be used on many places in the user interface. These reusable components should be part of the GUI API. But currently most of them are still in the gray zone. But we are continually sorting that out. To make the reuse easier, we are keeping a list of components that are already ready for reuse:
The GUI is already several years old and it has been through several redesigns and refactoring. That are some things that we are not entirely proud of but which cannot be completely removed right now. These are marked as deprecated in Java. But there are also associated CSS classes and JS scriptlets and other artifacts. Therefore the following list contains things that should not be used anymore:
How To Make a Reusable Component
-
Read this guide. Then read it again.
-
Make sure you are correctly handling the model, extend BasePanel (if appropriate), etc.
-
Make sure that the component is using only the classes from
com.evolveum.midpoint.gui.api
package. It must not use classes fromcom.evolveum.midpoint.gui.impl
package. If it uses classes from the oldcom.evolveum.midpoint.web
package consider cleaning them up and moving them tocom.evolveum.midpoint.gui.api
package. -
Make sure the component is using styles (CSS classes) that are either pure Bootstrap/AdminLTE or those that are in midpoint-theme.less (and they have appropriate comment).
-
Describe the purpose of the component in component class javadoc. Two or three sentences is enough. Describe the purpose, not the implementation. I.e. describe what the component is supposed to do. You may also describe where do you think the component might be useful (why do you think it will be reused).
-
Place the component in
com.evolveum.midpoint.gui.api.component
package. Each component java class and associated HTML file should be placed in its own sub-package. E.g.com.evolveum.midpoint.gui.api.component.foo.FooPanel.
-
List the component in List of Reusable GUI Components
Component Structure
Most components in midPoint GUI are panels (sublcasses of org.apache.wicket.markup.html.panel.Panel
class).
The basic characteristics are:
-
The components usually extends
com.evolveum.midpoint.gui.api.component.BasePanel
class. -
Component constructor takes
id
andmodel
as parameters. These are passed to superclass constructor. -
The component invokes (private) method
initLayout()
that creates the structure of the components (adds sub-components). -
WARNING: Do not invoke
model.getObject()
in constructor orinitLayout()
method. This may cause premature loading of the model (see below). Also, these methods should not invokegetPage()
method, because the component is just being constructed and it is not part of the component tree yet. -
TODO: Passing PageBase to constructor or rather passing the segregated interfaces.
Wicket Models
Model (IModel
implementation) is one of the fundamental concepts of the Wicket framework.
Models hold the information processes by the components.
Understanding the models can be a bit tricky, therefore please pay attention to this concept when reading Wicket documentation.
The use of models in midPoint GUI is usually quite explicit (they are explicitly passed as parameters of component constructors).
This makes it a bit easier to understand which model is used at which place.
MidPoint GUI often works with objects that are expensive to load.
Loading a user object from repository might be relatively cheap, but even that we do not want to do unless really necessary.
Loading user photo is more expensive.
And loading resource objects such as accounts and entitlement associations is very expensive.
We want to avoid that if possible.
Therefore there is a com.evolveum.midpoint.gui.api.model.LoadableModel
class.
This class in an implementation of Wicket IModel
interface that implements lazy loading.
Use this class as model when dealing with objects that are expensive to load.
Which is basically any midPoint object (PrismObject
) that needs to be retrieved from repository or from the resource.
Just implement the load()
method.
That’s it.
The important thing to keep in mind is that Wicket is processing component in several phases (lifecycle stages).
Especially interesting is the phase when component constructor is called, because that’s the point where the component layout (sub-components) is constructed.
The model object should already exist in this phase.
In midPoint GUI the model object is usually passed as an component constructor parameter and stored in the field of BasePanel
class (see above).
Then the initLayout() method is called.
The model is already present there.
But the model may be empty (not yet loaded).
Loading the model is often expensive operation.
We do not want to load the model unless it is necessary.
E.g. we would like to load a model only if an expandable component is expanded, so can usually avoid loading the model entirely for the component that are not visible.
If you need to do something with the model value in the subcomponent, do it indirectly through sub-component model and trigger loading only when subcomponent model is used.
Object Wrappers
TODO: object wrappers
Styles and Javascript
All SCSS styles and javascript files are processed using NodeJS/Webpack 5.
Styles and javascript dependencies are defined in admin-gui/package.json
, webpack configuration is along in webpack.*.js
.
Currently, there are two webpack profiles:
-
Production -
webpack.production.js
-
Development -
webpack.development.js
Both of them use common configuration defined in webpack.common.js and these configuration files are consumed using webpack.config.js
.
Last mentioned file decides which profile to use by checking environment variable.
We also defined two main and one helper script in package.json
- build, watch and postinstall.
Results of webpack build can be found in admin-gui/target/generated-resources/webpack/static
.
postinstall
npm script is used to execute our patch-package procedure.
This allows us to patch npm packages that were installed previously to admin-gui/node-modules
.
Currently, we have to patch:
-
admin-lte
-
bootstrap-multiselect
packages because they contain some bugs and authors didn’t publish bugfixed versions yet.
Patch files were generated using steps described in patch-package npm description.
Patch files are stored in admin-gui/patches
.
During maven build npm run build
is executed using maven-frontend-plugin
, which starts webpack build for production
environment.
Production build created minified css/javascript files.
During development one can use npm run watch
from executed from admin-gui
folder.
Webpack will then watch for changes done in scss/javascript resources.
This can be used together with application.yml
option spring.resources.chain.cache=false
for hot reload/recompile during development.
Style and javascript resources are located in admin-gui/src/frontend
.
Our style and javascript resources are divided via webpack configuration into two groups - vendors
and midpoint
.
vendors
group contain all resources that we use as libraries, mostly defined through npm dependencies, see:
-
admin-gui/src/frontend/js/vendors.js
-
admin-gui/src/frontend/scss/vendors.scss
midpoint
group contain all resources that were created for midPoint, see:
-
admin-gui/src/frontend/js/midpoint.js
-
admin-gui/src/frontend/scss/midpoint.scss
This means webpack will generate two main sets of resources (midpoint and vendors).
List of css/js resources that are linked from midPoint web pages:
File | Description |
---|---|
css/font-evosome.css |
Custom icon font created by Evolveum for midpoint. |
static/vendors.css |
CSS file that contain all 3rd party libraries css styles like bootstrap, adminlte, plugins, etc. Build by webpack, minified. |
static/midpoint.css |
CSS file with all midpoint related custom CSS compiled from scss. Build by webpack, minified. |
css/overlay.css |
Intentionally empty. Can be used in maven overlay project to simply extend CSS styles. |
static/vendors.js |
Javascript file that contains all 3rd party libraries like bootstrap, adminlte, plugins, etc. Build by webpack, minified. |
static/midpoint.js |
Javascript with all midpoint relatd custom JS code. Build by webpack, minified. |
js/overlay.js |
Intentionally empty. Can be used in maven overlay project to simply extend JS scripts with custom code. |
jquery.js |
JQuery javascript library is linked via apache wicket. |
Styles
MidPoint CSS style system is based on Bootstrap framework and AdminLTE template. The styles are processed by using SCSS CSS pre-processor.
The midpoint uses standard Bootstrap 4.6 and AdminLTE 3.2 CSS classes whenever possible. If a component needs a custom class, the SCSS code for that class should be places in the file:
File | Description |
---|---|
|
Used to override rules defined in admin-lte theme |
|
Used for variable overrides for admin-lte theme |
|
Used for variable overrides for bootstrap |
|
Defined variables specific for midpoint, includes variables/mixins/utils from admin-lte and bootstrap |
|
Main stylesheet for our custom styles |
|
Utility rules should be defined here |
|
Main stylesheet for all libraries |
|
Rule overrides for wicket framework |
Custom rules should be used only when necessary - to avoid large copy&paste or create very custom designs. In all other cases please use bootstrap utilities (mainly).
If custom rules are to be created for our wicket components, they should be defined in src/frontend/scss/midpoint.scss
.
For each component main class should be created and named using kebab-case. For example:
and corresponding styles in midpoint.scss
:
.my-sample-panel {
// whatever you need to define using scss syntax
}
Don’t forget to check dark-mode as well. Avoid copy&paste of SCSS or HTML.
Each definition should be commented. The comment should at least mention which component uses that definition.
Javascripts
File | Description |
---|---|
Folders |
Few folders contain javascript packages that weren’t published in NPM registry. We should consider them deprecated and move to existing NPM packages for better maintainability. |
|
ACE editor wrapper made to integrate ACE with wicket. |
|
Root javascript file that import all our javascript code. |
|
Our main javascript file, all functions should go here.
If necessary, for long and complex javascripts new file can be created and imported to |
|
Javascript related to password validation and complexity checks. Contains imports from 3rd party libraries + our customization. |
|
File containing imports from third party libraries. |
Error Handling
TODO:
Look&Feel and UX Recommendations
Use of Dates and Times
The goal is to use the same date formats in all parts of the GUI. There are several variants of the date formats, shorter and longer. The guidelines for their use are:
TODO: Kate
Misc Recommendation
-
Properly use generics. Using
IModel
is bad. UsingIModel<String>
is good. This makes the code more readable, especially in places likeList<IModel<ObjectWrapper<OrgType>>>
(as opposed to justList<IModel>
which does not really tells anything). Generics might sometimes be painful and sometimes you have to fight them to get what you want. But the benefits are huge. Learn to use generics properly. -
TODO: serialization and serial version ID