public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH proxmox-backup v2 0/4] add 'show information' for snapshots
@ 2023-12-05 10:53 Dominik Csapak
  2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 1/4] api-types: add an UploadStatistic api type Dominik Csapak
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Dominik Csapak @ 2023-12-05 10:53 UTC (permalink / raw)
  To: pbs-devel

includes the upload statistics. This also moves some actions from the
column directly into a 'hamburger menu' so we don't overload the grid.

The only downside to this currently is that i could get keyboard
navigation to work for the context menu or the hamburger menu, but i'm
still looking into this.

changes from v1:
* renamed 'group-notes' to 'group-comment' to be consistent with the
  'comment' field
* added the (group)comment as tabs where the files lists are
* moved the files/comment tabpanel to the bottom
* made the window a bit wider, and put the upload statistics and
  verification info side by side, this way we save a bit vertical space
  (needed since the tabpanel takes a bit more)

continuation for the context menu series[0]
(changes title due to the context menu patch being applied)

0: https://lists.proxmox.com/pipermail/pbs-devel/2023-November/007248.html

Dominik Csapak (4):
  api-types: add an UploadStatistic api type
  api: datastore admin: add 'snapshot-information' api call
  ui: datastore content: add snapshot information to context menu
  ui: datastore content: add 'more actions' menu to actions

 pbs-api-types/src/datastore.rs |  40 +++++
 src/api2/admin/datastore.rs    | 117 ++++++++++++++-
 www/Makefile                   |   1 +
 www/datastore/Content.js       | 130 +++++++++++------
 www/window/SnapshotInfo.js     | 259 +++++++++++++++++++++++++++++++++
 5 files changed, 493 insertions(+), 54 deletions(-)
 create mode 100644 www/window/SnapshotInfo.js

-- 
2.30.2





^ permalink raw reply	[flat|nested] 5+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v2 1/4] api-types: add an UploadStatistic api type
  2023-12-05 10:53 [pbs-devel] [PATCH proxmox-backup v2 0/4] add 'show information' for snapshots Dominik Csapak
@ 2023-12-05 10:53 ` Dominik Csapak
  2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 2/4] api: datastore admin: add 'snapshot-information' api call Dominik Csapak
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Dominik Csapak @ 2023-12-05 10:53 UTC (permalink / raw)
  To: pbs-devel

We'll want this to expose on the api, but the internal type used in the
backup environment does not use kebab case, so simply add a new type

I opted for a new type instead of modifying the existing one, so that we
keep the casing for the options in the manifest the same.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 pbs-api-types/src/datastore.rs | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/pbs-api-types/src/datastore.rs b/pbs-api-types/src/datastore.rs
index d4ead1d1..52f83f48 100644
--- a/pbs-api-types/src/datastore.rs
+++ b/pbs-api-types/src/datastore.rs
@@ -421,6 +421,22 @@ pub struct SnapshotVerifyState {
     pub state: VerifyState,
 }
 
+#[api()]
+#[derive(Copy, Clone, Serialize, Deserialize, PartialEq)]
+#[serde(rename_all = "kebab-case")]
+/// Chunk upload statistics of a snapshot
+pub struct UploadStatistic {
+    /// Amount of chunks uploaded (incl. duplicates)
+    pub count: u64,
+    /// Uncompressed bytes uploaded
+    pub size: u64,
+    /// Compressed bytes uploaded
+    #[serde(alias = "compressed_size")]
+    pub compressed_size: u64,
+    /// Amount of duplicate chunks uploaded
+    pub duplicates: u64,
+}
+
 /// A namespace provides a logical separation between backup groups from different domains
 /// (cluster, sites, ...) where uniqueness cannot be guaranteed anymore. It allows users to share a
 /// datastore (i.e., one deduplication domain (chunk store)) with multiple (trusted) sites and
-- 
2.30.2





^ permalink raw reply	[flat|nested] 5+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v2 2/4] api: datastore admin: add 'snapshot-information' api call
  2023-12-05 10:53 [pbs-devel] [PATCH proxmox-backup v2 0/4] add 'show information' for snapshots Dominik Csapak
  2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 1/4] api-types: add an UploadStatistic api type Dominik Csapak
@ 2023-12-05 10:53 ` Dominik Csapak
  2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 3/4] ui: datastore content: add snapshot information to context menu Dominik Csapak
  2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 4/4] ui: datastore content: add 'more actions' menu to actions Dominik Csapak
  3 siblings, 0 replies; 5+ messages in thread
