In this section we cover the possibilities of customizing the user interface. ReportServer provides the following customization options:
Further customization options are available with the use of ReportServer scripts. More information on ReportServer scripts can be found in the Administration Guide.
Any visible text in ReportServer can, in principle, be displayed in any language. The languages available on log-in can be defined in the configuration file /fileserver/etc/main/localization.cf.
<default>de</default>
The "default" property specifies which language to use as default language.
<locales>en,fr,de</locales>
The "locales" property specifies a comma-separated list of available languages. If the property is not specified, all supported languages are available for selection.
Errors can occur due to various reasons.
Typical errors are:
An exception is thrown whenever an error occurs in ReportServer. The exception is composed of: a title, error message, and the stack trace.
In /fileserver/etc/main/templates.cf you can customize the error message that is displayed on errors that occur during the export of reports.
When customizing the error message you should give clear instructions as to what the affected employee should do in this case. Usually you would specify the contact address of an administrator or help desk. When customizing, the following substitutions are available: ${headline}, ${msg} and ${stacktrace}.
In ReportServer Enterprise Edition it is possible to customize the theme via the /fileserver/ui/theme.cf config file. Further information on this can be found in the administration guide.
In the configuration file /fileserver/etc/ui/previews.cf you can specify how to render PDF previews. The options are ${native} (to use the native browser capabilities), ${jsviewer} (to use a javascript library) or ${image} to not render a PDF at all, but to only render the first page as an image. Also note that users can overwrite the settings within their profiles.
The configuration file /fileserver/etc/ui/previews.cf allows you to configure the preview of the dynamic list. The complete default dynamicList section is shown below:
<dynamicList>
<defaultColumnWidth>200</defaultColumnWidth>
<maxColumnWidth>800</maxColumnWidth>
<pageSize>
<configs>
<config minCols = "0" maxCols = "99">50</config>
<config minCols = "100" maxCols = "249">25</config>
<config minCols = "250" maxCols = "499">10</config>
<config minCols = "500" maxCols = "MAX">5</config>
</configs>
</pageSize>
</dynamicList>
The defaultColumnWidth setting allows you to configure the global default width of the dynamic list columns. Note that the width of a column may be configured individually in the variant configuration screen. If no width is configured there, the default global width is used. The maxColumnWidth allows you to configure the maximum column width.
The number of rows of the dynamic list preview can be configured in the pageSize section. The number of rows is dependent of the number of columns selected. In the example above, the preview will have:
Note that you can use the MAX keyword for denoting the maximum number of columns.
In case you don't want the number of rows to be dependent on the number of columns, you can configure it as follows:
<pageSize>
<configs>
<config minCols = "0" maxCols = "MAX">50</<config>
</configs>
</pageSize>
Using the configuration file /fileserver/etc/ui/urlview.cf you can define context aware tabs to be displayed in the TeamSpace or in the administration module (e.g., report management, user management, etc.). This allows you to, for example, display the documentation report directly whenever a user selects a report in the TeamSpace.
The configuration of extra tabs is split into three parts:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<adminviews>
</adminviews>
<objectinfo>
</objectinfo>
<module>
</module>
</configuration>
Tabs to be displayed in the administration module go into the
tags. Tabs for the TeamSpace are put within the tags. Tabs for ReportServer modules are put within the tags. The default configuration does not add any additional tabs for the admin interface or to the modules, but adds several tabs to the TeamSpace:<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<adminviews>
</adminviews>
<objectinfo>
<view>
<types>net.datenwerke.rs.tsreportarea.client.tsreportarea.dto.TsDiskReportReferenceDto</types>
<name>${msgs['net.datenwerke.rs.core.service.urlview.locale.UrlViewMessages']['info']}</name>
<url>rs:reportdoc://${reportId}/${id}</url>
</view>
<view>
<types>net.datenwerke.rs.tsreportarea.client.tsreportarea.dto.TsDiskReportReferenceDto</types>
<name>${msgs['net.datenwerke.rs.core.service.urlview.locale.UrlViewMessages']['history']}</name>
<url>rs:revisions://${reportId}</url>
</view>
<view>
<types>net.datenwerke.rs.tsreportarea.client.tsreportarea.dto.TsDiskReportReferenceDto</types>
<name>${msgs['net.datenwerke.rs.core.service.urlview.locale.UrlViewMessages']['preview']}</name>
<url>rs:reportpreview://${reportId}</url>
</view>
</objectinfo>
<module>
</module>
</configuration>
Each <view> tag adds a new tab. The <types> tag allows to define for which types of objects the tab is displayed and <name> provides a name (in the above example, the name is localized, but you could also simply write <name>SomeName</name>. Finally, the <url> tag takes a URL that is to be displayed. In the above example we have three custom ReportServer URLs that access custom functionality, the first accesses a documentation report, the second a revisions report and the last one a preview of the report. Via the replacement ${reportId} the id of the object is added to the URL.
In the following we go through the process of adding new tabs step by step.
To add a new tab, you define a <view> tag. For adding a tab to the TeamSpace whenever a report is selected add the following <view> tag within the <objectinfo> section.
<view>
<types>
net.datenwerke.rs.tsreportarea.client.tsreportarea.dto.TsDiskReportReferenceDto
</types>
<name>Some Additional Information</name>
<url>
reportserver/reportexport?key=someKey&format=html&p_reportId=${reportId}
</url>
</view>
The above will execute the report with key "someKey" and pass the given report id as parameter.
The following types are available in TeamSpaces.
All Objects in a TeamSpace:
net.datenwerke.rs.tsreportarea.client.tsreportarea.dto.AbstractTsDiskNodeDto
All folders in a TeamSpace:
net.datenwerke.rs.tsreportarea.client.tsreportarea.dto.TsDiskFolderDto
All reports and variants in a TeamSpace:
net.datenwerke.rs.tsreportarea.client.tsreportarea.dto.TsDiskReportReferenceDto
All exported reports which were, for example, created by the scheduler:
net.datenwerke.rs.scheduleasfile.client.scheduleasfile.dto.ExecutedReportFileReferenceDto
The name field defines the tab's name. The url is the address that is displayed. This also allows you to access external addresses that are then displayed within the tab.
If you need to restrict the tab to some users, groups or OUs, you can achieve this with help of the restrictTo tag. For example, if you want to restrict the tab shown above to users with ''demoadmin'' and ''demoadmin2'' usernames, you can enter these usernames separated by commas as shown below:
<view>
<types>
net.datenwerke.rs.tsreportarea.client.tsreportarea.dto.TsDiskReportReferenceDto
</types>
<name>Some Additional Information</name>
<url>
reportserver/reportexport?key=someKey&format=html&p_reportId=${reportId}
</url>
<restrictTo>
<users>demoadmin1,demoadmin2</users>
</restrictTo>
</view>
If you need to restrict the tab shown above to groups with ids ''1'' and ''2'', you can enter them separated by commas as shown below:
<view>
<types>
net.datenwerke.rs.tsreportarea.client.tsreportarea.dto.TsDiskReportReferenceDto
</types>
<name>Some Additional Information</name>
<url>
reportserver/reportexport?key=someKey&format=html&p_reportId=${reportId}
</url>
<restrictTo>
<groups>1,2</groups>
</restrictTo>
</view>
The sample above applies also to organizational units. For restricting the tab to organizational units with ids ''3'' and ''4'', you can enter them separated by commas as shown below:
<view>
<types>
net.datenwerke.rs.tsreportarea.client.tsreportarea.dto.TsDiskReportReferenceDto
</types>
<name>Some Additional Information</name>
<url>
reportserver/reportexport?key=someKey&format=html&p_reportId=${reportId}
</url>
<restrictTo>
<ous>3,4</ous>
</restrictTo>
</view>
Note that restrictTo needs comma-separated usernames for users and comma-separated ids for groups and organizational units.
Combining restrictions is also possible. The following configuration restricts the tab to ''demoadmin'' and ''demoadmin2'' users or ''1'' and ''2'' groups. This means, the uses ''demoadmin'', ''demoadmin2'', or any user in the ''1'' and ''2'' groups can access the tab.
<view>
<types>
net.datenwerke.rs.tsreportarea.client.tsreportarea.dto.TsDiskReportReferenceDto
</types>
<name>Some Additional Information</name>
<url>
reportserver/reportexport?key=someKey&format=html&p_reportId=${reportId}
</url>
<restrictTo>
<users>demoadmin1,demoadmin2</users>
<groups>1,2</groups>
</restrictTo>
</view>
For the report documentation you need to use the special ReportServer URL:
rs:reportdoc://${reportId}/${id}
As you can see there are two placeholders in the above url: reportId and id. The following replacements are available
id | the object's id |
type | the object's type |
username | the current user's username |
Note that TeamSpaces do not only contain report references. Thus, the replacement ${id} will contain the id of the reference rather than the id of the referenced report. For this, there is the special replacement called ${reportId} which is only available for report references.
Similarly, to the report documentation in the TeamSpace you can display additional information on any selected object in the administration module. In the administration module you can add tabs to objects in the report management, user management, dadget management, datasource management and fileserver modules. These are configured within the <adminviews> tag.
The tables in the following subsections describe which types can be used. Note that the type must be used together with the corresponding prefix.
Prefix net.datenwerke.rs.core.client.reportmanager.dto.reports.
Type | Description |
AbstractReportManagerNodeDto | All objects in the report management tree |
ReportDto | reports |
ReportFolderDto | folders |
Prefix net.datenwerke.security.client.usermanager.dto.
Type | Description |
AbstractUserManagerNodeDto | All objects in the user management tree |
UserDto | users |
GroupDto | groups |
OrganisationalUnitDto | organisational units (folders) |
Prefix net.datenwerke.rs.fileserver.client.fileserver.dto.
Type | Description |
AbstractFileServerNodeDto | All objects in the file server |
FileServerFolderDto | folders |
FileServerFileDto | files |
Prefix net.datenwerke.rs.core.client.datasourcemanager.dto.
Type | Description |
AbstractDatasourceManagerNodeDto | all objects in datasource management |
DatasourceFolderDto | folders |
DatasourceDefinitionDto | datasources |
Prefix net.datenwerke.rs.dashboard.client.dashboard.dto.
Type | Description |
AbstractDashboardManagerNodeDto | All objects in the dashboard tree |
DashboardNodeDto | dashboards |
DashboardFolderDto | folders |
The following example would display a tab http://www.mycompany.com/employee.
which displays the website at Url <adminviews>
<view>
<types>net.datenwerke.security.client.usermanager.dto.UserDto</types>
<name>User Information</name>
<url>http://www.mycompany.com/employee=${id}</url>
</view>
</adminviews>
As explained in Section 4.9.6., you can define context-aware tabs to be displayed in the TeamSpace or in the administration module.
In addition, you can introduce custom modules to ReportServer, which will be visible in the module tab section at the top of ReportServer. Essentially, the new module will appear after the ''Administration'' module.
Here's an example configuration for adding new modules:
<module>
<view>
<name>Section A</name>
<url>http://SERVER:PORT/reportserverbasedir/reportserver/scriptAccess?id=15</url>
</view>
<view>
<name>Section B</name>
<url>http://SERVER:PORT/reportserverbasedir/reportserver/scriptAccess?id=16</url>
</view>
</module>
In this example, two new modules, Section A'' and Section B'', are defined. These modules link to scripts in ReportServer (with id 15 and 16, respectively). While you can specify any URL within the configuration, linking to ReportServer scripts generally provide a higher level of customization.
The restrictTo discussed in Section 4.9.6.1. also applies here. The following restricts the ''Section A'' tab to groups with ''1'' and ''2'' ids:
<module>
<view>
<name>Section A</name>
<url>http://SERVER:PORT/reportserverbasedir/reportserver/scriptAccess?id=15</url>
<restrictTo>
<groups>1,2</groups>
</restrictTo>
</view>
<view>
<name>Section B</name>
<url>http://SERVER:PORT/reportserverbasedir/reportserver/scriptAccess?id=16</url>
</view>
</module>
Following is a quick guide for those who want to completely exchange the ReportServer login page by a custom looking page. In the following we assume that the FileServer contains a folder /resources/public and that the public folder is marked as ''web accessible''. In case not, you can either mark it with the ''web accessible'' checkbox in the UI, or use the following commands:
cd /fileserver/resources
dirmod webaccess public true
As a first step we are going to create a very simple login page, something along the following lines:
<html>
<head>
<title>Custom Login</title>
</head>
<body>
My custom login page
<form method="post" action="">
<label for="user">username:</label>
<input type="text" name="user" /> <br/>
<label for="pw">password:</label>
<input type="password" name="pw" /></br>
<input type="submit"/>
</form>
</body>
</html>
The page consists of a single form with a username and password field. We store the above as login.html in the folder /resources/public. For this, for example open the terminal (CTRL+ALT+T) and go for
cd /fileserver/resources/public
createTextFile login.html
As the public folder is accessible for anybody (even if the user is not logged in) we can access it via the fileServerAccess servlet. That is, if your ReportServer is accessible via the url reporting.mycompany.com/ then you should see the login page if you go to http://reporting.mycompany.com/reportserver/fileServerAccess?path=/resources/public/login.html.
Now what was missing in the above login page was the intended target of the form. For this we will create a publicly accessible script that handles the form data. As scripts always go into the bin folder we create a folder public beneath the bin folder and add a script customauth.groovy.
cd /fileserver/bin
mkdir public
cd public
createTextFile customauth.groovy
dirmod webaccess public true
To see that everything worked, we use the following simple script which simply outputs the parameter user:
def user = httpRequest.getParameter('user')
return user
Note that you have access to the request and response via the httpRequest and httpResponse variables. What is left is to change the action attribute of the login.html page to point to the script, that is, we need to change it to http://reporting.mycompany.com/reportserver/scriptAccess?path=/bin/public/customauth.groovy.
<html>
<head>
<title>Custom Login</title>
</head>
<body>
My custom login page
<form method="post" action="http://reporting.mycompany.com/ReportServer/reportserver/scriptAccess?path=/bin/public/customauth.groovy">
<label for="user">username:</label>
<input type="text" name="user" /> <br/>
<label for="pw">password:</label>
<input type="password" name="pw" /></br>
<input type="submit"/>
</form>
</body>
</html>
What we have so far is that we have a custom login page and a script which is the target. What we need is that the script can actually perform the login operation if the provided credentials match. For this we will use the AuthenticatorService located in
net.datenwerke.security.service.authenticator.AuthenticatorService
In the following we check for a username root and a password 123. If found we perform a login for user with id 6 (which in my case is the root user).
import net.datenwerke.security.service.authenticator.AuthenticatorService
def service = GLOBALS.getInstance(AuthenticatorService).get()
def user = httpRequest.getParameter('user')
def pw = httpRequest.getParameter('pw')
if( 'root' == user && '123' == pw ){
service.setAuthenticated(3) // the id of the root user
httpResponse.sendRedirect('http://reporting.mycompany.com/ReportServer/ReportServer.html');
return null
} else {
return 'Could not authenticate'
}
If all went well you can now logoff and log in via the custom page http://reporting.mycompany.com/reportserver/fileServerAccess?path=/resources/public/login.html.
In the above example we had a custom script to handle the authentication of a single user. This of course does not scale well and for any real scenario one would like to authenticate against a user database. In the following we show how to authenticate against ReportServer's own user database. For this we can again use the AuthenticatorService which offers a method authenticate that triggers the built-in authentication mechanisms.
ReportServer's built-in authentication is structured in so called pluggable authentication modules (short PAM) which perform the actual authentication. The active PAMs are configured in the reportserver.properties configuration file and usually only a single PAM is active:
rs.authenticator.pams = net.datenwerke.rs.authenticator.service.pam.UserPasswordPAMAuthoritative
The above config loads the UserPasswordPAM module in authoritative mode (more information on the PAMs can be found in the configuration guide). This PAM expects a username and password and then sets of to authenticate against the ReportServer user database. The authoritative flag means that if the UserPasswordPAM cannot authenticate a user that it will then trigger an abort. This can become necessary when you would like to combine multiple different PAMs.
As explained, the AuthenticatorService's authenticate method triggers the internal authentication process. That is, it expects an array of so called AuthTokens (which can be basically anything) and then hands these to the registered PAMs. The PAMs are then asked in turn whether or not they can authenticate a user. For this they use the AuthToken array. The UserPasswordPAM thus expects a username and password as an AuthToken. This is encapsulated in the UserPasswordAuthToken which is located in
net.datenwerke.rs.authenticator.client.login.dto.UserPasswordAuthToken
The authenticate method returns an AuthenticationResult which can be asked whether the authentication succeeded (isAllowed()). In the following script we combine our earlier example with an authentication against ReportServer's user database.
import net.datenwerke.security.service.authenticator.AuthenticatorService
import net.datenwerke.security.client.login.AuthToken
import net.datenwerke.rs.authenticator.client.login.dto.UserPasswordAuthToken
def service = GLOBALS.getInstance(AuthenticatorService)
def user = httpRequest.getParameter('user')
def pw = httpRequest.getParameter('pw')
/* construct authentication tokens */
def token = new UserPasswordAuthToken()
token.username = user
token.password = pw
def result = service.authenticate([token] as AuthToken[])
if(result.isAllowed()){
httpResponse.sendRedirect('http://reporting.mycompany.com/ReportServer/ReportServer.html')
return null
}
return 'Could not authenticate'
Now your custom login page should be fully functional. The logout part, explained next, should be customized as well.
Currently when a user logs out, the user will be taken back to the original ReportServer login page. In order to change that we need to change the config file located in etc/security/misc.cf within the ReportServer filesystem. If we add
<logout>
<url>http://reporting.mycompany.com/reportserver/fileServerAccess?path=/resources/public/login.html</url>
</logout>
to the config, then on logout ReportServer will redirect the user to http://reporting.mycompany.com/reportserver/fileServerAccess?path=/resources/public/login.html.
The complete example can found here: https://github.com/infofabrik/reportserver-samples/tree/main/src/net/datenwerke/rs/samples/admin/login/simple.
Based on the previous examples, you can used advanced techniques, together with e.g. JQuery: https://jquery.com/ to further customize your login page. An example of this can be found here: https://github.com/infofabrik/reportserver-samples/tree/main/src/net/datenwerke/rs/samples/admin/login/jquery.