mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2024-11-21 20:35:28 +03:00
Update admin interface
- Updated the admin interface dependencies. - Replace bootstrap-native with bootstrap - Added auto theme with an option to switch to dark/light - Some small color changes - Added an dev only function to always load static files from disk
This commit is contained in:
parent
f579a4154c
commit
83d5432cbf
16 changed files with 9163 additions and 7737 deletions
|
@ -14,11 +14,17 @@ use crate::{
|
|||
pub fn routes() -> Vec<Route> {
|
||||
// If addding more routes here, consider also adding them to
|
||||
// crate::utils::LOGGED_ROUTES to make sure they appear in the log
|
||||
let mut routes = routes![attachments, alive, alive_head, static_files];
|
||||
if CONFIG.web_vault_enabled() {
|
||||
routes![web_index, web_index_head, app_id, web_files, attachments, alive, alive_head, static_files]
|
||||
} else {
|
||||
routes![attachments, alive, alive_head, static_files]
|
||||
routes.append(&mut routes![web_index, web_index_head, app_id, web_files]);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if CONFIG.reload_templates() {
|
||||
routes.append(&mut routes![_static_files_dev]);
|
||||
}
|
||||
|
||||
routes
|
||||
}
|
||||
|
||||
pub fn catchers() -> Vec<Catcher> {
|
||||
|
@ -116,7 +122,30 @@ fn alive_head(_conn: DbConn) -> EmptyResult {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[get("/vw_static/<filename>")]
|
||||
// This endpoint/function is used during development and development only.
|
||||
// It allows to easily develop the admin interface by always loading the files from disk instead from a slice of bytes
|
||||
// This will only be active during a debug build and only when `RELOAD_TEMPLATES` is set to `true`
|
||||
// NOTE: Do not forget to add any new files added to the `static_files` function below!
|
||||
#[cfg(debug_assertions)]
|
||||
#[get("/vw_static/<filename>", rank = 1)]
|
||||
pub async fn _static_files_dev(filename: PathBuf) -> Option<NamedFile> {
|
||||
warn!("LOADING STATIC FILES FROM DISK");
|
||||
let file = filename.to_str().unwrap_or_default();
|
||||
let ext = filename.extension().unwrap_or_default();
|
||||
|
||||
let path = if ext == "png" || ext == "svg" {
|
||||
tokio::fs::canonicalize(Path::new(file!()).parent().unwrap().join("../static/images/").join(file)).await
|
||||
} else {
|
||||
tokio::fs::canonicalize(Path::new(file!()).parent().unwrap().join("../static/scripts/").join(file)).await
|
||||
};
|
||||
|
||||
if let Ok(path) = path {
|
||||
return NamedFile::open(path).await.ok();
|
||||
};
|
||||
None
|
||||
}
|
||||
|
||||
#[get("/vw_static/<filename>", rank = 2)]
|
||||
pub fn static_files(filename: &str) -> Result<(ContentType, &'static [u8]), Error> {
|
||||
match filename {
|
||||
"404.png" => Ok((ContentType::PNG, include_bytes!("../static/images/404.png"))),
|
||||
|
@ -138,12 +167,12 @@ pub fn static_files(filename: &str) -> Result<(ContentType, &'static [u8]), Erro
|
|||
Ok((ContentType::JavaScript, include_bytes!("../static/scripts/admin_diagnostics.js")))
|
||||
}
|
||||
"bootstrap.css" => Ok((ContentType::CSS, include_bytes!("../static/scripts/bootstrap.css"))),
|
||||
"bootstrap-native.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/bootstrap-native.js"))),
|
||||
"bootstrap.bundle.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/bootstrap.bundle.js"))),
|
||||
"jdenticon.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jdenticon.js"))),
|
||||
"datatables.js" => Ok((ContentType::JavaScript, include_bytes!("../static/scripts/datatables.js"))),
|
||||
"datatables.css" => Ok((ContentType::CSS, include_bytes!("../static/scripts/datatables.css"))),
|
||||
"jquery-3.6.4.slim.js" => {
|
||||
Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.6.4.slim.js")))
|
||||
"jquery-3.7.0.slim.js" => {
|
||||
Ok((ContentType::JavaScript, include_bytes!("../static/scripts/jquery-3.7.0.slim.js")))
|
||||
}
|
||||
_ => err!(format!("Static file not found: {filename}")),
|
||||
}
|
||||
|
|
85
src/static/scripts/admin.js
vendored
85
src/static/scripts/admin.js
vendored
|
@ -37,36 +37,107 @@ function _post(url, successMsg, errMsg, body, reload_page = true) {
|
|||
mode: "same-origin",
|
||||
credentials: "same-origin",
|
||||
headers: { "Content-Type": "application/json" }
|
||||
}).then( resp => {
|
||||
}).then(resp => {
|
||||
if (resp.ok) {
|
||||
msg(successMsg, reload_page);
|
||||
// Abuse the catch handler by setting error to false and continue
|
||||
return Promise.reject({error: false});
|
||||
return Promise.reject({ error: false });
|
||||
}
|
||||
respStatus = resp.status;
|
||||
respStatusText = resp.statusText;
|
||||
return resp.text();
|
||||
}).then( respText => {
|
||||
}).then(respText => {
|
||||
try {
|
||||
const respJson = JSON.parse(respText);
|
||||
if (respJson.ErrorModel && respJson.ErrorModel.Message) {
|
||||
return respJson.ErrorModel.Message;
|
||||
} else {
|
||||
return Promise.reject({body:`${respStatus} - ${respStatusText}\n\nUnknown error`, error: true});
|
||||
return Promise.reject({ body: `${respStatus} - ${respStatusText}\n\nUnknown error`, error: true });
|
||||
}
|
||||
} catch (e) {
|
||||
return Promise.reject({body:`${respStatus} - ${respStatusText}\n\n[Catch] ${e}`, error: true});
|
||||
return Promise.reject({ body: `${respStatus} - ${respStatusText}\n\n[Catch] ${e}`, error: true });
|
||||
}
|
||||
}).then( apiMsg => {
|
||||
}).then(apiMsg => {
|
||||
msg(`${errMsg}\n${apiMsg}`, reload_page);
|
||||
}).catch( e => {
|
||||
}).catch(e => {
|
||||
if (e.error === false) { return true; }
|
||||
else { msg(`${errMsg}\n${e.body}`, reload_page); }
|
||||
});
|
||||
}
|
||||
|
||||
// Bootstrap Theme Selector
|
||||
const getStoredTheme = () => localStorage.getItem("theme");
|
||||
const setStoredTheme = theme => localStorage.setItem("theme", theme);
|
||||
|
||||
const getPreferredTheme = () => {
|
||||
const storedTheme = getStoredTheme();
|
||||
if (storedTheme) {
|
||||
return storedTheme;
|
||||
}
|
||||
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||
};
|
||||
|
||||
const setTheme = theme => {
|
||||
if (theme === "auto" && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||
document.documentElement.setAttribute("data-bs-theme", "dark");
|
||||
} else {
|
||||
document.documentElement.setAttribute("data-bs-theme", theme);
|
||||
}
|
||||
};
|
||||
|
||||
setTheme(getPreferredTheme());
|
||||
|
||||
const showActiveTheme = (theme, focus = false) => {
|
||||
const themeSwitcher = document.querySelector("#bd-theme");
|
||||
|
||||
if (!themeSwitcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
const themeSwitcherText = document.querySelector("#bd-theme-text");
|
||||
const activeThemeIcon = document.querySelector(".theme-icon-active use");
|
||||
const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`);
|
||||
const svgOfActiveBtn = btnToActive.querySelector("span use").innerText;
|
||||
|
||||
document.querySelectorAll("[data-bs-theme-value]").forEach(element => {
|
||||
element.classList.remove("active");
|
||||
element.setAttribute("aria-pressed", "false");
|
||||
});
|
||||
|
||||
btnToActive.classList.add("active");
|
||||
btnToActive.setAttribute("aria-pressed", "true");
|
||||
activeThemeIcon.innerText = svgOfActiveBtn;
|
||||
const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`;
|
||||
themeSwitcher.setAttribute("aria-label", themeSwitcherLabel);
|
||||
|
||||
if (focus) {
|
||||
themeSwitcher.focus();
|
||||
}
|
||||
};
|
||||
|
||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
||||
const storedTheme = getStoredTheme();
|
||||
if (storedTheme !== "light" && storedTheme !== "dark") {
|
||||
setTheme(getPreferredTheme());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// onLoad events
|
||||
document.addEventListener("DOMContentLoaded", (/*event*/) => {
|
||||
showActiveTheme(getPreferredTheme());
|
||||
|
||||
document.querySelectorAll("[data-bs-theme-value]")
|
||||
.forEach(toggle => {
|
||||
toggle.addEventListener("click", () => {
|
||||
const theme = toggle.getAttribute("data-bs-theme-value");
|
||||
setStoredTheme(theme);
|
||||
setTheme(theme);
|
||||
showActiveTheme(theme, true);
|
||||
});
|
||||
});
|
||||
|
||||
// get current URL path and assign "active" class to the correct nav-item
|
||||
const pathname = window.location.pathname;
|
||||
if (pathname === "") return;
|
||||
|
|
4
src/static/scripts/admin_diagnostics.js
vendored
4
src/static/scripts/admin_diagnostics.js
vendored
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
/* eslint-env es2017, browser */
|
||||
/* global BASE_URL:readable, BSN:readable */
|
||||
/* global BASE_URL:readable, bootstrap:readable */
|
||||
|
||||
var dnsCheck = false;
|
||||
var timeCheck = false;
|
||||
|
@ -135,7 +135,7 @@ function copyToClipboard(event) {
|
|||
document.execCommand("copy");
|
||||
tmpCopyEl.remove();
|
||||
|
||||
new BSN.Toast("#toastClipboardCopy").show();
|
||||
new bootstrap.Toast("#toastClipboardCopy").show();
|
||||
}
|
||||
|
||||
function checkTimeDrift(utcTimeA, utcTimeB, statusPrefix) {
|
||||
|
|
14
src/static/scripts/admin_users.js
vendored
14
src/static/scripts/admin_users.js
vendored
|
@ -141,19 +141,20 @@ function resendUserInvite (event) {
|
|||
const ORG_TYPES = {
|
||||
"0": {
|
||||
"name": "Owner",
|
||||
"color": "orange"
|
||||
"bg": "orange",
|
||||
"font": "black"
|
||||
},
|
||||
"1": {
|
||||
"name": "Admin",
|
||||
"color": "blueviolet"
|
||||
"bg": "blueviolet"
|
||||
},
|
||||
"2": {
|
||||
"name": "User",
|
||||
"color": "blue"
|
||||
"bg": "blue"
|
||||
},
|
||||
"3": {
|
||||
"name": "Manager",
|
||||
"color": "green"
|
||||
"bg": "green"
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -227,7 +228,10 @@ function initUserTable() {
|
|||
// Color all the org buttons per type
|
||||
document.querySelectorAll("button[data-vw-org-type]").forEach(function(e) {
|
||||
const orgType = ORG_TYPES[e.dataset.vwOrgType];
|
||||
e.style.backgroundColor = orgType.color;
|
||||
e.style.backgroundColor = orgType.bg;
|
||||
if (orgType.font !== undefined) {
|
||||
e.style.color = orgType.font;
|
||||
}
|
||||
e.title = orgType.name;
|
||||
});
|
||||
|
||||
|
|
5991
src/static/scripts/bootstrap-native.js
vendored
5991
src/static/scripts/bootstrap-native.js
vendored
File diff suppressed because it is too large
Load diff
6313
src/static/scripts/bootstrap.bundle.js
vendored
Normal file
6313
src/static/scripts/bootstrap.bundle.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
2379
src/static/scripts/bootstrap.css
vendored
2379
src/static/scripts/bootstrap.css
vendored
File diff suppressed because it is too large
Load diff
47
src/static/scripts/datatables.css
vendored
47
src/static/scripts/datatables.css
vendored
|
@ -4,10 +4,10 @@
|
|||
*
|
||||
* To rebuild or modify this file with the latest versions of the included
|
||||
* software please visit:
|
||||
* https://datatables.net/download/#bs5/dt-1.13.4
|
||||
* https://datatables.net/download/#bs5/dt-1.13.6
|
||||
*
|
||||
* Included libraries:
|
||||
* DataTables 1.13.4
|
||||
* DataTables 1.13.6
|
||||
*/
|
||||
|
||||
@charset "UTF-8";
|
||||
|
@ -15,6 +15,13 @@
|
|||
--dt-row-selected: 13, 110, 253;
|
||||
--dt-row-selected-text: 255, 255, 255;
|
||||
--dt-row-selected-link: 9, 10, 11;
|
||||
--dt-row-stripe: 0, 0, 0;
|
||||
--dt-row-hover: 0, 0, 0;
|
||||
--dt-column-ordering: 0, 0, 0;
|
||||
--dt-html-background: white;
|
||||
}
|
||||
:root.dark {
|
||||
--dt-html-background: rgb(33, 37, 41);
|
||||
}
|
||||
|
||||
table.dataTable td.dt-control {
|
||||
|
@ -22,25 +29,19 @@ table.dataTable td.dt-control {
|
|||
cursor: pointer;
|
||||
}
|
||||
table.dataTable td.dt-control:before {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
margin-top: -9px;
|
||||
display: inline-block;
|
||||
color: white;
|
||||
border: 0.15em solid white;
|
||||
border-radius: 1em;
|
||||
box-shadow: 0 0 0.2em #444;
|
||||
box-sizing: content-box;
|
||||
text-align: center;
|
||||
text-indent: 0 !important;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
line-height: 1em;
|
||||
content: "+";
|
||||
background-color: #31b131;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
content: "►";
|
||||
}
|
||||
table.dataTable tr.dt-hasChild td.dt-control:before {
|
||||
content: "-";
|
||||
background-color: #d33333;
|
||||
content: "▼";
|
||||
}
|
||||
|
||||
html.dark table.dataTable td.dt-control:before {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
html.dark table.dataTable tr.dt-hasChild td.dt-control:before {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
table.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled,
|
||||
|
@ -303,14 +304,14 @@ table.dataTable > tbody > tr.selected a {
|
|||
color: rgb(var(--dt-row-selected-link));
|
||||
}
|
||||
table.dataTable.table-striped > tbody > tr.odd > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.05);
|
||||
}
|
||||
table.dataTable.table-striped > tbody > tr.odd.selected > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.95);
|
||||
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.95);
|
||||
}
|
||||
table.dataTable.table-hover > tbody > tr:hover > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075);
|
||||
}
|
||||
table.dataTable.table-hover > tbody > tr.selected:hover > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.975);
|
||||
|
@ -441,4 +442,10 @@ div.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:last-
|
|||
padding-right: 0;
|
||||
}
|
||||
|
||||
:root[data-bs-theme=dark] {
|
||||
--dt-row-hover: 255, 255, 255;
|
||||
--dt-row-stripe: 255, 255, 255;
|
||||
--dt-column-ordering: 255, 255, 255;
|
||||
}
|
||||
|
||||
|
||||
|
|
91
src/static/scripts/datatables.js
vendored
91
src/static/scripts/datatables.js
vendored
|
@ -4,20 +4,20 @@
|
|||
*
|
||||
* To rebuild or modify this file with the latest versions of the included
|
||||
* software please visit:
|
||||
* https://datatables.net/download/#bs5/dt-1.13.4
|
||||
* https://datatables.net/download/#bs5/dt-1.13.6
|
||||
*
|
||||
* Included libraries:
|
||||
* DataTables 1.13.4
|
||||
* DataTables 1.13.6
|
||||
*/
|
||||
|
||||
/*! DataTables 1.13.4
|
||||
/*! DataTables 1.13.6
|
||||
* ©2008-2023 SpryMedia Ltd - datatables.net/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @summary DataTables
|
||||
* @description Paginate, search and order HTML tables
|
||||
* @version 1.13.4
|
||||
* @version 1.13.6
|
||||
* @author SpryMedia Ltd
|
||||
* @contact www.datatables.net
|
||||
* @copyright SpryMedia Ltd.
|
||||
|
@ -50,7 +50,7 @@
|
|||
// returns a factory function that expects the window object
|
||||
var jq = require('jquery');
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
if (typeof window === 'undefined') {
|
||||
module.exports = function (root, $) {
|
||||
if ( ! root ) {
|
||||
// CommonJS environments without a window global must pass a
|
||||
|
@ -1396,7 +1396,7 @@
|
|||
|
||||
|
||||
var _isNumber = function ( d, decimalPoint, formatted ) {
|
||||
let type = typeof d;
|
||||
var type = typeof d;
|
||||
var strType = type === 'string';
|
||||
|
||||
if ( type === 'number' || type === 'bigint') {
|
||||
|
@ -1530,7 +1530,9 @@
|
|||
|
||||
|
||||
var _stripHtml = function ( d ) {
|
||||
return d.replace( _re_html, '' );
|
||||
return d
|
||||
.replace( _re_html, '' ) // Complete tags
|
||||
.replace(/<script/i, ''); // Safety for incomplete script tag
|
||||
};
|
||||
|
||||
|
||||
|
@ -1904,7 +1906,10 @@
|
|||
continue;
|
||||
}
|
||||
|
||||
if ( data === null || data[ a[i] ] === undefined ) {
|
||||
if (data === null || data[ a[i] ] === null) {
|
||||
return null;
|
||||
}
|
||||
else if ( data === undefined || data[ a[i] ] === undefined ) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -2351,6 +2356,12 @@
|
|||
oCol.aDataSort = [ oOptions.iDataSort ];
|
||||
}
|
||||
_fnMap( oCol, oOptions, "aDataSort" );
|
||||
|
||||
// Fall back to the aria-label attribute on the table header if no ariaTitle is
|
||||
// provided.
|
||||
if (! oCol.ariaTitle) {
|
||||
oCol.ariaTitle = th.attr("aria-label");
|
||||
}
|
||||
}
|
||||
|
||||
/* Cache the data get and set functions for speed */
|
||||
|
@ -4075,11 +4086,16 @@
|
|||
settings.iDraw++;
|
||||
_fnProcessingDisplay( settings, true );
|
||||
|
||||
// Keep track of drawHold state to handle scrolling after the Ajax call
|
||||
var drawHold = settings._drawHold;
|
||||
|
||||
_fnBuildAjax(
|
||||
settings,
|
||||
_fnAjaxParameters( settings ),
|
||||
function(json) {
|
||||
settings._drawHold = drawHold;
|
||||
_fnAjaxUpdateDraw( settings, json );
|
||||
settings._drawHold = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -4343,7 +4359,7 @@
|
|||
_fnThrottle( searchFn, searchDelay ) :
|
||||
searchFn
|
||||
)
|
||||
.on( 'mouseup', function(e) {
|
||||
.on( 'mouseup.DT', function(e) {
|
||||
// Edge fix! Edge 17 does not trigger anything other than mouse events when clicking
|
||||
// on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn`
|
||||
// checks the value to see if it has changed. In other browsers it won't have.
|
||||
|
@ -4409,7 +4425,7 @@
|
|||
if ( _fnDataSource( oSettings ) != 'ssp' )
|
||||
{
|
||||
/* Global filter */
|
||||
_fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive, oInput.return );
|
||||
_fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
|
||||
fnSaveFilter( oInput );
|
||||
|
||||
/* Now do the individual column filter */
|
||||
|
@ -4578,11 +4594,15 @@
|
|||
*
|
||||
* ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
|
||||
*/
|
||||
var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
|
||||
var a = $.map( search.match( /["\u201C][^"\u201D]+["\u201D]|[^ ]+/g ) || [''], function ( word ) {
|
||||
if ( word.charAt(0) === '"' ) {
|
||||
var m = word.match( /^"(.*)"$/ );
|
||||
word = m ? m[1] : word;
|
||||
}
|
||||
else if ( word.charAt(0) === '\u201C' ) {
|
||||
var m = word.match( /^\u201C(.*)\u201D$/ );
|
||||
word = m ? m[1] : word;
|
||||
}
|
||||
|
||||
return word.replace('"', '');
|
||||
} );
|
||||
|
@ -9386,7 +9406,8 @@
|
|||
* Set the jQuery or window object to be used by DataTables
|
||||
*
|
||||
* @param {*} module Library / container object
|
||||
* @param {string} type Library or container type `lib` or `win`.
|
||||
* @param {string} [type] Library or container type `lib`, `win` or `datetime`.
|
||||
* If not provided, automatic detection is attempted.
|
||||
*/
|
||||
DataTable.use = function (module, type) {
|
||||
if (type === 'lib' || module.fn) {
|
||||
|
@ -9396,6 +9417,9 @@
|
|||
window = module;
|
||||
document = module.document;
|
||||
}
|
||||
else if (type === 'datetime' || module.type === 'DateTime') {
|
||||
DataTable.DateTime = module;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9755,7 +9779,9 @@
|
|||
resolved._;
|
||||
}
|
||||
|
||||
return resolved.replace( '%d', plural ); // nb: plural might be undefined,
|
||||
return typeof resolved === 'string'
|
||||
? resolved.replace( '%d', plural ) // nb: plural might be undefined,
|
||||
: resolved;
|
||||
} );
|
||||
/**
|
||||
* Version string for plug-ins to check compatibility. Allowed format is
|
||||
|
@ -9765,7 +9791,7 @@
|
|||
* @type string
|
||||
* @default Version number
|
||||
*/
|
||||
DataTable.version = "1.13.4";
|
||||
DataTable.version = "1.13.6";
|
||||
|
||||
/**
|
||||
* Private data store, containing all of the settings objects that are
|
||||
|
@ -14189,7 +14215,7 @@
|
|||
*
|
||||
* @type string
|
||||
*/
|
||||
build:"bs5/dt-1.13.4",
|
||||
build:"bs5/dt-1.13.6",
|
||||
|
||||
|
||||
/**
|
||||
|
@ -14830,7 +14856,7 @@
|
|||
var btnDisplay, btnClass;
|
||||
|
||||
var attach = function( container, buttons ) {
|
||||
var i, ien, node, button, tabIndex;
|
||||
var i, ien, node, button;
|
||||
var disabledClass = classes.sPageButtonDisabled;
|
||||
var clickHandler = function ( e ) {
|
||||
_fnPageChange( settings, e.data.action, true );
|
||||
|
@ -14845,9 +14871,10 @@
|
|||
attach( inner, button );
|
||||
}
|
||||
else {
|
||||
var disabled = false;
|
||||
|
||||
btnDisplay = null;
|
||||
btnClass = button;
|
||||
tabIndex = settings.iTabIndex;
|
||||
|
||||
switch ( button ) {
|
||||
case 'ellipsis':
|
||||
|
@ -14858,8 +14885,7 @@
|
|||
btnDisplay = lang.sFirst;
|
||||
|
||||
if ( page === 0 ) {
|
||||
tabIndex = -1;
|
||||
btnClass += ' ' + disabledClass;
|
||||
disabled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -14867,8 +14893,7 @@
|
|||
btnDisplay = lang.sPrevious;
|
||||
|
||||
if ( page === 0 ) {
|
||||
tabIndex = -1;
|
||||
btnClass += ' ' + disabledClass;
|
||||
disabled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -14876,8 +14901,7 @@
|
|||
btnDisplay = lang.sNext;
|
||||
|
||||
if ( pages === 0 || page === pages-1 ) {
|
||||
tabIndex = -1;
|
||||
btnClass += ' ' + disabledClass;
|
||||
disabled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -14885,8 +14909,7 @@
|
|||
btnDisplay = lang.sLast;
|
||||
|
||||
if ( pages === 0 || page === pages-1 ) {
|
||||
tabIndex = -1;
|
||||
btnClass += ' ' + disabledClass;
|
||||
disabled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -14899,18 +14922,20 @@
|
|||
|
||||
if ( btnDisplay !== null ) {
|
||||
var tag = settings.oInit.pagingTag || 'a';
|
||||
var disabled = btnClass.indexOf(disabledClass) !== -1;
|
||||
|
||||
if (disabled) {
|
||||
btnClass += ' ' + disabledClass;
|
||||
}
|
||||
|
||||
node = $('<'+tag+'>', {
|
||||
'class': classes.sPageButton+' '+btnClass,
|
||||
'aria-controls': settings.sTableId,
|
||||
'aria-disabled': disabled ? 'true' : null,
|
||||
'aria-label': aria[ button ],
|
||||
'aria-role': 'link',
|
||||
'role': 'link',
|
||||
'aria-current': btnClass === classes.sPageButtonActive ? 'page' : null,
|
||||
'data-dt-idx': button,
|
||||
'tabindex': tabIndex,
|
||||
'tabindex': disabled ? -1 : settings.iTabIndex,
|
||||
'id': idx === 0 && typeof button === 'string' ?
|
||||
settings.sTableId +'_'+ button :
|
||||
null
|
||||
|
@ -15041,7 +15066,7 @@
|
|||
return -Infinity;
|
||||
}
|
||||
|
||||
let type = typeof d;
|
||||
var type = typeof d;
|
||||
|
||||
if (type === 'number' || type === 'bigint') {
|
||||
return d;
|
||||
|
@ -15415,7 +15440,7 @@
|
|||
var __thousands = ',';
|
||||
var __decimal = '.';
|
||||
|
||||
if (Intl) {
|
||||
if (window.Intl !== undefined) {
|
||||
try {
|
||||
var num = new Intl.NumberFormat().formatToParts(100000.1);
|
||||
|
||||
|
@ -15718,7 +15743,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
if (typeof window === 'undefined') {
|
||||
module.exports = function (root, $) {
|
||||
if ( ! root ) {
|
||||
// CommonJS environments without a window global must pass a
|
||||
|
@ -15856,10 +15881,10 @@ DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, bu
|
|||
'aria-controls': settings.sTableId,
|
||||
'aria-disabled': disabled ? 'true' : null,
|
||||
'aria-label': aria[ button ],
|
||||
'aria-role': 'link',
|
||||
'role': 'link',
|
||||
'aria-current': btnClass === 'active' ? 'page' : null,
|
||||
'data-dt-idx': button,
|
||||
'tabindex': settings.iTabIndex,
|
||||
'tabindex': disabled ? -1 : settings.iTabIndex,
|
||||
'class': 'page-link'
|
||||
} )
|
||||
.html( btnDisplay )
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" data-bs-theme="auto">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
@ -10,17 +10,17 @@
|
|||
<link rel="stylesheet" href="{{urlpath}}/vw_static/admin.css" />
|
||||
<script src="{{urlpath}}/vw_static/admin.js"></script>
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4 shadow fixed-top">
|
||||
<div class="container-xl">
|
||||
<a class="navbar-brand" href="{{urlpath}}/admin"><img class="vaultwarden-icon" src="{{urlpath}}/vw_static/vaultwarden-icon.png" alt="V">aultwarden Admin</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse"
|
||||
aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
|
||||
aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<ul class="navbar-nav me-auto">
|
||||
{{#if logged_in}}
|
||||
{{#if logged_in}}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{urlpath}}/admin">Settings</a>
|
||||
</li>
|
||||
|
@ -33,15 +33,59 @@
|
|||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{urlpath}}/admin/diagnostics">Diagnostics</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{urlpath}}/" target="_blank" rel="noreferrer">Vault</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item dropdown">
|
||||
<button
|
||||
class="btn btn-link nav-link py-0 px-0 px-md-2 dropdown-toggle d-flex align-items-center"
|
||||
id="bd-theme" type="button" aria-expanded="false" data-bs-toggle="dropdown"
|
||||
data-bs-display="static" aria-label="Toggle theme (auto)">
|
||||
<span class="my-1 fs-4 theme-icon-active">
|
||||
<use>☯</use>
|
||||
</span>
|
||||
<span class="d-md-none ms-2" id="bd-theme-text">Toggle theme</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="bd-theme-text">
|
||||
<li>
|
||||
<button type="button" class="dropdown-item d-flex align-items-center"
|
||||
data-bs-theme-value="light" aria-pressed="false">
|
||||
<span class="me-2 fs-4 theme-icon">
|
||||
<use>☀</use>
|
||||
</span>
|
||||
Light
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button type="button" class="dropdown-item d-flex align-items-center"
|
||||
data-bs-theme-value="dark" aria-pressed="false">
|
||||
<span class="me-2 fs-4 theme-icon">
|
||||
<use>★</use>
|
||||
</span>
|
||||
Dark
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button type="button" class="dropdown-item d-flex align-items-center active"
|
||||
data-bs-theme-value="auto" aria-pressed="true">
|
||||
<span class="me-2 fs-4 theme-icon">
|
||||
<use>☯</use>
|
||||
</span>
|
||||
Auto
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{{#if logged_in}}
|
||||
<a class="btn btn-sm btn-secondary" href="{{urlpath}}/admin/logout">Log Out</a>
|
||||
<a class="btn btn-sm btn-secondary" href="{{urlpath}}/admin/logout">Log Out</a>
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
@ -49,6 +93,6 @@
|
|||
{{> (lookup this "page_content") }}
|
||||
|
||||
<!-- This script needs to be at the bottom, else it will fail! -->
|
||||
<script src="{{urlpath}}/vw_static/bootstrap-native.js"></script>
|
||||
<script src="{{urlpath}}/vw_static/bootstrap.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<main class="container-xl">
|
||||
<div id="diagnostics-block" class="my-3 p-3 bg-white rounded shadow">
|
||||
<div id="diagnostics-block" class="my-3 p-3 rounded shadow">
|
||||
<h6 class="border-bottom pb-2 mb-2">Diagnostics</h6>
|
||||
|
||||
<h3>Versions</h3>
|
||||
|
@ -8,8 +8,8 @@
|
|||
<dl class="row">
|
||||
<dt class="col-sm-5">Server Installed
|
||||
<span class="badge bg-success d-none" id="server-success" title="Latest version is installed.">Ok</span>
|
||||
<span class="badge bg-warning d-none" id="server-warning" title="There seems to be an update available.">Update</span>
|
||||
<span class="badge bg-info d-none" id="server-branch" title="This is a branched version.">Branched</span>
|
||||
<span class="badge bg-warning text-dark d-none" id="server-warning" title="There seems to be an update available.">Update</span>
|
||||
<span class="badge bg-info text-dark d-none" id="server-branch" title="This is a branched version.">Branched</span>
|
||||
</dt>
|
||||
<dd class="col-sm-7">
|
||||
<span id="server-installed">{{page_data.current_release}}</span>
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<main class="container-xl">
|
||||
{{#if error}}
|
||||
<div class="align-items-center p-3 mb-3 text-white-50 bg-warning rounded shadow">
|
||||
<div class="align-items-center p-3 mb-3 text-opacity-50 text-dark bg-warning rounded shadow">
|
||||
<div>
|
||||
<h6 class="mb-0 text-white">{{error}}</h6>
|
||||
<h6 class="mb-0 text-dark">{{error}}</h6>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="align-items-center p-3 mb-3 text-white-50 bg-danger rounded shadow">
|
||||
<div class="align-items-center p-3 mb-3 text-opacity-75 text-light bg-danger rounded shadow">
|
||||
<div>
|
||||
<h6 class="mb-0 text-white">Authentication key needed to continue</h6>
|
||||
<h6 class="mb-0 text-light">Authentication key needed to continue</h6>
|
||||
<small>Please provide it below:</small>
|
||||
|
||||
<form class="form-inline" method="post" action="{{urlpath}}/admin">
|
||||
|
@ -17,7 +17,7 @@
|
|||
{{#if redirect}}
|
||||
<input type="hidden" id="redirect" name="redirect" value="/{{redirect}}">
|
||||
{{/if}}
|
||||
<button type="submit" class="btn btn-primary">Enter</button>
|
||||
<button type="submit" class="btn btn-primary mt-2">Enter</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<main class="container-xl">
|
||||
<div id="organizations-block" class="my-3 p-3 bg-white rounded shadow">
|
||||
<div id="organizations-block" class="my-3 p-3 rounded shadow">
|
||||
<h6 class="border-bottom pb-2 mb-3">Organizations</h6>
|
||||
<div class="table-responsive-xl small">
|
||||
<table id="orgs-table" class="table table-sm table-striped table-hover">
|
||||
|
@ -59,7 +59,7 @@
|
|||
</main>
|
||||
|
||||
<link rel="stylesheet" href="{{urlpath}}/vw_static/datatables.css" />
|
||||
<script src="{{urlpath}}/vw_static/jquery-3.6.4.slim.js"></script>
|
||||
<script src="{{urlpath}}/vw_static/jquery-3.7.0.slim.js"></script>
|
||||
<script src="{{urlpath}}/vw_static/datatables.js"></script>
|
||||
<script src="{{urlpath}}/vw_static/admin_organizations.js"></script>
|
||||
<script src="{{urlpath}}/vw_static/jdenticon.js"></script>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<form class="form needs-validation" id="config-form" novalidate>
|
||||
{{#each page_data.config}}
|
||||
{{#if groupdoc}}
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card mb-3">
|
||||
<button id="b_{{group}}" type="button" class="card-header text-start btn btn-link text-decoration-none" aria-expanded="false" aria-controls="g_{{group}}" data-bs-toggle="collapse" data-bs-target="#g_{{group}}">{{groupdoc}}</button>
|
||||
<div id="g_{{group}}" class="card-body collapse">
|
||||
{{#each elements}}
|
||||
|
@ -64,7 +64,7 @@
|
|||
{{/if}}
|
||||
{{/each}}
|
||||
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card mb-3">
|
||||
<button id="b_readonly" type="button" class="card-header text-start btn btn-link text-decoration-none" aria-expanded="false" aria-controls="g_readonly"
|
||||
data-bs-toggle="collapse" data-bs-target="#g_readonly">Read-Only Config</button>
|
||||
<div id="g_readonly" class="card-body collapse">
|
||||
|
@ -119,7 +119,7 @@
|
|||
</div>
|
||||
|
||||
{{#if page_data.can_backup}}
|
||||
<div class="card bg-light mb-3">
|
||||
<div class="card mb-3">
|
||||
<button id="b_database" type="button" class="card-header text-start btn btn-link text-decoration-none" aria-expanded="false" aria-controls="g_database"
|
||||
data-bs-toggle="collapse" data-bs-target="#g_database">Backup Database</button>
|
||||
<div id="g_database" class="card-body collapse">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<main class="container-xl">
|
||||
<div id="users-block" class="my-3 p-3 bg-white rounded shadow">
|
||||
<div id="users-block" class="my-3 p-3 rounded shadow">
|
||||
<h6 class="border-bottom pb-2 mb-3">Registered Users</h6>
|
||||
<div class="table-responsive-xl small">
|
||||
<table id="users-table" class="table table-sm table-striped table-hover">
|
||||
|
@ -30,7 +30,7 @@
|
|||
<span class="badge bg-success me-2" title="2FA is enabled">2FA</span>
|
||||
{{/if}}
|
||||
{{#case _Status 1}}
|
||||
<span class="badge bg-warning me-2" title="User is invited">Invited</span>
|
||||
<span class="badge bg-warning text-dark me-2" title="User is invited">Invited</span>
|
||||
{{/case}}
|
||||
{{#if EmailVerified}}
|
||||
<span class="badge bg-success me-2" title="Email has been verified">Verified</span>
|
||||
|
@ -140,7 +140,7 @@
|
|||
</main>
|
||||
|
||||
<link rel="stylesheet" href="{{urlpath}}/vw_static/datatables.css" />
|
||||
<script src="{{urlpath}}/vw_static/jquery-3.6.4.slim.js"></script>
|
||||
<script src="{{urlpath}}/vw_static/jquery-3.7.0.slim.js"></script>
|
||||
<script src="{{urlpath}}/vw_static/datatables.js"></script>
|
||||
<script src="{{urlpath}}/vw_static/admin_users.js"></script>
|
||||
<script src="{{urlpath}}/vw_static/jdenticon.js"></script>
|
||||
|
|
Loading…
Reference in a new issue