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

Custom content page

You can edit two fields:

  • The title is the name of the menu entry
  • The content for the page, markdown is supported for formatting.

If the title is empty, the menu will be hidden.

Examples

Javascript

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);

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;
            });
        });
    }
});

Always deactivate the export checkbox:

document.addEventListener('kimai.initialized', function(event) {
    const cb = document.querySelector('#preview_export #markAsExportedCheck');
    if (cb !== null && cb.checked) { cb.checked = false; }
});

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 a[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';

Alert

That's how the **alert / warning message** looks like. You can even include _markdown_ and [links](/en/custom-content-news) !

CSS / Stylesheet

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;
}

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

By default, these are assigned to each user with the role ROLE_SUPER_ADMIN.