Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 0 additions & 119 deletions lib/schema/graph.ex

This file was deleted.

90 changes: 58 additions & 32 deletions lib/schema_web/controllers/page_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -116,22 +116,6 @@ defmodule SchemaWeb.PageController do
end
end

@spec class_graph(Plug.Conn.t(), any) :: Plug.Conn.t()
def class_graph(conn, params) do
case SchemaController.class_ex(params) do
nil ->
send_resp(conn, 404, "Not Found: #{SchemaController.params_to_uid(params)}")

class ->
data = Schema.Graph.build(class)

render(conn, "class_graph.html",
extensions: Schema.extensions(),
profiles: get_profiles(params),
data: data
)
end
end

@doc """
Redirects from the older /base_event URL to /classes/base_event.
Expand Down Expand Up @@ -179,22 +163,6 @@ defmodule SchemaWeb.PageController do
end
end

@spec object_graph(Plug.Conn.t(), any) :: Plug.Conn.t()
def object_graph(conn, params) do
case SchemaController.object_ex(params) do
nil ->
send_resp(conn, 404, "Not Found: #{SchemaController.params_to_uid(params)}")

obj ->
data = Schema.Graph.build(obj)

render(conn, "object_graph.html",
extensions: Schema.extensions(),
profiles: get_profiles(params),
data: data
)
end
end

@spec dictionary(Plug.Conn.t(), any) :: Plug.Conn.t()
def dictionary(conn, params) do
Expand Down Expand Up @@ -226,6 +194,64 @@ defmodule SchemaWeb.PageController do
)
end

@spec visualizer(Plug.Conn.t(), any) :: Plug.Conn.t()
def visualizer(conn, params) do
schema = Schema.schema()
profiles = parse_profiles_from_params(params)
extensions = SchemaController.parse_options(SchemaController.extensions(params))

cond do
params["class"] ->
id = String.to_atom(params["class"])
case Schema.class_filter_profiles(schema, id, profiles) do
nil ->
send_resp(conn, 404, "Not Found: #{params["class"]}")

class ->
render(conn, "visualizer.html",
extensions: schema[:extensions],
profiles: get_profiles(params),
scope_data: Map.put(class, :scope_type, :class)
)
end

params["object"] ->
id = String.to_atom(params["object"])
case Schema.object_filter_extensions_profiles(schema, id, extensions, profiles) do
nil ->
send_resp(conn, 404, "Not Found: #{params["object"]}")

obj ->
render(conn, "visualizer.html",
extensions: schema[:extensions],
profiles: get_profiles(params),
scope_data: Map.put(obj, :scope_type, :object)
)
end

params["category"] ->
id = String.to_atom(params["category"])
case Schema.SingleRepo.categories()[:attributes][id] do
nil ->
send_resp(conn, 404, "Not Found: #{params["category"]}")

cat ->
scope_data = cat
|> Map.put(:scope_type, :category)
|> Map.put_new(:name, params["category"])

render(conn, "visualizer.html",
extensions: Schema.extensions(),
profiles: get_profiles(params),
scope_data: scope_data
)
end

true ->
redirect(conn, to: Routes.static_path(conn, "/classes"))
end
end

defp sort_classes(categories) do
Map.update!(categories, :attributes, fn list ->
Enum.map(list, fn {name, category} ->
Expand Down
5 changes: 1 addition & 4 deletions lib/schema_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,17 @@ defmodule SchemaWeb.Router do
get "/classes/:id", PageController, :class_by_id
get "/classes/:extension/:id", PageController, :class_by_id

get "/class/graph/:id", PageController, :class_graph
get "/class/graph/:extension/:id", PageController, :class_graph

get "/base_event", PageController, :base_event

get "/objects", PageController, :objects
get "/objects/:id", PageController, :object_by_id
get "/objects/:extension/:id", PageController, :object_by_id

get "/object/graph/:id", PageController, :object_graph
get "/object/graph/:extension/:id", PageController, :object_graph

get "/dictionary", PageController, :dictionary
get "/data_types", PageController, :data_types
get "/visualizer", PageController, :visualizer
end

# Other scopes may use custom stacks.
Expand Down
86 changes: 63 additions & 23 deletions lib/schema_web/templates/layout/app.html.eex
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ limitations under the License.
<meta name="description" content="Open Cybersecurity Schema Framework">
<title>Open Cybersecurity Schema Framework</title>

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">

<link rel="stylesheet" href='<%= Routes.static_path(@conn, "/css/bootstrap-4.6.2.min.css") %>'/>
<link rel="stylesheet" href='<%= Routes.static_path(@conn, "/css/bootstrap.select-1.13.18.min.css") %>'/>
<link rel="stylesheet" href='<%= Routes.static_path(@conn, "/css/fontawesome-6.4.0.min.css") %>'/>
Expand Down Expand Up @@ -57,12 +61,13 @@ limitations under the License.
// Update content links (class, object, category, profile links in page body)
$(".main-page a").each(function() {
const href = this.getAttribute('href');
if (!href || href.startsWith('http') || href.startsWith('#') || href.startsWith('javascript:') || href.includes('?')) {
if (!href || href.startsWith('http') || href.startsWith('#') || href.startsWith('javascript:')) {
return;
}
// Only update internal schema navigation links
if (href.match(/^\/(classes|objects|categories|profiles|dictionary|data_types)/)) {
this.href = this.href + params;
if (href.match(/^\/(classes|objects|categories|profiles|dictionary|data_types|visualizer)/)) {
const joiner = href.includes('?') ? '&' : '';
this.href = this.href + (joiner ? params.replace('?', '&') : params);
}
});

Expand All @@ -86,7 +91,16 @@ limitations under the License.
// Get current profiles and build complete URL
const selected_profiles = get_selected_profiles();
const params = build_url_params(selected_extensions, selected_profiles);
window.location.search = params;

// Preserve non-filter query params (e.g. class=X, object=X)
const currentParams = new URLSearchParams(window.location.search);
const newParams = new URLSearchParams(params.replace('?', ''));
currentParams.forEach(function(value, key) {
if (key !== 'extensions' && key !== 'profiles') {
newParams.set(key, value);
}
});
window.location.search = '?' + newParams.toString();
});

// init the attributes dropdown filters
Expand Down Expand Up @@ -134,16 +148,6 @@ limitations under the License.
<img src='<%= Routes.static_path(@conn, "/images/ocsf-logo.png") %>' alt="OCSF" height="32" class="d-inline-block align-top">
</a>

<!-- Theme Toggle - Always Visible -->
<div class="navbar-theme-toggle-container">
<label class="theme-toggle-switch navbar-theme-toggle">
<input type="checkbox" id="theme-toggle" onclick="toggleTheme()" title="Toggle dark mode">
<span class="theme-toggle-slider">
<i class="fas fa-sun theme-icon-light"></i>
<i class="fas fa-moon theme-icon-dark"></i>
</span>
</label>
</div>

<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<i class="fas fa-chevron-down"></i>
Expand Down Expand Up @@ -237,15 +241,28 @@ limitations under the License.
<i class="fas fa-cog"></i> Options
<hr class="divider"/>
</div>
<div class="sidebar-section-content">
<ul class="sidebar-nav">
<li class="sidebar-nav-item">
<label>
<input type="checkbox" id="show-deprecated-global" class="show-deprecated-checkbox" data-toggle="collapse" data-target=".deprecated" onclick="on_click_show_deprecated(this)">
Show deprecated items
</label>
</li>
</ul>
<div class="sidebar-section-content sidebar-options">
<div class="pill-group">
<span class="pill-label">Dark mode</span>
<label class="pill-toggle">
<input type="checkbox" id="theme-toggle" onclick="toggleTheme(); updateThemePill()">
<span class="pill-slider"></span>
</label>
</div>
<div class="pill-group">
<span class="pill-label">Compact view</span>
<label class="pill-toggle">
<input type="checkbox" id="compact-view-toggle" onclick="toggleCompactView(this)" checked>
<span class="pill-slider"></span>
</label>
</div>
<div class="pill-group">
<span class="pill-label">Show deprecated</span>
<label class="pill-toggle">
<input type="checkbox" id="show-deprecated-global" class="show-deprecated-checkbox" data-toggle="collapse" data-target=".deprecated" onclick="on_click_show_deprecated(this)">
<span class="pill-slider"></span>
</label>
</div>
</div>
</div>
</div>
Expand Down Expand Up @@ -352,6 +369,29 @@ limitations under the License.
window.clearProfileHighlighting = function() {
$('.profile-item').removeClass('profile-applicable profile-not-applicable');
};

// Pill toggle helpers
function updateThemePill() {
const theme = document.documentElement.getAttribute('data-theme');
document.getElementById('theme-toggle').checked = (theme === 'dark');
}

function toggleCompactView(el) {
document.body.classList.toggle('full-width-mode', !el.checked);
localStorage.setItem('schema_compact_view', el.checked);
}

// Initialize pill states
(function() {
// Theme pill
updateThemePill();

// Compact view
const isCompact = localStorage.getItem('schema_compact_view');
const compact = isCompact === null ? true : isCompact === 'true';
document.getElementById('compact-view-toggle').checked = compact;
if (!compact) document.body.classList.add('full-width-mode');
})();
</script>

</body>
Expand Down
8 changes: 8 additions & 0 deletions lib/schema_web/templates/page/category.html.eex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ limitations under the License.
</div>
</div>

<div class="row align-items-center mb-3">
<div class="col-auto">
<div class="btn-group" role="group" aria-label="Schema actions">
<a href="<%= Routes.static_path(@conn, "/visualizer?category=#{@data[:name]}") %>" id="btn-visualizer" class="btn btn-sm btn-outline-primary" title="View relationship graph">Visualizer</a>
</div>
</div>
</div>

<div class="mt-4">
<table class="table table-bordered sortable">
<thead>
Expand Down
Loading
Loading