Finish implementing linking view page

This commit is contained in:
Matthew Strasitoto
2022-08-14 13:13:07 +10:00
parent d72e47e7a7
commit fc888de045
3 changed files with 270 additions and 34 deletions
+40 -7
View File
@@ -1,8 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<!-- Polyfill styles that are missing when serving without dashboard -->
<link rel="stylesheet" href="emby-restyle.css" /> <link rel="stylesheet" href="emby-restyle.css" />
<link id="theme-style" rel="stylesheet" /> <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> <script defer>
import("./ApiClient.js").then((clientModule) => { import("./ApiClient.js").then((clientModule) => {
import("./linking.js").then((renderer) => { import("./linking.js").then((renderer) => {
@@ -12,8 +20,9 @@
"/web/themes/dark/theme.css" "/web/themes/dark/theme.css"
); );
document.querySelector("a.emby-button.home").href = const homeButton = document.querySelector("a.emby-button.home");
ApiClient.serverAddress(); homeButton.href = ApiClient.serverAddress();
homeButton.style.display = "";
renderer.default(view); renderer.default(view);
}); });
}); });
@@ -30,7 +39,7 @@
<div data-role="content"> <div data-role="content">
<div class="content-primary"> <div class="content-primary">
<div class="sectionTitleContainer flex align-items-center"> <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 class="material-icons home" aria-hidden="true"></span
><span>Home</span> ><span>Home</span>
</a> </a>
@@ -44,9 +53,16 @@
> >
</div> </div>
<p> <p>
Use the following buttons to link your jellyfin account to any one Use the
of the SSO providers. Please note that deletion is currently only <span
available by API. 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>
<p> <p>
See the See the
@@ -65,9 +81,26 @@
</a> </a>
for more information. for more information.
</p> </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 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> </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> </div>
</div> </div>
+153 -27
View File
@@ -2,9 +2,69 @@ const ssoConfigLinking = {
pluginUniqueId: "505ce9d1-d916-42fa-86ca-673ef241d7df", pluginUniqueId: "505ce9d1-d916-42fa-86ca-673ef241d7df",
loadProviders: (view) => { loadProviders: (view) => {
const provider_list_id = "sso-provider-list"; 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); const provider_list_saml = view.querySelector(`#${provider_list_saml_id}`);
provider_list.innerHTML = ""; 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(); const currentUserId = ApiClient.getCurrentUserId();
@@ -12,49 +72,115 @@ const ssoConfigLinking = {
ApiClient.fetch( ApiClient.fetch(
{ {
type: "GET", type: "GET",
url: ApiClient.getUrl(`sso/OID/links/${currentUserId}`), url: ApiClient.getUrl(`sso/${provider_mode}/links/${currentUserId}`),
}, },
true true
).then((resp) => { ).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) => { populateExistingLinks: (
resp.json().then((config_names) => { container,
ssoConfigLinking.loadProviderList(provider_list, config_names, "oid"); 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) => { checkboxes.forEach((e) => {
ssoConfigLinking.loadProviderList(provider_list, config_names, "saml"); container.appendChild(e);
});
}); });
}, },
loadProviderList: (element, providers, provider_mode) => {
providers.forEach((provider_name) => {
var provider_link = document.createElement("a");
provider_link.classList.add("raised"); handleDeleteButtonPressed: (evt, view) => {
provider_link.classList.add("block"); if (evt.target.disabled) return;
provider_link.classList.add("emby-button");
//const provider_name_css = ssoConfigLinking.safeCSSId(provider_name); const currentUserId = ApiClient.getCurrentUserId();
//provider_link.id = "sso-provider-" + provider_name_css; if (!currentUserId) return;
//provider_link.classList.add("sso-provider-" + provider_name_css);
provider_link.classList.add("sso-provider");
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( if (![canonical_name, provider_name, provider_mode].every((e) => e)) {
`/SSO/${provider_mode}/p/${provider_name}?isLinking=true` 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) { export default function (view) {
ssoConfigLinking.loadProviders(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)
);
} }
+77
View File
@@ -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 Emby Button
*/ */
@@ -370,3 +385,65 @@ form {
margin-top: auto; margin-top: auto;
margin-bottom: 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";
}