Custom content
Add custom content to your Kimai installation
A Kimai plugin which allows adding custom content for:
- Stylesheet (embedded in all pages)
- Javascript (embedded in all pages, except security screens)
- A global warning message, shown to every logged-in user
- An entire new page to display (markdown formatted) information for your users
News page
You can edit two fields:
- The news title is the name of the menu entry
- The content for the page (markdown is supported for formatting)
Deactivate the news: if the title is empty, the menu will be hidden.
Alert
That's how the **alert / warning message** looks like. You can even include _markdown_ and [links](/en/custom-content-news) !
Javascript
Make your tables horizontal scrollable if they are too wide:
function scrollableTable() {
[].slice.call(document.querySelectorAll('div.dataTables_wrapper')).map((element) => {
element.style.overflowX = 'scroll';
});
}
document.addEventListener('kimai.initialized', scrollableTable);
document.addEventListener('kimai.reloadedContent', scrollableTable);
Remove the activity from the timesheet form.
You have to create one global activity and grab the ID from the URL, replace 5095
in the code with your new ID:
document.addEventListener('show.bs.modal', (e) => {
const activity = e.srcElement.querySelector('#timesheet_edit_form_activity');
if (activity !== null) {
activity.value = '5095';
activity.dispatchEvent(new Event('change'));
const activityRow = e.srcElement.querySelector('.timesheet_edit_form_row_activity');
if (activityRow !== null && !activityRow.classList.contains('d-none')) {
activityRow.classList.add('d-none');
}
}
});
Make the header fixed at the screen top and remove that stickyness from the table headers (both are not possible right now):
function stickyHeader() {
[].slice.call(document.querySelectorAll('thead.sticky-top')).map((element) => {
element.classList.toggle('sticky-top');
});
const header = document.querySelector('div.page header.navbar');
if (!header.classList.contains('sticky-top')) {
header.classList.toggle('sticky-top');
}
}
document.addEventListener('kimai.initialized', stickyHeader);
document.addEventListener('kimai.reloadedContent', stickyHeader);
Hide the dashboard edit button (for all non super-admins only):
document.addEventListener('kimai.initialized', function (event) {
if (event.detail.kimai.getUser().isSuperAdmin() && document.querySelector('section.dashboard') !== null) {
document.querySelector('.page-header').style.setProperty("display", "none", "important");
}
});
React on global events and utilize the Kimai Javascript API:
document.addEventListener('kimai.initialized', function(event) {
alert(event.detail.kimai.getTranslation().get('confirm'));
});
Select an Activity e.g. to simulate a global default activity. The ID 6451
is the Activity ID to be selected (should be global).
document.addEventListener('show.bs.modal', (e) => {
const activity = e.srcElement.querySelector('#timesheet_edit_form_activity');
if (activity !== null) {
activity.value = '6451';
activity.dispatchEvent(new Event('change'));
}
});
Set the “activity description” upon selection as “timesheet description”:
document.addEventListener('show.bs.modal', (e) => {
const desc = e.srcElement.querySelector('#timesheet_edit_form_description');
if (desc !== null) {
e.srcElement.querySelector('#timesheet_edit_form_activity').addEventListener('change', (e) => {
kimai.getPlugin('api').get('/api/activities/' + e.target.value, {}, function(data) {
desc.value = data.comment;
});
});
}
});
Make the timesheet description field mandatory:
document.addEventListener('show.bs.modal', (e) => {
const desc = e.srcElement.querySelector('#timesheet_edit_form_description');
if (desc !== null) {
desc.required = true;
const label = e.srcElement.querySelector('label[for=timesheet_edit_form_description]');
if (label !== null && !label.classList.contains('required')) { label.classList.add('required'); }
}
});
Always deactivate the export checkbox:
document.addEventListener('kimai.initialized', function(event) {
const ec = document.getElementById('markAsExportedCheck');
if (ec !== null && ec.checked) { ec.checked = false; document.getElementById('markAsExported').value = 0; }
});
Deactivate a certain field by ID in a modal if the current user is not an Admin or SuperAdmin:
function deactivateField(source, selector) {
const field = source.querySelector(selector);
if (field !== null) { field.disabled = true; if (field.tomselect) { field.tomselect.disable(); } }
}
document.addEventListener('kimai.initialized', function(event) {
const kimai = event.detail.kimai;
if (!kimai.getUser().isAdmin() && !kimai.getUser().isSuperAdmin()) {
document.addEventListener('show.bs.modal', (e) => { deactivateField(e.srcElement, '#expense_form_metaFields_status_value'); });
deactivateField(document, '#expense_form_metaFields_status_value');
}
});
Automatically login with SAML:
document.querySelector('body.login-page #social-login-button')?.click();
Always expand extended timesheet settings:
document.addEventListener('show.bs.modal', (e) => {
e.srcElement.querySelector('#timesheet_extended_settings button[data-bs-toggle]')?.click();
});
Always set a static time in the timesheet screen:
document.addEventListener('show.bs.modal', (e) => {
const time = e.srcElement.querySelector('#timesheet_edit_form_begin_time');
if (time !== null) { time.value = '01:00'; }
});
Set a custom browser title:
document.title = 'My fancy company';
Add IDs to all <h3> header of your custom news page (allowing to use jump anchor links):
document.addEventListener('kimai.initialized', function (event) {
if (document.getElementById('custom-content-news') !== null) {
const headings = document.querySelectorAll('#custom-content-news .markdown h3');
let counter = 0;
for (const element of headings) {
element.id = 'news' + (++counter);
}
}
});
Make the tags field mandatory:
document.addEventListener('show.bs.modal', (e) => {
const field = e.srcElement.querySelector('#timesheet_edit_form_tags');
if (field !== null) {
field.required = true;
const label = e.srcElement.querySelector('label[for=timesheet_edit_form_tags-ts-control]');
if (label !== null && !label.classList.contains('required')) { label.classList.add('required'); }
}
});
CSS
Hiding a menu:
ul.sidebar-menu li#calendar { display:none; }
Hiding the colored dots:
i.dot, span.dot {display:none !important;}
Activating horizontal scrolling on data-tables:
.box .dataTables_wrapper {
overflow-x: auto;
min-height: .01%;
}
.box .dataTables_wrapper > .row {
margin-left: 0;
margin-right: 0;
}
.box .dataTables_wrapper > .row > .col-sm-12 {
padding-left: 0;
padding-right: 0;
}
Deactivate the background blur for modals, which might be problematic in RDP sessions:
.modal-blur {
-webkit-backdrop-filter: blur(0px) !important;
backdrop-filter: blur(0px) !important;
}
Switching the order of save and cancel buttons:
.modal-footer button[type=submit], .box-footer input[type=submit] {
float: right !important
}
.modal-footer .btn-cancel, .box-footer input[type=reset] {
float: left !important
}
Remove the red dotted lines between overlapping timesheet entries:
table.dataTable tr.overlapping {
border-top: none;
}
Highlight active timesheet records:
tr.recording {
background-color: #ffa059 !important;
}
Hiding the billable field:
label[for=timesheet_edit_form_billable] { display:none; }
Hiding the navigation icons:
.sidebar-menu>li>ul>li>a>i, .sidebar-menu>li>a>i {
display: none;
}
body.sidebar-collapse .sidebar-menu>li>ul>li>a>i, body.sidebar-collapse .sidebar-menu>li>a>i {
display: inline-block;
}
Remove the title on security screens (login, reset password):
.login-logo, .register-logo { visibility: hidden; }
Setting a plain background color for security screens:
.login-logo, .register-logo { visibility: hidden; }
.layout-boxed body, .layout-boxed html, body, html { background: #000000; }
.login-page, .register-page { background: none; }
Hide the header on mobile devices:
@media (max-width: 767px) {
.main-header .logo {
display: none;
}
.fixed .content-wrapper, .fixed .right-side, .control-sidebar, .main-sidebar {
padding-top: 50px;
}
}
Permissions
Permission Name | Description |
---|---|
edit_custom_content | show the “custom content” administration screen |
js_custom_content | edit the additional javascript |
css_custom_content | edit the additional stylesheet |
alert_custom_content | edit the page wide warning message |
news_custom_content | edit the additional news page |
By default, these are assigned to each user with the role ROLE_SUPER_ADMIN
.
Read how to assign these permissions to your user roles in the permission documentation.