From: Dominik Csapak @ 2023-12-05 10:53 UTC (permalink / raw)
  To: pbs-devel

get information about a snapshot. This is intended to contain all
relevant information about a snapshot, so that we don't fill the
'list_snapshots' api call with further info

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 pbs-api-types/src/datastore.rs |  24 +++++++
 src/api2/admin/datastore.rs    | 117 +++++++++++++++++++++++++++++++--
 2 files changed, 135 insertions(+), 6 deletions(-)

diff --git a/pbs-api-types/src/datastore.rs b/pbs-api-types/src/datastore.rs
index 52f83f48..76d1811f 100644
--- a/pbs-api-types/src/datastore.rs
+++ b/pbs-api-types/src/datastore.rs
@@ -421,6 +421,30 @@ pub struct SnapshotVerifyState {
     pub state: VerifyState,
 }
 
+#[api(
+    properties: {
+        info : {
+            type: SnapshotListItem,
+        },
+        "upload-statistics": {
+            type: UploadStatistic,
+            optional: true,
+        },
+    },
+)]
+#[derive(Serialize, Deserialize, Clone, PartialEq)]
+#[serde(rename_all = "kebab-case")]
+/// Information about a Snapshot
+pub struct SnapshotInformation {
+    #[serde(flatten)]
+    pub info: SnapshotListItem,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub upload_statistics: Option<UploadStatistic>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    /// "notes" for the whole backup group
+    pub group_comment: Option<String>,
+}
+
 #[api()]
 #[derive(Copy, Clone, Serialize, Deserialize, PartialEq)]
 #[serde(rename_all = "kebab-case")]
diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs
index a95031e7..f6fcb8ff 100644
--- a/src/api2/admin/datastore.rs
+++ b/src/api2/admin/datastore.rs
@@ -34,12 +34,12 @@ use pxar::EntryKind;
 use pbs_api_types::{
     print_ns_and_snapshot, print_store_and_ns, Authid, BackupContent, BackupNamespace, BackupType,
     Counts, CryptMode, DataStoreListItem, DataStoreStatus, GarbageCollectionStatus, GroupListItem,
-    KeepOptions, Operation, PruneJobOptions, RRDMode, RRDTimeFrame, SnapshotListItem,
-    SnapshotVerifyState, BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA,
-    BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, DATASTORE_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA,
-    MAX_NAMESPACE_DEPTH, NS_MAX_DEPTH_SCHEMA, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP,
-    PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE, PRIV_DATASTORE_READ, PRIV_DATASTORE_VERIFY,
-    UPID_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA,
+    KeepOptions, Operation, PruneJobOptions, RRDMode, RRDTimeFrame, SnapshotInformation,
+    SnapshotListItem, SnapshotVerifyState, UploadStatistic, BACKUP_ARCHIVE_NAME_SCHEMA,
+    BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA,
+    DATASTORE_SCHEMA, IGNORE_VERIFIED_BACKUPS_SCHEMA, MAX_NAMESPACE_DEPTH, NS_MAX_DEPTH_SCHEMA,
+    PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_PRUNE,
+    PRIV_DATASTORE_READ, PRIV_DATASTORE_VERIFY, UPID_SCHEMA, VERIFICATION_OUTDATED_AFTER_SCHEMA,
 };
 use pbs_client::pxar::{create_tar, create_zip};
 use pbs_config::CachedUserInfo;
@@ -2239,6 +2239,107 @@ pub async fn set_backup_owner(
     .await?
 }
 
