mirror of
https://github.com/9p4/jellyfin-plugin-sso.git
synced 2026-09-19 13:12:19 +00:00
Finish implementing linking view page
This commit is contained in:
@@ -1,8 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Polyfill styles that are missing when serving without dashboard -->
|
||||
<link rel="stylesheet" href="emby-restyle.css" />
|
||||
<link id="theme-style" rel="stylesheet" />
|
||||
<!-- Polyfill material icons -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/material-design-icons.min.css"
|
||||
integrity="sha256-YEkUHnLEghQI0YDx3I/r6cCeYYJHQEdrN+FN2PneTsk="
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<script defer>
|
||||
import("./ApiClient.js").then((clientModule) => {
|
||||
import("./linking.js").then((renderer) => {
|
||||
@@ -12,8 +20,9 @@
|
||||
"/web/themes/dark/theme.css"
|
||||
);
|
||||
|
||||
document.querySelector("a.emby-button.home").href =
|
||||
ApiClient.serverAddress();
|
||||
const homeButton = document.querySelector("a.emby-button.home");
|
||||
homeButton.href = ApiClient.serverAddress();
|
||||
homeButton.style.display = "";
|
||||
renderer.default(view);
|
||||
});
|
||||
});
|
||||
@@ -30,7 +39,7 @@
|
||||
<div data-role="content">
|
||||
<div class="content-primary">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<a class="raised emby-button home">
|
||||
<a class="raised emby-button home" style="display: none">
|
||||
<span class="material-icons home" aria-hidden="true"></span
|
||||
><span>Home</span>
|
||||
</a>
|
||||
@@ -44,9 +53,16 @@
|
||||
>
|
||||
</div>
|
||||
<p>
|
||||
Use the following buttons to link your jellyfin account to any one
|
||||
of the SSO providers. Please note that deletion is currently only
|
||||
available by API.
|
||||
Use the
|
||||
<span
|
||||
style="font-size: initial"
|
||||
class="fab material-icons add"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
button to create a new link for the given provider.
|
||||
<br />
|
||||
You may remove existing links by selecting them and pressing the
|
||||
"Delete" button.
|
||||
</p>
|
||||
<p>
|
||||
See the
|
||||
@@ -65,9 +81,26 @@
|
||||
</a>
|
||||
for more information.
|
||||
</p>
|
||||
<label class="checkbox-wrapper">
|
||||
<input is="emby-checkbox" id="enable-delete" type="checkbox" />
|
||||
<span class="checkbox-label">Enable "Delete" button.</span>
|
||||
</label>
|
||||
<h1 class="sectionTitle">Link a provider</h1>
|
||||
<div class="verticalSection" title="Link a provider">
|
||||
<div id="sso-provider-list"></div>
|
||||
<h2 class="sectionTitle">SAML</h2>
|
||||
<div id="sso-provider-list-saml" data-id="saml"></div>
|
||||
<h2 class="sectionTitle">OID</h2>
|
||||
<div id="sso-provider-list-oid" data-id="oid"></div>
|
||||
</div>
|
||||
<button
|
||||
id="btn-delete-selected-links"
|
||||
type="button"
|
||||
class="button-delete raised emby-button"
|
||||
disabled="true"
|
||||
>
|
||||
<span class="material-icons delete" aria-hidden="true"></span>
|
||||
<span>Delete Selected</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+153
-27
@@ -2,9 +2,69 @@ const ssoConfigLinking = {
|
||||
pluginUniqueId: "505ce9d1-d916-42fa-86ca-673ef241d7df",
|
||||
loadProviders: (view) => {
|
||||
const provider_list_id = "sso-provider-list";
|
||||
const provider_list_saml_id = `${provider_list_id}-saml`;
|
||||
const provider_list_oid_id = `${provider_list_id}-oid`;
|
||||
|
||||
var provider_list = view.querySelector("#" + provider_list_id);
|
||||
provider_list.innerHTML = "";
|
||||
const provider_list_saml = view.querySelector(`#${provider_list_saml_id}`);
|
||||
const provider_list_oid = view.querySelector(`#${provider_list_oid_id}`);
|
||||
provider_list_saml.innerHTML = "";
|
||||
provider_list_oid.innerHTML = "";
|
||||
|
||||
fetch(new Request(ApiClient.getUrl("sso/OID/GetNames"))).then((resp) => {
|
||||
resp.json().then((config_names) => {
|
||||
ssoConfigLinking.loadProviderList(
|
||||
provider_list_oid,
|
||||
config_names,
|
||||
"oid"
|
||||
);
|
||||
});
|
||||
});
|
||||
fetch(new Request(ApiClient.getUrl("sso/SAML/GetNames"))).then((resp) => {
|
||||
resp.json().then((config_names) => {
|
||||
ssoConfigLinking.loadProviderList(
|
||||
provider_list_saml,
|
||||
config_names,
|
||||
"saml"
|
||||
);
|
||||
});
|
||||
});
|
||||
},
|
||||
loadProviderList: (container, providers, provider_mode) => {
|
||||
providers.forEach((provider_name) => {
|
||||
var provider_config = document.createElement("div");
|
||||
provider_config.classList.add("sso-provider-links-container");
|
||||
provider_config.setAttribute("data-id", provider_name);
|
||||
|
||||
provider_config.innerHTML = `
|
||||
<label
|
||||
class="inputLabel inputLabelUnfocused sso-provider-link-title"
|
||||
>${provider_name}
|
||||
</label>
|
||||
<a
|
||||
class="fab emby-button sso-provider-add-link"
|
||||
>
|
||||
<span class="material-icons add" aria-hidden="true"></span>
|
||||
</a>
|
||||
<div
|
||||
class="sso-provider-existing-links-container"
|
||||
data-provider="${provider_name}"
|
||||
></div>
|
||||
`;
|
||||
var add_provider = provider_config.querySelector(
|
||||
".sso-provider-add-link"
|
||||
);
|
||||
|
||||
//const provider_name_css = ssoConfigLinking.safeCSSId(provider_name);
|
||||
//provider_link.id = "sso-provider-" + provider_name_css;
|
||||
//provider_link.classList.add("sso-provider-" + provider_name_css);
|
||||
add_provider.classList.add("sso-provider");
|
||||
|
||||
add_provider.href = ApiClient.getUrl(
|
||||
`/SSO/${provider_mode}/p/${provider_name}?isLinking=true`
|
||||
);
|
||||
|
||||
container.appendChild(provider_config);
|
||||
});
|
||||
|
||||
const currentUserId = ApiClient.getCurrentUserId();
|
||||
|
||||
@@ -12,49 +72,115 @@ const ssoConfigLinking = {
|
||||
ApiClient.fetch(
|
||||
{
|
||||
type: "GET",
|
||||
url: ApiClient.getUrl(`sso/OID/links/${currentUserId}`),
|
||||
url: ApiClient.getUrl(`sso/${provider_mode}/links/${currentUserId}`),
|
||||
},
|
||||
true
|
||||
).then((resp) => {
|
||||
resp.json().then((x) => console.log(x));
|
||||
resp.json().then((provider_map) => {
|
||||
console.log({ provider_map, currentUserId });
|
||||
|
||||
Object.keys(provider_map).forEach((provider_name) => {
|
||||
const provider_container = container.querySelector(
|
||||
`.sso-provider-existing-links-container[data-provider="${provider_name}"]`
|
||||
);
|
||||
ssoConfigLinking.populateExistingLinks(
|
||||
provider_container,
|
||||
provider_mode,
|
||||
provider_name,
|
||||
provider_map[provider_name]
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
fetch(new Request(ApiClient.getUrl("sso/OID/GetNames"))).then((resp) => {
|
||||
resp.json().then((config_names) => {
|
||||
ssoConfigLinking.loadProviderList(provider_list, config_names, "oid");
|
||||
});
|
||||
populateExistingLinks: (
|
||||
container,
|
||||
provider_mode,
|
||||
provider_name,
|
||||
canonical_names
|
||||
) => {
|
||||
container
|
||||
.querySelectorAll(".sso-provider-link-checkbox-wrapper")
|
||||
.forEach((e) => e.remove());
|
||||
|
||||
const checkboxes = canonical_names.map((canonical_name) => {
|
||||
var out = document.createElement("label");
|
||||
out.classList.add("sso-provider-link-checkbox-wrapper");
|
||||
out.classList.add("checkbox-wrapper");
|
||||
out.innerHTML = `
|
||||
<input
|
||||
is="emby-checkbox"
|
||||
class="sso-link-checkbox"
|
||||
data-id="${canonical_name}"
|
||||
data-mode="${provider_mode}"
|
||||
data-provider="${provider_name}"
|
||||
type="checkbox"
|
||||
/>
|
||||
<span class="checkbox-label">${canonical_name}</span>
|
||||
`;
|
||||
return out;
|
||||
});
|
||||
fetch(new Request(ApiClient.getUrl("sso/SAML/GetNames"))).then((resp) => {
|
||||
resp.json().then((config_names) => {
|
||||
ssoConfigLinking.loadProviderList(provider_list, config_names, "saml");
|
||||
});
|
||||
|
||||
checkboxes.forEach((e) => {
|
||||
container.appendChild(e);
|
||||
});
|
||||
},
|
||||
loadProviderList: (element, providers, provider_mode) => {
|
||||
providers.forEach((provider_name) => {
|
||||
var provider_link = document.createElement("a");
|
||||
|
||||
provider_link.classList.add("raised");
|
||||
provider_link.classList.add("block");
|
||||
provider_link.classList.add("emby-button");
|
||||
handleDeleteButtonPressed: (evt, view) => {
|
||||
if (evt.target.disabled) return;
|
||||
|
||||
//const provider_name_css = ssoConfigLinking.safeCSSId(provider_name);
|
||||
//provider_link.id = "sso-provider-" + provider_name_css;
|
||||
//provider_link.classList.add("sso-provider-" + provider_name_css);
|
||||
provider_link.classList.add("sso-provider");
|
||||
const currentUserId = ApiClient.getCurrentUserId();
|
||||
if (!currentUserId) return;
|
||||
|
||||
provider_link.text = provider_name;
|
||||
const delete_requests = [...view.querySelectorAll(".sso-link-checkbox")]
|
||||
.filter((checkbox_link) => {
|
||||
const canonical_name = checkbox_link.getAttribute("data-id");
|
||||
const provider_name = checkbox_link.getAttribute("data-provider");
|
||||
const provider_mode = checkbox_link.getAttribute("data-mode");
|
||||
|
||||
provider_link.href = ApiClient.getUrl(
|
||||
`/SSO/${provider_mode}/p/${provider_name}?isLinking=true`
|
||||
);
|
||||
if (![canonical_name, provider_name, provider_mode].every((e) => e)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
element.appendChild(provider_link);
|
||||
if (!checkbox_link.checked) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((checked_link) => {
|
||||
const canonical_name = checked_link.getAttribute("data-id");
|
||||
const provider_name = checked_link.getAttribute("data-provider");
|
||||
const provider_mode = checked_link.getAttribute("data-mode");
|
||||
|
||||
return ApiClient.fetch({
|
||||
type: "DELETE",
|
||||
url: ApiClient.getUrl(
|
||||
`sso/${provider_mode}/link/${provider_name}/${currentUserId}/${canonical_name}`
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
Promise.all(delete_requests).then((values) => {
|
||||
console.log({ message: "Delete requests handled", values });
|
||||
window.location.reload();
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default function (view) {
|
||||
ssoConfigLinking.loadProviders(view);
|
||||
|
||||
view.querySelector("#enable-delete").addEventListener("change", (e) => {
|
||||
view.querySelector("#btn-delete-selected-links").disabled =
|
||||
!e.target.checked;
|
||||
});
|
||||
|
||||
view
|
||||
.querySelector("#btn-delete-selected-links")
|
||||
.addEventListener("click", (e) =>
|
||||
ssoConfigLinking.handleDeleteButtonPressed(e, view)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
theme.css
|
||||
*/
|
||||
.button-delete {
|
||||
background: rgb(247, 0, 0);
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
}
|
||||
|
||||
.button-delete:disabled {
|
||||
background: rgb(105, 0, 0);
|
||||
color: rgba(127, 127, 127, 0.87);
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Emby Button
|
||||
*/
|
||||
@@ -370,3 +385,65 @@ form {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
/* Fix checkboxes */
|
||||
/* Customize the label (the container) */
|
||||
*/ .checkbox-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.checkbox-wrapper [type="checkbox"] {
|
||||
/*display: none;*/
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
position: relative;
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.checkbox-label:before,
|
||||
.checkbox-label:after {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.checkbox-label:before {
|
||||
display: flex;
|
||||
content: " ";
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border: 0.14em solid white;
|
||||
border-radius: 0.14em;
|
||||
/* background: #fff; */
|
||||
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.checkbox-label:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
content: " ";
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border: 0.14em solid white;
|
||||
border-radius: 0.14em;
|
||||
background: none;
|
||||
font-family: Material Icons;
|
||||
}
|
||||
|
||||
.checkbox-wrapper input[type="checkbox"]:checked + .checkbox-label:after {
|
||||
background-color: #2196f3;
|
||||
content: "\e5ca";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user