mirror of
https://git.proxmox.com/git/proxmox
synced 2025-10-26 02:05:54 +00:00
Instead of passing the template strings for subject and body when constructing a notification, we pass only the name of a template. When rendering the template, the name of the template is used to find corresponding template files. For PVE, they are located at /usr/share/proxmox-ve/templates/default. The `default` part is the 'template namespace', which is a preparation for user-customizable and/or translatable notifications. Previously, the same template string was used to render HTML and plaintext notifications. This was achieved by providing some template helpers that 'abstract away' HTML/plaintext formatting. However, in hindsight this turned out to be pretty finicky. Since the current changes lay the foundations for user-customizable notification templates, I ripped these abstractions out. Now there are simply two templates, one for plaintext, one for HTML. Signed-off-by: Lukas Wagner <l.wagner@proxmox.com> Tested-by: Folke Gleumes <f.gleumes@proxmox.com> Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
104 lines
2.6 KiB
Rust
104 lines
2.6 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use handlebars::{
|
|
Context, Handlebars, Helper, HelperResult, Output, RenderContext,
|
|
RenderError as HandlebarsRenderError,
|
|
};
|
|
use serde_json::Value;
|
|
|
|
use super::{table::Table, value_to_string};
|
|
use crate::renderer::BlockRenderFunctions;
|
|
|
|
fn optimal_column_widths(table: &Table) -> HashMap<&str, usize> {
|
|
let mut widths = HashMap::new();
|
|
|
|
for column in &table.schema.columns {
|
|
let mut min_width = column.label.len();
|
|
|
|
for row in &table.data {
|
|
let entry = row.get(&column.id).unwrap_or(&Value::Null);
|
|
|
|
let text = if let Some(renderer) = &column.renderer {
|
|
renderer.render(entry)
|
|
} else {
|
|
value_to_string(entry)
|
|
};
|
|
|
|
min_width = std::cmp::max(text.len(), min_width);
|
|
}
|
|
|
|
widths.insert(column.label.as_str(), min_width + 4);
|
|
}
|
|
|
|
widths
|
|
}
|
|
|
|
fn render_plaintext_table(
|
|
h: &Helper,
|
|
_: &Handlebars,
|
|
_: &Context,
|
|
_: &mut RenderContext,
|
|
out: &mut dyn Output,
|
|
) -> HelperResult {
|
|
let param = h
|
|
.param(0)
|
|
.ok_or_else(|| HandlebarsRenderError::new("parameter not found"))?;
|
|
let value = param.value();
|
|
let table: Table = serde_json::from_value(value.clone())?;
|
|
let widths = optimal_column_widths(&table);
|
|
|
|
// Write header
|
|
for column in &table.schema.columns {
|
|
let width = widths.get(column.label.as_str()).unwrap_or(&0);
|
|
out.write(&format!("{label:width$}", label = column.label))?;
|
|
}
|
|
|
|
out.write("\n")?;
|
|
|
|
// Write individual rows
|
|
for row in &table.data {
|
|
for column in &table.schema.columns {
|
|
let entry = row.get(&column.id).unwrap_or(&Value::Null);
|
|
let width = widths.get(column.label.as_str()).unwrap_or(&0);
|
|
|
|
let text = if let Some(renderer) = &column.renderer {
|
|
renderer.render(entry)
|
|
} else {
|
|
value_to_string(entry)
|
|
};
|
|
|
|
out.write(&format!("{text:width$}",))?;
|
|
}
|
|
out.write("\n")?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn render_object(
|
|
h: &Helper,
|
|
_: &Handlebars,
|
|
_: &Context,
|
|
_: &mut RenderContext,
|
|
out: &mut dyn Output,
|
|
) -> HelperResult {
|
|
let param = h
|
|
.param(0)
|
|
.ok_or_else(|| HandlebarsRenderError::new("parameter not found"))?;
|
|
|
|
let value = param.value();
|
|
|
|
out.write("\n")?;
|
|
out.write(&serde_json::to_string_pretty(&value)?)?;
|
|
out.write("\n")?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub(super) fn block_render_functions() -> BlockRenderFunctions {
|
|
BlockRenderFunctions {
|
|
table: Box::new(render_plaintext_table),
|
|
object: Box::new(render_object),
|
|
}
|
|
}
|