+#[api(
+    input: {
+        properties: {
+            store: { schema: DATASTORE_SCHEMA },
+            ns: {
+                type: BackupNamespace,
+                optional: true,
+            },
+            backup_dir: {
+                type: pbs_api_types::BackupDir,
+                flatten: true,
+            },
+        },
+    },
+    access: {
+        permission: &Permission::Anybody,
+        description: "Requires on /datastore/{store}[/{namespace}] either DATASTORE_AUDIT for any \
+            or DATASTORE_BACKUP and being the owner of the group",
+    },
+)]
+/// Get Information about a snapshot
+pub fn get_snapshot_info(
+    store: String,
+    ns: Option<BackupNamespace>,
+    backup_dir: pbs_api_types::BackupDir,
+    rpcenv: &mut dyn RpcEnvironment,
+) -> Result<SnapshotInformation, Error> {
+    let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
+    let ns = ns.unwrap_or_default();
+
+    let datastore = check_privs_and_load_store(
+        &store,
+        &ns,
+        &auth_id,
+        PRIV_DATASTORE_AUDIT,
+        PRIV_DATASTORE_BACKUP,
+        Some(Operation::Read),
+        &backup_dir.group,
+    )?;
+
+    let group = datastore.backup_group(ns.clone(), backup_dir.group.clone());
+    let owner = group.get_owner()?;
+    let backup = datastore.backup_dir(ns.clone(), backup_dir.clone())?;
+
+    let protected = backup.is_protected();
+    let backup_info = BackupInfo::new(backup)?;
+
+    let (manifest, files) = get_all_snapshot_files(&backup_info)?;
+    let comment: Option<String> = manifest.unprotected["notes"].as_str().map(String::from);
+
+    let fingerprint = match manifest.fingerprint() {
+        Ok(fp) => fp,
+        Err(err) => {
+            eprintln!("error parsing fingerprint: '{}'", err);
+            None
+        }
+    };
+
+    let verification = manifest.unprotected["verify_state"].clone();
+    let verification: Option<SnapshotVerifyState> = match serde_json::from_value(verification) {
+        Ok(verify) => verify,
+        Err(err) => {
+            eprintln!("error parsing verification state : '{}'", err);
+            None
+        }
+    };
+
+    let size = Some(files.iter().map(|x| x.size.unwrap_or(0)).sum());
+
+    let group = backup_dir.group.clone();
+
+    let info = SnapshotListItem {
+        backup: backup_dir,
+        comment,
+        verification,
+        fingerprint,
+        files,
+        size,
+        owner: Some(owner),
+        protected,
+    };
+
+    let upload_statistics =
+        match Option::<UploadStatistic>::deserialize(&manifest.unprotected["chunk_upload_stats"]) {
+            Ok(stats) => stats,
+            Err(err) => {
+                log::warn!("error parsing chunk_upload_stats: {err}");
+                None
+            }
+        };
+
+    let note_path = get_group_note_path(&datastore, &ns, &group);
+    let group_comment = file_read_optional_string(note_path)?;
+
+    Ok(SnapshotInformation {
+        info,
+        upload_statistics,
+        group_comment,
+    })
+}
+
 #[sortable]
 const DATASTORE_INFO_SUBDIRS: SubdirMap = &[
     (
@@ -2304,6 +2405,10 @@ const DATASTORE_INFO_SUBDIRS: SubdirMap = &[
         &Router::new().download(&API_METHOD_PXAR_FILE_DOWNLOAD),
     ),
     ("rrd", &Router::new().get(&API_METHOD_GET_RRD_STATS)),
+    (
+        "snapshot-information",
+        &Router::new().get(&API_METHOD_GET_SNAPSHOT_INFO),
+    ),
     (
         "snapshots",
         &Router::new()
-- 
2.30.2





^ permalink raw reply	[flat|nested] 5+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v2 3/4] ui: datastore content: add snapshot information to context menu
  2023-12-05 10:53 [pbs-devel] [PATCH proxmox-backup v2 0/4] add 'show information' for snapshots Dominik Csapak
  2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 1/4] api-types: add an UploadStatistic api type Dominik Csapak
  2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 2/4] api: datastore admin: add 'snapshot-information' api call Dominik Csapak
