Category tree for cms admin ui

Former-commit-id: 081ff7d7c6f9c3814c5a069e42712ed307337464
pull/10/head
Jens Pelzetter 2021-02-13 19:51:54 +01:00
parent d0341133b3
commit 10ce888689
14 changed files with 377 additions and 92 deletions

View File

@ -100,14 +100,29 @@ public class CategoriesController {
}
@GET
@Path("/{key}/categories")
@Path("/{context}")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String showCategorySystemRoot(
@PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("context") final String context
) {
return String.format(
"redirect:/%s/categorysystems/%s/categories",
sectionIdentifier,
context
);
}
@GET
@Path("/{context}/categories")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String showCategorySystem(
@PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("key") final String domainKey
@PathParam("context") final String context
) {
return showCategorySystem(sectionIdentifier, domainKey, "");
return showCategorySystem(sectionIdentifier, context, "");
}
@GET
@ -126,6 +141,7 @@ public class CategoriesController {
return "org/librecms/ui/contentsection/contentsection-not-found.xhtml";
}
final ContentSection section = sectionResult.get();
sectionModel.setSection(section);
final Optional<DomainOwnership> domainResult = section
.getDomains()
@ -153,7 +169,15 @@ public class CategoriesController {
);
final Domain domain = domainResult.get().getDomain();
categorySystemModel.setCategoryTree(buildCategoryTree(domain));
final String activePath;
if (categoryPath.isEmpty()) {
activePath = "/";
} else {
activePath = categoryPath;
}
categorySystemModel.setCategoryTree(
buildCategoryTree(domain, activePath)
);
final Category category;
if (categoryPath.isEmpty()) {
@ -175,71 +199,6 @@ public class CategoriesController {
return "org/librecms/ui/contentsection/categorysystems/categorysystem.xhtml";
}
@POST
@Path("/{context}/categories/{categoryPath:(.+)?}")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String renameCategory(
@PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("context") final String context,
@PathParam("categoryPath") final String categoryPath,
@FormParam("categoryName") final String categoryName
) {
// final Optional<ContentSection> sectionResult = retrieveContentSection(
// sectionIdentifier);
// if (!sectionResult.isPresent()) {
// models.put("sectionIdentifier", sectionIdentifier);
// return "org/librecms/ui/contentsection/contentsection-not-found.xhtml";
// }
// final ContentSection section = sectionResult.get();
//
// final Optional<DomainOwnership> domainResult = section
// .getDomains()
// .stream()
// .filter(domain -> domain.getContext().equals(context))
// .findAny();
// if (!domainResult.isPresent()) {
// models.put("sectionIdentifier", sectionIdentifier);
// models.put("context", context);
// return "org/librecms/ui/contentsection/categorysystems/categorysystem-not-found.xhtml";
// }
// final Domain domain = domainResult.get().getDomain();
if (categoryPath.isEmpty()) {
models.put("sectionIdentifier", sectionIdentifier);
models.put("context", context);
models.put("categoryPath", categoryPath);
return "org/librecms/ui/contentsection/categorysystems/category-not-found.xhtml";
}
// final Category category;
// final Optional<Category> categoryResult = categoryRepo
// .findByPath(domain, categoryPath);
// if (!categoryResult.isPresent()) {
// models.put("sectionIdentifier", sectionIdentifier);
// models.put("context", context);
// models.put("categoryPath", categoryPath);
// return "org/librecms/ui/contentsection/categorysystems/category-not-found.xhtml";
// }
// category = categoryResult.get();
final RetrieveResult<Category> result = retrieveCategory(
sectionIdentifier, context, categoryPath
);
if (result.isSuccessful()) {
final Category category = result.getResult();
category.setName(categoryName);
categoryRepo.save(category);
return String.format(
"redirect:/%s/categorysystems/%s/categories/%s",
sectionIdentifier,
context,
categoryPath
);
} else {
return result.getResponseTemplate();
}
}
@POST
@Path("/{context}/categories/@title/add")
@AuthorizationRequired
@ -485,10 +444,12 @@ public class CategoriesController {
@Path("/{context}/categories/{categoryPath:(.+)?}/@attributes")
@AuthorizationRequired
@Transactional(Transactional.TxType.REQUIRED)
public String updateAttributes(
public String updateCategory(
@PathParam("sectionIdentifier") final String sectionIdentifier,
@PathParam("context") final String context,
@PathParam("categoryPath") final String categoryPath,
@FormParam("categoryName") final String categoryName,
@FormParam("uniqueId") final String uniqueId,
@FormParam("isEnabled") final String isEnabled,
@FormParam("isVisible") final String isVisible,
@FormParam("isAbstract") final String isAbstract
@ -498,15 +459,19 @@ public class CategoriesController {
);
if (result.isSuccessful()) {
final Category category = result.getResult();
category.setName(categoryName);
category.setUniqueId(uniqueId);
category.setEnabled(Objects.equals("true", isEnabled));
category.setVisible(Objects.equals("true", isVisible));
category.setAbstractCategory(Objects.equals("true", isAbstract));
categoryRepo.save(category);
return String.format(
"redirect:/%s/categorysystems/%s/categories/%s",
"redirect:/%s/categorysystems/%s/categories/%s/%s",
sectionIdentifier,
context,
categoryPath
categoryManager.getCategoryPath(category.getParentCategory()),
categoryName
);
} else {
return result.getResponseTemplate();
@ -602,6 +567,7 @@ public class CategoriesController {
@PathParam("context") final String context,
@PathParam("categoryPath") final String categoryPath,
@FormParam("categoryName") final String categoryName,
@FormParam("uniqueId") final String uniqueId,
@FormParam("isEnabled") final String isEnabled,
@FormParam("isVisible") final String isVisible,
@FormParam("isAbstract") final String isAbstract
@ -613,6 +579,7 @@ public class CategoriesController {
final Category category = result.getResult();
final Category subCategory = new Category();
subCategory.setName(categoryName);
subCategory.setUniqueId(uniqueId);
subCategory.setEnabled(Objects.equals("true", isEnabled));
subCategory.setVisible(Objects.equals("true", isVisible));
subCategory.setAbstractCategory(Objects.equals("true", isAbstract));
@ -935,27 +902,39 @@ public class CategoriesController {
return model;
}
private CategoryTreeNodeModel buildCategoryTree(final Domain domain) {
return buildCategoryTreeNode(domain.getRoot());
private CategoryTreeNodeModel buildCategoryTree(
final Domain domain, final String activePath
) {
return buildCategoryTreeNode(domain.getRoot(), activePath);
}
private CategoryTreeNodeModel buildCategoryTreeNode(
final Category category
final Category category, final String activePath
) {
final CategoryTreeNodeModel model = new CategoryTreeNodeModel();
model.setUuid(category.getUuid());
if (category.getTitle().getValues().isEmpty()) {
model.setTitle(category.getName());
} else {
model.setTitle(
globalizationHelper.getValueFromLocalizedString(
category.getTitle()
)
);
model.setPath(categoryManager.getCategoryPath(category));
}
final String path = categoryManager.getCategoryPath(category);
model.setActive(activePath.equals(path));
model.setPath(path);
if (!category.getSubCategories().isEmpty()) {
model.setSubCategories(
category
.getSubCategories()
.stream()
.map(this::buildCategoryTreeNode)
.collect(Collectors.toList())
.map(
subCategory -> buildCategoryTreeNode(
subCategory, activePath
)
).collect(Collectors.toList())
);
}
@ -995,6 +974,7 @@ public class CategoriesController {
)
);
model.setUniqueId(category.getUniqueId());
model.setUuid(category.getUuid());
model.setVisible(category.isVisible());
return model;
}
@ -1018,6 +998,7 @@ public class CategoriesController {
)
);
model.setUniqueId(category.getUniqueId());
model.setUuid(category.getUuid());
model.setVisible(category.isVisible());
return model;
}

View File

@ -17,6 +17,8 @@ public class CategoryModel {
private long categoryId;
private String uuid;
private String uniqueId;
private String name;
@ -52,6 +54,14 @@ public class CategoryModel {
this.categoryId = categoryId;
}
public String getUuid() {
return uuid;
}
public void setUuid(final String uuid) {
this.uuid = uuid;
}
public String getUniqueId() {
return uniqueId;
}

View File

@ -16,6 +16,10 @@ import java.util.List;
*/
public class CategoryTreeNodeModel {
private String uuid;
private boolean active;
private String path;
private String title;
@ -52,4 +56,21 @@ public class CategoryTreeNodeModel {
this.subCategories = new ArrayList<>(subCategories);
}
public String getUuid() {
return uuid;
}
public void setUuid(final String uuid) {
this.uuid = uuid;
}
public boolean isActive() {
return active;
}
public void setActive(final boolean active) {
this.active = active;
}
}

View File

@ -13,7 +13,7 @@
<button class="btn btn-light p-0 subfolders-toggler"
data-toggle="collapse"
data-target="##{folder.name}-subfolders"
aria-expanded="false"
aria-expanded="#{folder.open ? 'true' : 'false'}"
aria-controls="##{folder.name}-subfolders"
type="button">
<span class="sr-only">#{CmsAdminMessages['contentsection.assetfolder.foldersnav.subfolders.expand']}

View File

@ -0,0 +1,27 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/content-section/contentsection.xhtml">
<ui:param name="activePage" value="categorySystems" />
<ui:param name="title" value="#{CmsAdminMessages['contentsection.not_found.title']}" />
<ui:define name="breadcrumb">
<li class="breadcrumb-item">
#{CmsAdminMessages['contentsections.list.label']}
</li>
</ui:define>
<ui:define name="main">
<div class="container">
<div class="alert alert-danger" role="alert">
#{CmsAdminMessages.getMessage('contentsection.categories.category.not_found', [sectionIdentifier, context, categoryPath])}
</div>
</div>
</ui:define>
</ui:composition>
</html>

View File

@ -0,0 +1,44 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:cms="http://xmlns.jcp.org/jsf/composite/components/cms"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition>
<c:choose>
<c:when test="#{not empty category.subCategories}">
<li class="categorytree-node list-group-item"
aria-current="#{category.active ? 'true' : ''}">
<div class="d-flex">
<button class="btn btn-light p-0 subcategories-toggler"
data-toggle="collapse"
data-target="#subcategories-#{category.uuid}"
aria-expanded="#{category.active ? 'true' : 'false'}"
aria-controls="#subcategories-#{category.uuid}"
type="button">
<span class="sr-only">#{CmsAdminMessages['contentsection.categorysystems.categories.tree.expand']}</span>
</button>
<a class="pl-0"
href="#{basePath}#{category.path}">#{category.title}</a>
</div>
<ul class="border-0 #{!category.active ? 'collapse' : 'collapse show'} list-group"
id="subcategories-#{category.uuid}">
<c:forEach items="#{category.subCategories}"
var="subCategory">
<ui:include src="category-tree-node.xhtml">
<ui:param name="basePath" value="#{basePath}" />
<ui:param name="category" value="#{subCategory}" />
</ui:include>
</c:forEach>
</ul>
</li>
</c:when>
<c:otherwise>
<li class="categorytree-node list-group-item #{category.active ? 'active' : ''}">
<a href="#{basePath}#{category.path}">#{category.title}</a>
</li>
</c:otherwise>
</c:choose>
</ui:composition>
</html>

View File

@ -0,0 +1,26 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/content-section/contentsection.xhtml">
<ui:param name="activePage" value="categorySystems" />
<ui:param name="title" value="#{CmsAdminMessages['contentsection.not_found.title']}" />
<ui:define name="breadcrumb">
<li class="breadcrumb-item">
#{CmsAdminMessages['contentsections.list.label']}
</li>
</ui:define>
<ui:define name="main">
<div class="container">
<div class="alert alert-danger" role="alert">
#{CmsAdminMessages.getMessage('contentsection.categories.categorysystem.not_found', [sectionIdentifier, context])}
</div>
</div>
</ui:define>
</ui:composition>
</html>

View File

@ -0,0 +1,123 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:cms="http://xmlns.jcp.org/jsf/composite/components/cms"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/contentsection.xhtml">
<ui:param name="activePage" value="categorySystems" />
<ui:param name="title"
value="#{CmsAdminMessages['contentsection.categories.title']}" />
<ui:define name="breadcrumb">
<li class="breadcrumb-item">
<a href="#{mvc.basePath}/categorysystems">
#{CmsAdminMessages['contentsection.categories.title']}
</a>
</li>
<li aria-current="page" class="breadcrumb-item">
#{CategorySystemModel.selectedCategorySystem.context}
</li>
</ui:define>
<ui:define name="main">
<div class="container-fluid">
<h1>#{CmsAdminMessages.getMessage("contentsection.categories.category.heading", [ContentSectionModel.sectionName, CategorySystemModel.selectedCategorySystem.context, CategorySystemModel.selectedCategory.path])}</h1>
<div class="row">
<div class="col-sm-3">
<nav>
<h2>#{CmsAdminMessages['contentsection.categorysytems.list.heading']}</h2>
<ul class="list-group">
<c:forEach items="#{CategorySystemModel.categorySystems}"
var="categorySystem">
<li class="list-group-item #{CategorySystemModel.selectedCategorySystem.context.equals(categorySystem.context) ? 'active' : ''}"
aria-current="#{CategorySystemModel.selectedCategorySystem.context.equals(categorySystem.context) ? 'true' : ''}">
<a href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/categorysystems/#{categorySystem.context}/categories">
#{categorySystem.context}
</a>
</li>
</c:forEach>
</ul>
</nav>
<h2>#{CmsAdminMessages['contentsection.categorysystems.categorytree.heading']}</h2>
<nav class="categorytree">
<ul class="list-group">
<ui:include src="category-tree-node.xhtml">
<ui:param name="basePath"
value="#{mvc.basePath}/#{ContentSectionModel.sectionName}/categorysystems/#{CategorySystemModel.selectedCategorySystem.context}/categories" />
<ui:param name="category"
value="#{CategorySystemModel.categoryTree}" />
</ui:include>
</ul>
</nav>
</div>
<div class="col-sm-9">
<h2>#{CmsAdminMessages.getMessage("contentsection.categorysystems.category.properties.heading", [CategorySystemModel.selectedCategory.name])}</h2>
<dl>
<div>
<dt>#{CmsAdminMessages['contentsection.categorysystems.category.properties.id']}</dt>
<dd>#{CategorySystemModel.selectedCategory.id}</dd>
</div>
<div>
<dt>#{CmsAdminMessages['contentsection.categorysystems.category.properties.uuid']}</dt>
<dd>#{CategorySystemModel.selectedCategory.uuid}</dd>
</div>
<div>
<dt>#{CmsAdminMessages['contentsection.categorysystems.category.properties.uniqueId']}</dt>
<dd>#{CategorySystemModel.selectedCategory.uniqueId}</dd>
</div>
<div>
<dt>#{CmsAdminMessages['contentsection.categorysystems.category.properties.name']}</dt>
<dd>#{CategorySystemModel.selectedCategory.name}</dd>
</div>
<div>
<dt>#{CmsAdminMessages['contentsection.categorysystems.category.properties.enabled']}</dt>
<dd>
<c:choose>
<c:when test="#{CategorySystemModel.selectedCategory.enabled}">
#{CmsAdminMessages['contentsection.categorysystems.category.properties.enabled.yes']}
</c:when>
<c:otherwise>
#{CmsAdminMessages['contentsection.categorysystems.category.properties.enabled.no']}
</c:otherwise>
</c:choose>
</dd>
</div>
<div>
<dt>#{CmsAdminMessages['contentsection.categorysystems.category.properties.visible']}</dt>
<dd>
<c:choose>
<c:when test="#{CategorySystemModel.selectedCategory.visible}">
#{CmsAdminMessages['contentsection.categorysystems.category.properties.visible.yes']}
</c:when>
<c:otherwise>
#{CmsAdminMessages['contentsection.categorysystems.category.properties.visible.no']}
</c:otherwise>
</c:choose>
</dd>
</div>
<div>
<dt>#{CmsAdminMessages['contentsection.categorysystems.category.properties.abstract_category']}</dt>
<dd>
<c:choose>
<c:when test="#{CategorySystemModel.selectedCategory.abstractCategory}">
#{CmsAdminMessages['contentsection.categorysystems.category.properties.abstract_category.yes']}
</c:when>
<c:otherwise>
#{CmsAdminMessages['contentsection.categorysystems.category.properties.abstract_category.no']}
</c:otherwise>
</c:choose>
</dd>
</div>
</dl>
</div>
</div>
</div>
</ui:define>
</ui:composition>
</html>

View File

@ -8,7 +8,8 @@
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/contentsection.xhtml">
<ui:param name="activePage" value="categorySystems" />
<ui:param name="title" value="#{CmsAdminMessages['contentsection.documentfolders.title']}" />
<ui:param name="title"
value="#{CmsAdminMessages['contentsection.categories.title']}" />
<ui:define name="breadcrumb">
<li aria-current="page" class="breadcrumb-item">
@ -38,7 +39,9 @@
<c:forEach items="#{categorySystems}"
var="categorySystem">
<tr>
<td>#{categorySystem.context}</td>
<td>
<a href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/categorysystems/#{categorySystem.context}/categories">#{categorySystem.context}</a>
</td>
<td>#{categorySystem.domainKey}</td>
<td>#{categorySystem.title}</td>
</tr>

View File

@ -59,7 +59,7 @@
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="#{mvc.basePath}/contentsections/list">
<a href="/@cms/contentsections">
#{CmsAdminMessages['contentsections.list.label']}
</a>
</li>

View File

@ -13,7 +13,7 @@
<button class="btn btn-light p-0 subfolders-toggler"
data-toggle="collapse"
data-target="##{folder.name}-subfolders"
aria-expanded="false"
aria-expanded="#{folder.open ? 'true' : 'false'}"
aria-controls="##{folder.name}-subfolders"
type="button">
<span class="sr-only">#{CmsAdminMessages['contentsection.documentfolder.foldersnav.subfolders.expand']}

View File

@ -145,3 +145,23 @@ contentsection.categories.categorysystems.list.heading.context=Context
contentsection.categories.categorysystems.list.heading.domainKey=Domain Key
contentsection.categories.categorysystems.list.heading.title=Category System Title
contentsection.categories.heading=Category Systems mapped to Content Section {0}
contentsection.categories.categorysystem.not_found=No Category System mapped to Content Section {0} with context {1} available
contentsection.categories.category.not_found=The category system {1} associated with Content Section {0} has no category with the path {2}.
contentsection.categories.category.heading=Category {2} of category system {1} of Content Section {0}
contentsection.categorysytems.list.heading=Category Systems
contentsection.categorysystems.categories.tree.expand=Show subcategories of
contentsection.categorysystems.categorytree.heading=Categories
contentsection.categorysystems.category.properties.heading=Properties of category {0}
contentsection.categorysystems.category.properties.id=ID
contentsection.categorysystems.category.properties.uuid=UUID
contentsection.categorysystems.category.properties.uniqueId=Unique ID
contentsection.categorysystems.category.properties.name=Name
contentsection.categorysystems.category.properties.enabled=Active?
contentsection.categorysystems.category.properties.enabled.yes=Yes
contentsection.categorysystems.category.properties.enabled.no=No
contentsection.categorysystems.category.properties.visible=Visible?
contentsection.categorysystems.category.properties.visible.yes=Yes
contentsection.categorysystems.category.properties.visible.no=No
contentsection.categorysystems.category.properties.abstract_category=Abstract category?
contentsection.categorysystems.category.properties.abstract_category.yes=Yes
contentsection.categorysystems.category.properties.abstract_category.no=No

View File

@ -145,3 +145,23 @@ contentsection.categories.categorysystems.list.heading.context=Kontext
contentsection.categories.categorysystems.list.heading.domainKey=Domain Key
contentsection.categories.categorysystems.list.heading.title=Titel des Kategoriensystems
contentsection.categories.heading=Mit Content Section {0} verbundene Kategoriensysteme
contentsection.categories.categorysystem.not_found=Es gibt kein Kategoriensystem, dass mit dem Kontext {1} mit der Content Section {0} verkn\u00fcpft ist.
contentsection.categories.category.not_found=Das Kategoriensystem {1} der Content Section {0} hat keine Kategorie mit dem Pfad {2}.
contentsection.categories.category.heading=Kategorie {2} des Kategoriensystems {1} der Content Section {0}
contentsection.categorysytems.list.heading=Kategoriensysteme
contentsection.categorysystems.categories.tree.expand=Zeige Sub-Kategorien von
contentsection.categorysystems.categorytree.heading=Kategorien
contentsection.categorysystems.category.properties.heading=Eigenschaften Kategorie {0}
contentsection.categorysystems.category.properties.id=ID
contentsection.categorysystems.category.properties.uuid=UUID
contentsection.categorysystems.category.properties.uniqueId=Eindeutige ID
contentsection.categorysystems.category.properties.name=Name
contentsection.categorysystems.category.properties.enabled=Aktiv?
contentsection.categorysystems.category.properties.enabled.yes=Ja
contentsection.categorysystems.category.properties.enabled.no=Nein
contentsection.categorysystems.category.properties.visible=Sichtbar?
contentsection.categorysystems.category.properties.visible.yes=Ja
contentsection.categorysystems.category.properties.visible.no=Nein
contentsection.categorysystems.category.properties.abstract_category=Abstrakte Kategorie?
contentsection.categorysystems.category.properties.abstract_category.yes=Ja
contentsection.categorysystems.category.properties.abstract_category.no=Nein

View File

@ -38,7 +38,15 @@ $pre-scrollable-max-height: 21.25rem;
// Navbar default colors have insufficient contrast
$navbar-dark-color: #fff;
nav.foldertree {
nav ul .list-group-item.active a {
color: #fff;
}
nav ul .list-group-item.active ul .list-group-item a {
color: #007bff;
}
nav.foldertree,nav.categorytree {
& > ul.nav {
margin-left: 1em;
margin-right: 1em;
@ -48,6 +56,7 @@ nav.foldertree {
}
}
button.subcategories-toggler[aria-expanded="false"]::before,
button.subfolders-toggler[aria-expanded="false"]::before {
display: inline-block;
content: "";
@ -58,6 +67,7 @@ nav.foldertree {
height: 1em;
}
button.subcategories-toggler[aria-expanded="true"]::before,
button.subfolders-toggler[aria-expanded="true"]::before {
display: inline-block;
content: "";