@ 2023-12-05 10:53 ` Dominik Csapak
  2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 4/4] ui: datastore content: add 'more actions' menu to actions Dominik Csapak
  3 siblings, 0 replies; 5+ messages in thread
From: Dominik Csapak @ 2023-12-05 10:53 UTC (permalink / raw)
  To: pbs-devel

contains a summary of snapshot information, especially interesting since
it contains the upload statistics. This may allow us to reduce the
default column count in the grid.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/Makefile               |   1 +
 www/datastore/Content.js   |  22 ++++
 www/window/SnapshotInfo.js | 259 +++++++++++++++++++++++++++++++++++++
 3 files changed, 282 insertions(+)
 create mode 100644 www/window/SnapshotInfo.js

diff --git a/www/Makefile b/www/Makefile
index be7e27ab..071ef5d8 100644
--- a/www/Makefile
+++ b/www/Makefile
@@ -87,6 +87,7 @@ JSSRC=							\
 	window/ZFSCreate.js				\
 	window/InfluxDbEdit.js				\
 	window/DatastoreRepoInfo.js			\
+	window/SnapshotInfo.js				\
 	dashboard/DataStoreStatistics.js		\
 	dashboard/LongestTasks.js			\
 	dashboard/RunningTasks.js			\
diff --git a/www/datastore/Content.js b/www/datastore/Content.js
index 87317ec1..f715f814 100644
--- a/www/datastore/Content.js
+++ b/www/datastore/Content.js
@@ -712,6 +712,17 @@ Ext.define('PBS.DataStoreContent', {
 	    });
 	},
 
+	onShowInformation: function(view, rI, cI, item, e, rec) {
+	    let me = this;
+	    Ext.create('PBS.window.SnapshotInfo', {
+		autoShow: true,
+		datastore: view.datastore,
+		'backup-type': rec.data['backup-type'],
+		'backup-id': rec.data['backup-id'],
+		'backup-time': (rec.data["backup-time"].getTime()/1000).toFixed(0),
+	    });
+	},
+
 	onForget: function(table, rI, cI, item, e, { data }) {
 	    let me = this;
 	    let view = this.getView();
@@ -893,6 +904,7 @@ Ext.define('PBS.DataStoreContent', {
 		    title: gettext('Snapshot'),
 		    onVerify: createControllerCallback('onVerify'),
 		    onProtectionChange: createControllerCallback('onProtectionChange'),
+		    onShowInformation: createControllerCallback('onShowInformation'),
 		    onForget: createControllerCallback('onForget'),
 		});
 	    }
@@ -1331,6 +1343,7 @@ Ext.define('PBS.datastore.SnapshotCmdMenu', {
 
     onVerify: undefined,
     onProtectionChange: undefined,
+    onShowInformation: undefined,
     onForget: undefined,
 
     items: [
@@ -1352,6 +1365,15 @@ Ext.define('PBS.datastore.SnapshotCmdMenu', {
 		disabled: '{!onProtectionChange}',
 	    },
 	},
+	{
+	    text: gettext('Show Information'),
+	    iconCls: 'fa fa-info-circle',
+	    handler: function() { this.up('menu').onShowInformation(); },
+	    cbind: {
+		hidden: '{!onShowInformation}',
+		disabled: '{!onShowInformation}',
+	    },
+	},
 	{ xtype: 'menuseparator' },
 	{
 	    text: gettext('Remove'),
diff --git a/www/window/SnapshotInfo.js b/www/window/SnapshotInfo.js
new file mode 100644
index 00000000..f2cc7a55
--- /dev/null
+++ b/www/window/SnapshotInfo.js
@@ -0,0 +1,259 @@
+Ext.define('PBS.window.SnapshotInfo', {
+    extend: 'Ext.window.Window',
+    alias: 'widget.pbsSnapshotInfo',
+
+    modal: true,
+    width: 700,
+    resizable: true,
+    referenceHolder: true,
+
+    bodyPadding: 25,
+    defaults: {
+	xtype: 'pmxInfoWidget',
+	printBar: false,
+	padding: '2 0',
+    },
+
+    items: [
+	{
+	    reference: 'backup-time',
+	    iconCls: 'fa fa-clock-o',
+	    title: gettext('Backup Time'),
+	},
+	{
+	    reference: 'protected',
+	    iconCls: 'fa fa-shield',
+	    title: gettext('Protected'),
+	},
+	{
+	    reference: 'size',
+	    iconCls: 'fa fa-hdd-o',
+	    title: gettext('Size'),
+	},
+	{
+	    reference: 'owner',
+	    iconCls: 'fa fa-user-o',
+	    title: gettext('Owner'),
+	},
+	{
+	    // spacer
+	    xtype: 'box',
+	    padding: 10,
+	},
+	{
+	    xtype: 'container',
+	    layout: {
+		type: 'hbox',
+		align: 'stretch',
+	    },
+	    items: [
+		{
+		    xtype: 'container',
+		    flex: 1,
+		    layout: {
+			type: 'vbox',
+			align: 'stretch',
+		    },
+		    defaults: {
+			xtype: 'pmxInfoWidget',
+			printBar: false,
+			padding: '2 0',
+		    },
+		    items: [
+			{
+			    xtype: 'box',
+			    html: `<i class="fa fa-upload"></i> ${gettext('Upload Statistics')}`,
+			    reference: 'upload-title',
+			    padding: '0 0 10 0',
+			},
+			{
+			    title: gettext('Size'),
+			    reference: 'upload-size',
+			},
+			{
+			    title: gettext('Compressed Size'),
+			    reference: 'compressed-size',
+			},
+			{
+			    title: gettext('Chunk Count'),
+			    reference: 'chunk-count',
+			},
+			{
+			    title: gettext('Duplicate Chunks'),
+			    reference: 'duplicate-chunks',
+			},
+		    ],
+		},
+		{
+		    // spacer
+		    xtype: 'box',
+		    padding: 10,
+		},
+		{
+		    xtype: 'container',
+		    flex: 1,
+		    layout: {
+			type: 'vbox',
+			align: 'stretch',
+		    },
+		    defaults: {
+			xtype: 'pmxInfoWidget',
+			printBar: false,
+			padding: '2 0',
+		    },
+		    items: [
+			{
+			    xtype: 'box',
+			    html: `<i class="pve-icon-verify-lettering"></i> ${gettext('Verification')}`,
+			    reference: 'verify-title',
+			    padding: '0 0 10 0',
+			},
+			{
+			    title: gettext('Verify State'),
+			    reference: 'verify-state',
+			},
+			{
+			    title: gettext('Last Verificaton'),
+			    iconCls: 'fa fa-list-alt',
+			    reference: 'verify-last',
+			},
+		    ],
+		},
+	    ],
+	},
+	{
+	    // spacer
+	    xtype: 'box',
+	    padding: 10,
+	},
+	{
+	    xtype: 'tabpanel',
+	    height: 200,
+	    items: [{
+		title: gettext('Files'),
+		xtype: 'grid',
+		reference: 'files',
+		store: {
+		    data: [],
+		},
+		scrollable: true,
+		columns: [
+		    {
+			dataIndex: 'filename',
+			text: gettext('Filename'),
+			flex: 1,
+		    },
+		    {
+			dataIndex: 'size',
+			text: gettext('Size'),
+			flex: 1,
+			renderer: (v) => v !== undefined ? Proxmox.Utils.format_size(v) : '',
+		    },
+		    {
+			dataIndex: 'crypt-mode',
+			text: gettext('Encrypted'),
+			flex: 1,
+			renderer: (v) => {
+			    let modeIdx = PBS.Utils.cryptmap.indexOf(v);
+			    if (modeIdx === -1) {
+				modeIdx = 0;
+			    }
+			    let iconCls = PBS.Utils.cryptIconCls[modeIdx];
+			    return `<i class="fa fa-${iconCls}"></i> ${PBS.Utils.cryptText[modeIdx]}`;
+			},
+		    },
+		],
+	    }, {
+		xtype: 'box',
+		padding: 10,
+		style: {
+		    'white-space': 'pre',
+		},
+		scrollable: true,
+		title: gettext('Comment'),
+		reference: 'comment',
+	    }, {
+		xtype: 'box',
+		padding: 10,
+		style: {
+		    'white-space': 'pre',
+		},
+		scrollable: true,
+		title: gettext('Group Comment'),
+		reference: 'group-comment',
+	    }],
+	},
+    ],
+
+    initComponent: function() {
+	let me = this;
+	let type = me['backup-type'];
+	let id = me['backup-id'];
+	let time = me['backup-time'];
+	let datetime = new Date(time*1000);
+	if (type === undefined || id === undefined || time === undefined) {
+	    throw "snapshot id not given";
+	}
+
+	let snapshotText = `${type}/${id}/${PBS.Utils.render_datetime_utc(datetime)}`;
+	me.title = Ext.String.format(gettext('Snapshot {0}'), snapshotText);
+
+	me.callParent();
+	Proxmox.Utils.API2Request({
+	    url: `/api2/extjs/admin/datastore/${me.datastore}/snapshot-information`,
+	    params: {
+		'backup-type': type,
+		'backup-id': id,
+		'backup-time': time,
+	    },
+	    method: 'GET',
+	    success: ({ result }) => {
+		me.lookup('backup-time').updateValue(datetime);
+		me.lookup('protected').updateValue(Proxmox.Utils.format_boolean(result.data.protected));
+		me.lookup('size').updateValue(Proxmox.Utils.format_size(result.data.size));
+		me.lookup('owner').updateValue(result.data.owner);
+		me.lookup('comment').setData(result.data.comment);
+		me.lookup('group-comment').setData(result.data['group-notes']);
+
+		me.lookup('files').getStore().setData(result.data.files);
+
+		let verification = result.data.verification;
+		if (verification !== undefined) {
+		    let iconCls = 'times critical';
+		    if (verification.state === 'ok') {
+			iconCls = 'check good';
+		    }
+		    let txt = Ext.htmlEncode(verification.state);
+		    let verifyState = `<i class="fa fa-${iconCls}"></i> ${txt}`;
+
+		    let task = Proxmox.Utils.parse_task_upid(verification.upid);
+		    let verifyTime = Proxmox.Utils.render_timestamp(task.starttime);
+		    me.lookup('verify-state').updateValue(verifyState);
+		    me.lookup('verify-last').updateValue(verifyTime);
+		} else {
+		    me.lookup('verify-state').updateValue(Proxmox.Utils.NoneText);
+		    me.lookup('verify-last').updateValue(Proxmox.Utils.NoneText);
+		}
+
+		let uploadStats = result.data['upload-statistics'];
+		if (uploadStats !== undefined) {
+		    me.lookup('upload-size').updateValue(Proxmox.Utils.format_size(uploadStats.size));
+		    me.lookup('compressed-size').updateValue(Proxmox.Utils.format_size(uploadStats['compressed-size']));
+		    me.lookup('chunk-count').updateValue(uploadStats.count);
+		    me.lookup('duplicate-chunks').updateValue(uploadStats.duplicates);
+		} else {
+		    me.lookup('upload-title').setVisible(false);
+		    me.lookup('upload-size').setVisible(false);
+		    me.lookup('compressed-size').setVisible(false);
+		    me.lookup('chunk-count').setVisible(false);
+		    me.lookup('duplicate-chunks').setVisible(false);
+		}
+	    },
+	    failure: function(response, opts) {
+		Ext.Msg.alert(gettext('Error'), response.htmlStatus, function() {
+		    me.close();
+		});
+	    },
+	});
+    },
+});
-- 
2.30.2





^ permalink raw reply	[flat|nested] 5+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v2 4/4] ui: datastore content: add 'more actions' menu to actions
  2023-12-05 10:53 [pbs-devel] [PATCH proxmox-backup v2 0/4] add 'show information' for snapshots Dominik Csapak
                   ` (2 preceding siblings ...)
  2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 3/4] ui: datastore content: add snapshot information to context menu Dominik Csapak
@ 2023-12-05 10:53 ` Dominik Csapak
  3 siblings, 0 replies; 5+ messages in thread
From: Dominik Csapak @ 2023-12-05 10:53 UTC (permalink / raw)
  To: pbs-devel

and move change owner and change protection there, since these are not
that often used. Also add the 'Show Information' action there for
snapshots, as this was not available via actions.

Refactor the menu creation for this.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 www/datastore/Content.js | 110 ++++++++++++++++++++++-----------------
 1 file changed, 61 insertions(+), 49 deletions(-)

diff --git a/www/datastore/Content.js b/www/datastore/Content.js
index f715f814..8f063287 100644
--- a/www/datastore/Content.js
+++ b/www/datastore/Content.js
@@ -806,6 +806,48 @@ Ext.define('PBS.DataStoreContent', {
 	    }).show();
 	},
 
+	onMoreActions: function(panel, rI, cI, item, event, record) {
+	    this.showContextMenu(panel, record, event, false);
+	},
+
+	showContextMenu: function(panel, record, event, rightClick) {
+	    let me = this;
+	    event.stopEvent();
+	    let menu;
+	    let view = me.getView();
+	    let createControllerCallback = function(name) {
+		return function() {
+		    me[name](view, undefined, undefined, undefined, undefined, record);
+		};
+	    };
+	    if (record.data.ty === 'group') {
+		let params = {
+		    title: gettext('Group'),
+		    onChangeOwner: createControllerCallback('onChangeOwner'),
+		};
+		if (rightClick) {
+		    params.onVerify = createControllerCallback('onVerify');
+		    params.onPrune = createControllerCallback('onPrune');
+		    params.onForget = createControllerCallback('onForget');
+		}
+		menu = Ext.create('PBS.datastore.GroupCmdMenu', params);
+	    } else if (record.data.ty === 'dir') {
+		let params = {
+		    title: gettext('Snapshot'),
+		    onProtectionChange: createControllerCallback('onProtectionChange'),
+		    onShowInformation: createControllerCallback('onShowInformation'),
+		};
+		if (rightClick) {
+		    params.onVerify = createControllerCallback('onVerify');
+		    params.onForget = createControllerCallback('onForget');
+		}
+		menu = Ext.create('PBS.datastore.SnapshotCmdMenu', params);
+	    }
+	    if (menu) {
+		menu.showAt(event.getXY());
+	    }
+	},
+
 	filter: function(item, value) {
 	    if (item.data.text.indexOf(value) !== -1) {
 		return true;
@@ -882,35 +924,7 @@ Ext.define('PBS.DataStoreContent', {
 	    }
 	},
 	itemcontextmenu: function(panel, record, item, index, event) {
-	    event.stopEvent();
-	    let menu;
-	    let view = panel.up('pbsDataStoreContent');
-	    let controller = view.getController();
-	    let createControllerCallback = function(name) {
-		return function() {
-		    controller[name](view, undefined, undefined, undefined, undefined, record);
-		};
-	    };
-	    if (record.data.ty === 'group') {
-		menu = Ext.create('PBS.datastore.GroupCmdMenu', {
-		    title: gettext('Group'),
-		    onVerify: createControllerCallback('onVerify'),
-		    onChangeOwner: createControllerCallback('onChangeOwner'),
-		    onPrune: createControllerCallback('onPrune'),
-		    onForget: createControllerCallback('onForget'),
-		});
-	    } else if (record.data.ty === 'dir') {
-		menu = Ext.create('PBS.datastore.SnapshotCmdMenu', {
-		    title: gettext('Snapshot'),
-		    onVerify: createControllerCallback('onVerify'),
-		    onProtectionChange: createControllerCallback('onProtectionChange'),
-		    onShowInformation: createControllerCallback('onShowInformation'),
-		    onForget: createControllerCallback('onForget'),
-		});
-	    }
-	    if (menu) {
-		menu.showAt(event.getXY());
-	    }
+	    this.getController().showContextMenu(panel, record, event, true);
 	},
     },
 
@@ -1000,30 +1014,12 @@ Ext.define('PBS.DataStoreContent', {
 		        ? 'pve-icon-verify-lettering' : 'pmx-hidden',
 		    isActionDisabled: (v, r, c, i, rec) => !!rec.data.leaf,
                 },
-                {
-		    handler: 'onChangeOwner',
-		    getClass: (v, m, { data }) => data.ty === 'group' ? 'fa fa-user' : 'pmx-hidden',
-		    getTip: (v, m, rec) => Ext.String.format(gettext("Change owner of '{0}'"), v),
-		    isActionDisabled: (v, r, c, i, { data }) => data.ty !== 'group',
-                },
 		{
 		    handler: 'onPrune',
 		    getTip: (v, m, rec) => Ext.String.format(gettext("Prune '{0}'"), v),
 		    getClass: (v, m, { data }) => data.ty === 'group' ? 'fa fa-scissors' : 'pmx-hidden',
 		    isActionDisabled: (v, r, c, i, { data }) => data.ty !== 'group',
 		},
-		{
-		    handler: 'onProtectionChange',
-		    getTip: (v, m, rec) => Ext.String.format(gettext("Change protection of '{0}'"), v),
-		    getClass: (v, m, rec) => {
-			if (rec.data.ty === 'dir') {
-			    let extraCls = rec.data.protected ? 'good' : 'faded';
-			    return `fa fa-shield ${extraCls}`;
-			}
-			return 'pmx-hidden';
-		    },
-		    isActionDisabled: (v, r, c, i, rec) => rec.data.ty !== 'dir',
-		},
 		{
 		    handler: 'onForget',
 		    getTip: (v, m, { data }) => {
@@ -1044,6 +1040,12 @@ Ext.define('PBS.DataStoreContent', {
 		        : 'pmx-hidden',
 		    isActionDisabled: (v, r, c, i, { data }) => false,
 		},
+		{
+		    handler: 'onMoreActions',
+		    getTip: (v, m, { data }) => gettext('Show more actions'),
+		    getClass: (v, m, { data }) => data.ty === 'group' || data.ty === 'dir' ? 'fa fa-bars' : 'pmx-hidden',
+		    isActionDisabled: (v, r, c, i, { data }) => data.ty !== 'group' && data.ty !== 'dir',
+		},
 		{
 		    handler: 'downloadFile',
 		    getTip: (v, m, rec) => Ext.String.format(gettext("Download '{0}'"), v),
@@ -1325,7 +1327,12 @@ Ext.define('PBS.datastore.GroupCmdMenu', {
 		hidden: '{!onPrune}',
 	    },
 	},
-	{ xtype: 'menuseparator' },
+	{
+	    xtype: 'menuseparator',
+	    cbind: {
+		hidden: '{!onForget}',
+	    },
+	},
 	{
 	    text: gettext('Remove'),
 	    iconCls: 'fa critical fa-trash-o',
@@ -1374,7 +1381,12 @@ Ext.define('PBS.datastore.SnapshotCmdMenu', {
 		disabled: '{!onShowInformation}',
 	    },
 	},
-	{ xtype: 'menuseparator' },
+	{
+	    xtype: 'menuseparator',
+	    cbind: {
+		hidden: '{!onForget}',
+	    },
+	},
 	{
 	    text: gettext('Remove'),
 	    iconCls: 'fa critical fa-trash-o',
-- 
2.30.2





^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2023-12-05 10:54 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-05 10:53 [pbs-devel] [PATCH proxmox-backup v2 0/4] add 'show information' for snapshots Dominik Csapak
2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 1/4] api-types: add an UploadStatistic api type Dominik Csapak
2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 2/4] api: datastore admin: add 'snapshot-information' api call Dominik Csapak
2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 3/4] ui: datastore content: add snapshot information to context menu Dominik Csapak
2023-12-05 10:53 ` [pbs-devel] [PATCH proxmox-backup v2 4/4] ui: datastore content: add 'more actions' menu to actions Dominik Csapak

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal