From 6b3ce31b7350912d6080771f0f02f0990fdcbf5e Mon Sep 17 00:00:00 2001 From: Michael Rasmussen Date: Wed, 16 Jul 2014 02:19:47 +0200 Subject: [PATCH] GUI part from Niclas as well as debian build script --- build-package.sh | 14 + .../omv/module/admin/service/zfs/Overview.js | 503 ++++++++++++++ .../omv/module/admin/service/zfs/TreePanel.js | 646 ++++++++++++++++++ gui/js/omv/module/admin/service/zfs/Zfs.js | 8 + gui/rpc/zfs.inc | 313 +++++++++ images/zfs.png | Bin 0 -> 278 bytes images/zfs.svg | 19 + images/zfs_disk.png | Bin 0 -> 426 bytes images/zfs_mag.png | Bin 0 -> 358 bytes images/zfs_snap.png | Bin 0 -> 866 bytes openmediavault-zfs/README.md | 2 + openmediavault-zfs/openmediavault.mk | 63 ++ 12 files changed, 1568 insertions(+) create mode 100755 build-package.sh create mode 100644 gui/js/omv/module/admin/service/zfs/Overview.js create mode 100644 gui/js/omv/module/admin/service/zfs/TreePanel.js create mode 100644 gui/js/omv/module/admin/service/zfs/Zfs.js create mode 100644 gui/rpc/zfs.inc create mode 100644 images/zfs.png create mode 100644 images/zfs.svg create mode 100644 images/zfs_disk.png create mode 100644 images/zfs_mag.png create mode 100644 images/zfs_snap.png create mode 100644 openmediavault-zfs/README.md create mode 100644 openmediavault-zfs/openmediavault.mk diff --git a/build-package.sh b/build-package.sh new file mode 100755 index 0000000..4a30205 --- /dev/null +++ b/build-package.sh @@ -0,0 +1,14 @@ +#!/bin/bash +rm -rf openmediavault-zfs/usr/share/openmediavault/engined/rpc +mkdir -p openmediavault-zfs/usr/share/openmediavault/engined/rpc/zfs +rm -rf openmediavault-zfs/var/www/openmediavault/js/omv/module/admin/service/zfs +mkdir -p openmediavault-zfs/var/www/openmediavault/js/omv/module/admin/service/zfs +rm -rf openmediavault-zfs/var/www/openmediavault/images +mkdir -p openmediavault-zfs/var/www/openmediavault/images +cp src/* openmediavault-zfs/usr/share/openmediavault/engined/rpc/zfs +cp gui/rpc/zfs.inc openmediavault-zfs/usr/share/openmediavault/engined/rpc +cp gui/js/omv/module/admin/service/zfs/* openmediavault-zfs/var/www/openmediavault/js/omv/module/admin/service/zfs +cp images/* openmediavault-zfs/var/www/openmediavault/images +cd openmediavault-zfs +dpkg-buildpackage -us -uc + diff --git a/gui/js/omv/module/admin/service/zfs/Overview.js b/gui/js/omv/module/admin/service/zfs/Overview.js new file mode 100644 index 0000000..fe72e89 --- /dev/null +++ b/gui/js/omv/module/admin/service/zfs/Overview.js @@ -0,0 +1,503 @@ +// require("js/omv/tree/Panel.js") +// require("js/omv/module/admin/service/zfs/TreePanel.js") +// require("js/omv/workspace/window/Grid.js") + +Ext.define("OMV.module.admin.services.zfs.AddObject", { + extend: "OMV.workspace.window.Form", + uses: [ + "OMV.data.Store", + "OMV.data.Model", + "OMV.data.proxy.Rpc", + "OMV.data.reader.RpcArray" + ], + + rpcService: "ZFS", + rpcSetMethod: "addObject", + width: 420, + + getFormItems: function() { + var me = this; + return [{ + xtype: "combo", + name: "type", + fieldLabel: _("Object Type"), + queryMode: "local", + store: [ + [ "filesystem", "Filesystem" ], + [ "snapshot", "Snapshot" ], + [ "volume", "Volume" ] + ], + allowBlank: true, + editable: false, + triggerAction: "all", + value: "filesystem", + listeners: { + scope: me, + change: function(combo, value) { + var sizeField = this.findField("size"); + switch(value) { + case "volume": + sizeField.show(); + sizeField.allowBlank = false; + break; + default: + sizeField.hide(); + sizeField.allowBlank = true; + break; + } + sizeField.validate(); + } + } + },{ + xtype: "textfield", + name: "path", + fieldLabel: _("Prefix"), + allowBlank: false, + readOnly: true + },{ + xtype: "textfield", + name: "name", + fieldLabel: _("Name"), + allowBlank: false, + plugins: [{ + ptype: "fieldinfo", + text: _("Name of the new object. Prefix will prepend the name. Please omit leading /") + }] + },{ + xtype: "textfield", + name: "size", + hidden: true, + fieldLabel: _("Size"), + allowBlank: true, + plugins: [{ + ptype: "fieldinfo", + text: _("Size of the volume e.g. 5mb,100gb,1tb etc") + }] + }]; + } +}); + + + +Ext.define("OMV.module.admin.service.zfs.EditProperties", { + extend: "OMV.workspace.window.Grid", + requires: [ + "OMV.data.Store", + "OMV.data.Model", + "OMV.data.proxy.Rpc" + ], + + rpcService: "ZFS", + rpcSetMethod: "setProperties", + + title: _("Edit properties"), + width: 500, + height: 305, + + getGridConfig: function() { + var me = this; + + var rowEditing = Ext.create('Ext.grid.plugin.RowEditing', { + clicksToEdit: 1, + pluginId: 'rowEditing', + listeners: { + validateedit: function(editor, e, eOpts) { + e.record.set("modified", "true"); + }, + beforeedit: function(editor, e, eOpts) { + if (e.record.get("property") === "mountpoint") { + e.grid.getPlugin('rowEditing').editor.form.findField("value").disable(); + e.grid.getPlugin('rowEditing').editor.form.findField("property").disable(); + } else if (e.record.get("newproperty") === "false") { + e.grid.getPlugin('rowEditing').editor.form.findField("value").enable(); + e.grid.getPlugin('rowEditing').editor.form.findField("property").disable(); + } else { + e.grid.getPlugin('rowEditing').editor.form.findField("value").enable(); + e.grid.getPlugin('rowEditing').editor.form.findField("property").enable(); + } + } + + } + }); + + var store = Ext.create("OMV.data.Store", { + autoLoad: true, + model: OMV.data.Model.createImplicit({ + fields: [ + { name: "property", type: "string" }, + { name: "value", type: "string" }, + { name: "source", type: "string" }, + { name: "modified", type: "string" }, + { name: "newproperty", type: "string", defaultValue: "false" } + ] + }), + proxy: { + type: "rpc", + rpcData: { + service: "ZFS", + method: "getProperties", + params: { + name: me.name, + type: me.type + } + } + } + }); + + return { + border: false, + stateful: true, + stateId: "8c3dc800-bdbb-11e3-b1b6-0800200c9a66", + selType: 'rowmodel', + plugins: [rowEditing], + store: store, + tbar: [{ + text: "Add property", + icon: "images/add.png", + iconCls: Ext.baseCSSPrefix + "btn-icon-16x16", + handler: function(view) { + Ext.define('Property', { + extend: 'Ext.data.Model', + fields: [ + "property", + "value", + "source", + "modified", + "newproperty" + ] + }); + var newProperty = Ext.create("Property", { + property: "", + value: "", + source: "local", + modified: "true", + newproperty: "true" + }); + rowEditing.cancelEdit(); + store.insert(0, newProperty); + rowEditing.startEdit(); + } + }], + columns: [{ + text: _("Property"), + sortable: true, + dataIndex: "property", + stateId: "property", + editor: { + xtype: "textfield", + allowBlank: false, + } + },{ + text: _("Value"), + sortable: true, + dataIndex: "value", + stateId: "value", + flex: 1, + readOnly: true, + editor: { + xtype: "textfield", + allowBlank: false, + } + },{ + text: _("Source"), + sortable: true, + dataIndex: "source", + stateId: "source", + },{ + xtype: 'actioncolumn', + header: 'Inherit', + icon: "images/checkmark.png", + tooltip: "Inherit", + handler: function(view, rowIndex, colIndex, item, e, record, row) { + OMV.RpcObserver.request({ + msg : _("Updating property..."), + rpcData : { + service: "ZFS", + method: "inherit", + params: { + name: me.name, + type: me.type, + property: record.get("property") + } + }, + finish : function() { + view.getStore().reload(); + } + }); + }, + isDisabled: function(view, rowIdx, colIdx, item, record) { + var src = record.get("source"); + if(src === "local") { + return false; + } else { + return true; + } + } + },{ + text: _("New"), + dataIndex: "newproperty", + stateId: "newproperty", + sortable: false, + hidden: true + },{ + text: _("Modified"), + sortable: false, + dataIndex: "modified", + stateId: "modified", + hidden: true + }], + }; + }, + + getRpcSetParams: function() { + var me = this; + var properties = []; + var values = me.getValues(); + Ext.Array.each(values, function(value) { + if(value.modified === "false") + return; + properties.push({ + "property": value.property, + "value": value.value, + }); + }); + return { + name: me.name, + type: me.type, + properties: properties + }; + } + +}); + + +Ext.define("OMV.module.admin.services.zfs.CreateShare", { + extend: "OMV.workspace.window.Form", + uses: [ + "OMV.data.Store", + "OMV.data.Model", + "OMV.data.proxy.Rpc", + "OMV.data.reader.RpcArray" + ], + + rpcService: "ZFS", + rpcSetMethod: "createShare", + width: 500, + + getFormItems: function() { + var me = this; + return [{ + xtype: "textfield", + name: "sharename", + fieldLabel: _("Name"), + allowBlank: false, + },{ + xtype: "textfield", + name: "mountpoint", + fieldLabel: _("Path"), + allowBlank: false, + readOnly: true + },{ + xtype: "combo", + name: "mode", + fieldLabel: _("Permissions"), + queryMode: "local", + store: Ext.create("Ext.data.ArrayStore", { + fields: [ "value", "text" ], + data: [ + [ "700", _("Administrator: read/write, Users: no access, Others: no access") ], + [ "750", _("Administrator: read/write, Users: read-only, Others: no access") ], + [ "770", _("Administrator: read/write, Users: read/write, Others: no access") ], + [ "755", _("Administrator: read/write, Users: read-only, Others: read-only") ], + [ "775", _("Administrator: read/write, Users: read/write, Others: read-only") ], + [ "777", _("Everyone: read/write") ] + ] + }), + displayField: "text", + valueField: "value", + allowBlank: false, + editable: false, + showItemTooltip: true, + triggerAction: "all", + value: "775", + plugins: [{ + ptype: "fieldinfo", + text: _("The file mode of the shared folder path.") + }] + },{ + xtype: "textarea", + name: "comment", + fieldLabel: _("Comment"), + allowBlank: true + },{ + xtype: "textarea", + name: "name", + hidden: true + },{ + xtype: "textarea", + name: "type", + hidden: true + }]; + } +}); + + + +Ext.define("OMV.module.admin.service.zfs.Overview", { + extend: "OMV.module.admin.services.zfs.TreePanel", + + rpcService: "ZFS", + rpcGetMethod: "getObjectTree", + requires: [ + "OMV.data.Store", + "OMV.data.Model", + "OMV.data.proxy.Rpc" + ], + + rootVisible: false, + stateful: true, + stateId: "cec54550-bc2a-11e3-a5e2-0800200c9a66", + + columns: [{ + text: _("Name"), + xtype: 'treecolumn', + dataIndex: 'name', + sortable: true, + flex: 2, + stateId: 'name' + },{ + text: _("Type"), + dataIndex: 'type', + sortable: true, + flex: 1, + stateId: 'type' + },{ + text: _("Share"), + xtype: 'actioncolumn', + tooltip: 'Create shared folder', + align: 'center', + icon: 'images/checkmark.png', + handler: function(view, rowIndex, colIndex, item, e, record, row) { + var me = this; + Ext.create("OMV.module.admin.services.zfs.CreateShare", { + title: _("Create shared folder"), + rpcGetMethod: "getSharedParams", + rpcGetParams: { + name: record.get('path'), + type: record.get('type') + } + }).show(); + }, + isDisabled: function(view, rowIdx, colIdx, item, record) { + var src = record.get("type"); + if((src === "Filesystem") && (record.get("shared") === "false")) { + return false; + } else { + return true; + } + } + + + },{ + text: _("Details"), + xtype: 'actioncolumn', + tooltip: 'Details', + align: 'center', + icon: 'images/zfs_mag.png' + },{ + text: _("Shared"), + dataIndex: 'shared', + sortable: false, + stateId: 'shared', + hidden: true + }], + + initComponent: function() { + var me = this; + this.width = 600; + Ext.apply(me, { + store: Ext.create("Ext.data.TreeStore", { + autoLoad: true, + model: OMV.data.Model.createImplicit({ + fields: [ + { name: "name", type: "string" }, + { name: "type", type: "string" }, + { name: "id", type: "string" }, + { name: "path", type: "string" }, + { name: "origin", type: "string", defaultValue: "none" }, + { name: "shared", type: "string", defaultValue: "false" } + ] + }), + proxy: { + type: "rpc", + rpcData: { + service: "ZFS", + method: "getObjectTree", + } + }, + folderSort: true + }) + }); + me.callParent(arguments); + }, + + onAddObjButton: function() { + var me = this; + var sm = me.getSelectionModel(); + var records = sm.getSelection(); + var record = records[0]; + Ext.create("OMV.module.admin.services.zfs.AddObject", { + title: _("Add Object"), + rpcGetMethod: "passParam", + rpcGetParams: { + key: "path", + value: record.get('path') + }, + listeners: { + scope: me, + submit: function() { + this.doReload(); + } + } + }).show(); + }, + + onEditButton: function() { + var me = this; + var sm = me.getSelectionModel(); + var records = sm.getSelection(); + var record = records[0]; + Ext.create("OMV.module.admin.service.zfs.EditProperties", { + name: record.get("path"), + type: record.get("type") + }).show(); + }, + + doDeletion: function(record) { + var me = this; + OMV.Rpc.request({ + scope: me, + callback: me.onDeletion, + rpcData: { + service: "ZFS", + method: "deleteObject", + params: { + name: record.get('path'), + type: record.get('type') + } + } + }); + } + +}); + +OMV.WorkspaceManager.registerPanel({ + id: "overview", + path: "/service/zfs", + text: _("Overview"), + position: 10, + className: "OMV.module.admin.service.zfs.Overview" +}); + + + diff --git a/gui/js/omv/module/admin/service/zfs/TreePanel.js b/gui/js/omv/module/admin/service/zfs/TreePanel.js new file mode 100644 index 0000000..26a25ce --- /dev/null +++ b/gui/js/omv/module/admin/service/zfs/TreePanel.js @@ -0,0 +1,646 @@ +/** + * This file is part of OpenMediaVault. + * + * @license http://www.gnu.org/licenses/gpl.html GPL Version 3 + * @author Volker Theile + * @copyright Copyright (c) 2009-2014 Volker Theile + * + * OpenMediaVault is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * OpenMediaVault is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenMediaVault. If not, see . + */ +// require("js/omv/tree/Panel.js") +// require("js/omv/grid/Panel.js") +// require("js/omv/grid/column/BinaryUnit.js") +// require("js/omv/grid/column/BooleanIcon.js") +// require("js/omv/grid/column/BooleanText.js") +// require("js/omv/grid/column/Empty.js") +// require("js/omv/grid/column/Hyperlink.js") +// require("js/omv/grid/column/UnixTimestamp.js") +// require("js/omv/grid/column/WhiteSpace.js") +// require("js/omv/window/MessageBox.js") + +/** + * @ingroup webgui + * @class OMV.workspace.grid.Panel + * @derived OMV.grid.Panel + * An enhanced grid panel. This grid provides 'Add', 'Edit' and 'Delete' + * buttons in the toolbar by default. The basic delete functionality is also + * implemented, simply overwrite the 'doDeletion' and 'afterDeletion' + * functions to implement fit your requirements. To implement the 'Add' and + * 'Edit' functionality overwrite the 'onAdd' and 'onEdit' callback + * functions. A paging toolbar which is displayed at the bottom of the grid + * can be displayed also. It is also possible to reload the grid + * automatically in a given interval. + * @param hideTopToolbar TRUE to hide the whole toolbar. Defaults to FALSE. + * @param hidePagingToolbar TRUE to hide the paging toolbar at the bottom of + * the grid. Defaults to TRUE. + * @param hideAddButton Hide the 'Add' button in the top toolbar. + * Defaults to FALSE. + * @param hideEditButton Hide the 'Edit' button in the top toolbar. + * Defaults to FALSE. + * @param hideDeleteButton Hide the 'Delete' button in the top toolbar. + * Defaults to FALSE. + * @param hideUpButton Hide the 'Up' button in the top toolbar. + * Defaults to TRUE. + * @param hideDownButton Hide the 'Down' button in the top toolbar. + * Defaults to TRUE. + * @param hideApplyButton Hide the 'Apply' button in the top toolbar. + * Defaults to TRUE. + * @param hideRefreshButton Hide the 'Refresh' button in the top toolbar. + * Defaults to TRUE. + * @param addButtonText The button text. Defaults to 'Add'. + * @param editButtonText The button text. Defaults to 'Edit'. + * @param deleteButtonText The button text. Defaults to 'Delete'. + * @param upButtonText The button text. Defaults to 'Up'. + * @param downButtonText The button text. Defaults to 'Down'. + * @param applyButtonText The button text. Defaults to 'Save'. + * @param refreshButtonText The button text. Defaults to 'Refresh'. + * @param deletionConfirmRequired Set to TRUE to force the user to confirm + * the deletion request. Defaults to TRUE. + * @param deletionWaitMsg The message displayed during the deletion process. + * @param mode The mode how to retrieve the data displayed in the grid panel. + * This can be 'local' or 'remote' which means the data is requested via + * RPC. Defaults to 'remote'. + * @param rememberSelected TRUE to reselect the previous selected rows + * after the grid content has been reloaded/refreshed. Defaults to FALSE. + */ +Ext.define("OMV.module.admin.services.zfs.TreePanel", { + extend: "OMV.tree.Panel", + requires: [ + "OMV.window.MessageBox", + "OMV.grid.column.BinaryUnit", + "OMV.grid.column.BooleanIcon", + "OMV.grid.column.BooleanText", + "OMV.grid.column.Empty", + "OMV.grid.column.Hyperlink", + "OMV.grid.column.UnixTimestamp", + "OMV.grid.column.WhiteSpace" + ], + + border: false, + rowLines: false, + columnLines: true, + selModel: { + allowDeselect: true, + mode: "SINGLE" + }, + + hideTopToolbar: false, + hidePagingToolbar: true, + hideAddButton: false, + hideAddObjButton: false, + hideEditButton: false, + hideDeleteButton: false, + hideUpButton: true, + hideDownButton: true, + hideApplyButton: true, + hideRefreshButton: true, + addButtonText: _("Add Pool"), + addObjButtonText: _("Add Object"), + editButtonText: _("Edit"), + deleteButtonText: _("Delete"), + upButtonText: _("Up"), + downButtonText: _("Down"), + applyButtonText: _("Save"), + refreshButtonText: _("Refresh"), + deletionConfirmRequired: true, + deletionWaitMsg: _("Deleting selected item(s)"), + mode: "remote", + rememberSelected: false, + + initComponent: function() { + var me = this; + // Initialize toolbars. + me.dockedItems = []; + if(!me.hideTopToolbar) { + me.dockedItems.push(me.topToolbar = Ext.widget({ + xtype: "toolbar", + dock: "top", + items: me.getTopToolbarItems(me) + })); + } + if(!me.hidePagingToolbar) { + me.dockedItems.push({ + xtype: "toolbar", + dock: "bottom", + items: [ me.pagingToolbar = Ext.widget({ + xtype: "pagingtoolbar", + store: me.store, + displayInfo: true, + displayMsg: _("Displaying items {0} - {1} of {2}"), + emptyMsg: _("No items to display") + }) ] + }); + } + me.callParent(arguments); + // Register event handler. + // Process double clicks in grid. + me.on("itemdblclick", me.onItemDblClick, me); + // Process selections in grid, e.g. to update the toolbar. + var selModel = me.getSelectionModel(); + selModel.on("selectionchange", me.onSelectionChange, me); + // Remember selection to restore it after the grid has been + // refreshed. + if(me.rememberSelected) { + me.getStore().on("beforeload", function() { + if(!me.rendered || Ext.isEmpty(me.getEl())) + return; + if(!selModel.hasSelection()) + return; + me.previousSelected = selModel.getSelection(); + }); + me.getView().on("refresh", function(view) { + if(Ext.isEmpty(me.previousSelected)) + return; + var select = []; + Ext.Array.each(me.previousSelected, function(r) { + var record = me.getStore().getById(r.getId()); + if(!Ext.isEmpty(record)) + select.push(record); + }); + delete me.previousSelected; + if(select.length > 0) { + selModel.select(select, false, false); + selModel.view.focusNode(select[0]); + } + }); + } + }, + + /** + * Returns the items displayed in the top toolbar. + * @param c This component object. + * @return An array of buttons displayed in the top toolbar. + */ + getTopToolbarItems: function(c) { + var me = this; + return [{ + id: me.getId() + "-add", + xtype: "button", + text: me.addButtonText, + icon: "images/add.png", + iconCls: Ext.baseCSSPrefix + "btn-icon-16x16", + hidden: me.hideAddButton, + handler: Ext.Function.bind(me.onAddButton, me, [ me ]), + scope: me + },{ + id: me.getId() + "-addobj", + xtype: "button", + text: me.addObjButtonText, + icon: "images/add.png", + iconCls: Ext.baseCSSPrefix + "btn-icon-16x16", + hidden: me.hideAddObjButton, + handler: Ext.Function.bind(me.onAddObjButton, me, [ me ]), + scope: me, + disabled: true + },{ + id: me.getId() + "-edit", + xtype: "button", + text: me.editButtonText, + icon: "images/edit.png", + iconCls: Ext.baseCSSPrefix + "btn-icon-16x16", + hidden: me.hideEditButton, + handler: Ext.Function.bind(me.onEditButton, me, [ me ]), + scope: me, + disabled: true + },{ + id: me.getId() + "-delete", + xtype: "button", + text: me.deleteButtonText, + icon: "images/delete.png", + iconCls: Ext.baseCSSPrefix + "btn-icon-16x16", + hidden: me.hideDeleteButton, + handler: Ext.Function.bind(me.onDeleteButton, me, [ me ]), + scope: me, + disabled: true + },{ + id: me.getId() + "-up", + xtype: "button", + text: me.upButtonText, + icon: "images/arrow-up.png", + iconCls: Ext.baseCSSPrefix + "btn-icon-16x16", + hidden: me.hideUpButton, + handler: Ext.Function.bind(me.onUpButton, me, [ me ]), + scope: me, + disabled: true + },{ + id: me.getId() + "-down", + xtype: "button", + text: me.downButtonText, + icon: "images/arrow-down.png", + iconCls: Ext.baseCSSPrefix + "btn-icon-16x16", + hidden: me.hideDownButton, + handler: Ext.Function.bind(me.onDownButton, me, [ me ]), + scope: me, + disabled: true + },{ + id: me.getId() + "-apply", + xtype: "button", + text: me.applyButtonText, + icon: "images/checkmark.png", + hidden: me.hideApplyButton, + handler: Ext.Function.bind(me.onApplyButton, me, [ me ]), + scope: me + },{ + id: me.getId() + "-refresh", + xtype: "button", + text: me.refreshButtonText, + icon: "images/refresh.png", + iconCls: Ext.baseCSSPrefix + "btn-icon-16x16", + hidden: me.hideRefreshButton, + handler: Ext.Function.bind(me.onRefreshButton, me, [ me ]), + scope: me + }] + }, + + /** + * Handler that is called whenever the selection in the grid has + * been changed. The top toolbar buttons will be enabled/disabled + * depending on how much rows has been selected. + * @param model The selection model + */ + onSelectionChange: function(model, records) { + var me = this; + if(me.hideTopToolbar) + return; + var tbarBtnName = [ "addobj", "edit", "delete", "up", "down" ]; + var tbarBtnDisabled = { + "addobj": false, + "edit": false, + "delete": false, + "up": true, + "down": true + }; + // Enable/disable buttons depending on the number of selected rows. + if(records.length <= 0) { + tbarBtnDisabled["addobj"] = true; + tbarBtnDisabled["edit"] = true; + tbarBtnDisabled["delete"] = true; + tbarBtnDisabled["up"] = true; + tbarBtnDisabled["down"] = true; + } else if(records.length == 1) { + tbarBtnDisabled["addobj"] = false; + tbarBtnDisabled["edit"] = false; + tbarBtnDisabled["delete"] = false; + tbarBtnDisabled["up"] = false; + tbarBtnDisabled["down"] = false; + } else { + tbarBtnDisabled["addobj"] = true; + tbarBtnDisabled["edit"] = true; + tbarBtnDisabled["delete"] = false; + tbarBtnDisabled["up"] = false; + tbarBtnDisabled["down"] = false; + } + //Disable 'AddObj' button if selected row is a Poool or a Snapshot + Ext.Array.each(records, function(record) { + if(("Pool" == record.get("type")) || + ("Snapshot" == record.get("type"))) { + tbarBtnDisabled["addobj"] = true; + return false; + } + }); + + // Disable 'Delete' button if a selected node is not a leaf + Ext.Array.each(records, function(record) { + if((false == record.get("leaf"))) { + tbarBtnDisabled["delete"] = true; + return false; + } + }); + + // Update the button controls. + Ext.Array.each(tbarBtnName, function(name) { + var tbarBtnCtrl = me.queryById(me.getId() + "-" + name); + if(!Ext.isEmpty(tbarBtnCtrl)) { + if(true == tbarBtnDisabled[name]) { + tbarBtnCtrl.disable(); + } else { + tbarBtnCtrl.enable(); + } + } + }); + }, + + onItemDblClick: function() { + var me = this; + if(!me.hideTopToolbar && !me.hideEditButton) { + me.onEditButton(me); + } + }, + + /** + * Load the grid content. + */ + doLoad: function() { + var me = this; + if(me.mode === "remote") { + me.store.load(); + } + }, + + /** + * Reload the grid content. + */ + doReload: function() { + var me = this; + if(me.mode === "remote") { + me.store.reload(); + } + }, + + /** + * Handler that is called when the 'Add' button in the top toolbar + * is pressed. Override this method to customize the default behaviour. + * @param this The grid itself. + */ + onAddButton: function() { + // Nothing to do here + }, + + /** + * * Handler that is called when the 'AddObj' button in the top toolbar + * is pressed. Override this method to customize the default behaviour. + * @param this The grid itself. + */ + onAddObjButton: function() { + // Nothing to do here + }, + + + /** + * Handler that is called when the 'Edit' button in the top toolbar + * is pressed. Override this method to customize the default behaviour. + * @param this The grid itself. + */ + onEditButton: function() { + // Nothing to do here + }, + + /** + * Handler that is called when the 'Up' button in the top toolbar + * is pressed. Override this method to customize the default behaviour. + * @param this The grid itself. + */ + onUpButton: function() { + var me = this; + var sm = me.getSelectionModel(); + var records = sm.getSelection(); + if(records.length > 0) { + // Find the smallest index of the selected rows. + var ltIndex = me.store.indexOf(records[0]); + Ext.Array.each(records, function(record) { + var index = me.store.indexOf(record); + if(ltIndex > index) + ltIndex = index; + }); + // Calculate the index where to insert the rows. + var index = ltIndex - 1; + if(index < 0) + index = 0; + me.doMoveRows(records, index); + } + }, + + /** + * Handler that is called when the 'Down' button in the top toolbar + * is pressed. + * @param this The grid itself. + */ + onDownButton: function() { + var me = this; + var sm = me.getSelectionModel(); + var records = sm.getSelection(); + if(records.length > 0) { + // Find the smallest index of the selected rows. + var ltIndex = me.store.indexOf(records[0]); + Ext.Array.each(records, function(record) { + var index = me.store.indexOf(record); + if(ltIndex > index) + ltIndex = index; + }); + // Calculate the index where to insert the rows. + var index = ltIndex + records.length; + var count = me.store.getCount() - 1; + if(index > count) + index = count; + me.doMoveRows(records, index); + } + }, + + /** + * Handler that is called when the 'Apply' button in the top toolbar + * is pressed. Override this method to customize the default behaviour. + * @param this The grid itself. + */ + onApplyButton: function() { + // Nothing to do here + }, + + /** + * Handler that is called when the 'Refresh' button in the top toolbar + * is pressed. Override this method to customize the default behaviour. + * @param this The grid itself. + */ + onRefreshButton: function() { + this.doReload(); + }, + + /** + * Move the given rows to the given index. + * @param records The records to move. + * @param index The index where to insert the rows to be moved. + */ + doMoveRows: function(records, index) { + var me = this; + if(!Ext.isNumber(index)) + return; + records = Ext.Array.from(records); + me.store.suspendEvents(); + Ext.Array.each(records, function(record) { + me.store.remove(record); + me.store.insert(index, record); + }); + me.store.resumeEvents(); + me.afterMoveRows(records, index); + me.getView().refresh(); + }, + + /** + * Function that is called after the selected rows have been moved. + * Override this method to customize the default behaviour. + * @param records The records that have been move. + * @param index The index where the rows have been inserted. + */ + afterMoveRows: function(records, index) { + var sm = this.getSelectionModel(); + sm.select(records); + }, + + /** + * Handler that is called when the 'Delete' button in the top toolbar + * is pressed. + */ + onDeleteButton: function() { + var me = this; + var sm = me.getSelectionModel(); + var records = sm.getSelection(); + if(me.deletionConfirmRequired === true) { + var msg = _("Do you really want to delete the selected item(s)?"); + OMV.MessageBox.show({ + title: _("Confirmation"), + msg: msg, + buttons: Ext.Msg.YESNO, + fn: function(answer) { + if(answer !== "yes") + return; + me.startDeletion(records); + }, + scope: me, + icon: Ext.Msg.QUESTION + }); + } else { + me.startDeletion(records); + } + }, + + /** + * @private + * Private method that is called when the deletion of the selected records + * has been aggreed. + * @param records The records to delete. + */ + startDeletion: function(records) { + var me = this; + if(records.length <= 0) + return; + // Store selected records in a local variable + me.delActionInfo = { + records: records, + count: records.length + } + // Get first record to be deleted + var record = me.delActionInfo.records.pop(); + // Display progress dialog + OMV.MessageBox.progress("", me.deletionWaitMsg, ""); + me.updateDeletionProgress(); + // Execute deletion function + me.doDeletion(record); + }, + + /** + * The method that is called to delete a selected record. Override this + * method to customize the default behaviour. This is necessary in + * 'remote' mode. + */ + doDeletion: function(record) { + var me = this; + if(me.mode === "local") { + // Remove record from store + me.store.remove(record); + // Continue deletion process + me.onDeletion(null, true, null); + } + }, + + /** + * The method that is called by the 'doDeletion' method. The progress + * bar will be updated and the deletion progress will be continued if + * there are still records to delete. + */ + onDeletion: function(id, success, response) { + var me = this; + if(!success) { + // Remove temporary local variables + delete me.delActionInfo; + // Hide progress dialog + OMV.MessageBox.hide(); + // Display error message + OMV.MessageBox.error(null, response); + } else { + if(me.delActionInfo.records.length > 0) { + var record = me.delActionInfo.records.pop(); + // Update progress dialog + me.updateDeletionProgress(); + // Execute deletion function + me.doDeletion(record); + } else { + // Remove temporary local variables + delete me.delActionInfo; + // Update and hide progress dialog + OMV.MessageBox.updateProgress(1, _("100% completed ...")); + OMV.MessageBox.hide(); + me.afterDeletion(); + } + } + }, + + /** + * Function that is called after the deletion has been successful finished. + */ + afterDeletion: function() { + var me = this; + if(me.mode === "remote") { + me.doReload(); + } + }, + + /** + * @private + * Private helper function to update the progress dialog. + */ + updateDeletionProgress: function() { + var me = this; + // Calculate percentage + var p = (me.delActionInfo.count - me.delActionInfo.records.length) / + me.delActionInfo.count; + // Create message text + var text = Math.round(100 * p) + _("% completed ..."); + // Update progress dialog + OMV.MessageBox.updateProgress(p, text); + }, + + /** + * Convenience function for setting the given toolbar button + * disabled/enabled. + * @param name The name of the toolbar button. + * @param disabled TRUE to disable the button, FALSE to enable. + * @return The button component, otherwise FALSE. + */ + setToolbarButtonDisabled: function(name, disabled) { + var me = this; + var result = false; + var btnCtrl = me.queryById(me.getId() + "-" + name); + if(!Ext.isEmpty(btnCtrl) && btnCtrl.isButton) + result = btnCtrl.setDisabled(disabled); + return result; + }, + + /** + * Helper function to get the top toolbar object. + * @return The paging toolbar object or NULL. + */ + getTopToolbar: function() { + return this.topToolbar; + }, + + /** + * Helper function to get the paging toolbar object. + * @return The paging toolbar object or NULL. + */ + getPagingToolbar: function() { + return this.pagingToolbar; + } +}); + + + diff --git a/gui/js/omv/module/admin/service/zfs/Zfs.js b/gui/js/omv/module/admin/service/zfs/Zfs.js new file mode 100644 index 0000000..0b5a037 --- /dev/null +++ b/gui/js/omv/module/admin/service/zfs/Zfs.js @@ -0,0 +1,8 @@ +OMV.WorkspaceManager.registerNode({ + id : "zfs", + path : "/service", + text : _("ZFS"), + icon16 : "images/zfs.png", + iconSvg : "images/zfs.svg" +}); + diff --git a/gui/rpc/zfs.inc b/gui/rpc/zfs.inc new file mode 100644 index 0000000..bdf8c44 --- /dev/null +++ b/gui/rpc/zfs.inc @@ -0,0 +1,313 @@ +registerMethod("getObjectTree"); + $this->registermethod("passParam"); + $this->registermethod("addObject"); + $this->registermethod("deleteObject"); + $this->registermethod("getProperties"); + $this->registermethod("setProperties"); + $this->registermethod("inherit"); + $this->registermethod("getSharedParams"); + $this->registermethod("createShare"); + } + + public function getObjectTree($params, $context) { + $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR)); + $objects = OMVModuleZFSUtil::getZFSFlatArray(); + $new = array(); + foreach ($objects as $a){ + $new[$a['parentid']][] = $a; + } + $tree = OMVModuleZFSUtil::createTree($new, $new['root']); + return $tree; + } + + public function passParam($params, $context) { + $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR)); + //$msg = "Key=" . $params['key'] . ";Value=" . $params['value'] . ";"; + //throw new OMVModuleZFSException($msg); + return array($params['key'] => $params['value']); + } + + public function addObject($params, $context) { + $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR)); + switch ($params['type']) { + case "filesystem": + $name = $params['path'] . "/" . $params['name']; + $tmp = new OMVModuleZFSDataset($name); + break; + case "snapshot": + $name = $params['path'] . "@" . $params['name']; + $tmp = new OMVModuleZFSSnapshot($name); + break; + case "volume": + $name = $params['path'] . "/" . $params['name']; + $tmp = new OMVModuleZFSZvol($name); + $tmp->create($params['size']); + break; + default: + throw new OMVModuleZFSException("Illegal type provided: " . $params['type']); + break; + } + } + + public function deleteObject($params, $context) { + $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR)); + switch ($params['type']) { + case "Filesystem" || "Clone": + $name = $params['name']; + $tmp = new OMVModuleZFSDataset($name); + $tmp->destroy(); + break; + case "Snapshot": + $name = $params['name']; + $tmp = new OMVModuleZFSSnapshot($name); + $tmp->destroy(); + break; + case "Volume": + $name = $params['name']; + $tmp = new OMVModuleZFSZvol($name); + $tmp->destroy(); + break; + default: + throw new OMVModuleZFSException("Illegal type provided: " . $params['type']); + break; + } + } + + public function getProperties($params, $context) { + $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR)); + $objects = array(); + $name = $params['name']; + switch ($params['type']) { + case "Filesystem" || "Clone": + $tmp = new OMVModuleZFSDataset($name); + break; + case "Snapshot": + $tmp = new OMVModuleZFSSnapshot($name); + break; + case "Volume": + $tmp = new OMVModuleZFSZvol($name); + break; + default: + throw new OMVModuleZFSException("Illegal type provided: " . $params['type']); + break; + } + $properties = $tmp->getProperties(); + foreach ($properties as $propertyk => $propertyv) { + if (!(strcmp($propertyv['source'], "-") == 0)) { + $objects[] = array('property' => $propertyk, + 'value' => $propertyv['value'], + 'source' => $propertyv['source'], + 'modified' => "false"); + } + } + return $objects; + } + + public function setProperties($params, $context) { + $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR)); + $objects = array(); + switch ($params['type']) { + case "Filesystem" || "Clone": + $tmp = new OMVModuleZFSDataset($params['name']); + break; + case "Snapshot": + $tmp = new OMVModuleZFSSnapshot($params['name']); + break; + case "Volume": + $tmp = new OMVModuleZFSZvol($params['name']); + break; + default: + throw new OMVModuleZFSException("Illegal type provided: " . $params['type']); + break; + } + foreach ($params['properties'] as $property) { + $objects[$property['property']] = $property['value']; + } + $tmp->setProperties($objects); + } + + public function inherit($params, $context) { + $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR)); + // Create a background process. + $bgStatusFilename = $this->createBgProcStatus(); + $pid = $this->fork(); + if($pid > 0) { // Parent process. + $this->initializeBgProcStatus($bgStatusFilename, $pid); + return $bgStatusFilename; + } + // Child process. + try { + $bgOutputFilename = $this->createBgProcOutput(); + $this->updateBgProcStatus($bgStatusFilename, "outputfilename", $bgOutputFilename); + switch ($params['type']) { + case "Filesystem" || "Clone": + $tmp = new OMVModuleZFSDataset($params['name']); + break; + case "Snapshot": + $tmp = new OMVModuleZFSSnapshot($params['name']); + break; + case "Volume": + $tmp = new OMVModuleZFSZvol($params['name']); + break; + default: + throw new OMVModuleZFSException("Illegal type provided: " . $params['type']); + break; + } + $tmp->inherit($params['property']); + $this->finalizeBgProcStatus($bgStatusFilename, $output); + exit(0); + } catch(Exception $e) { + $this->finalizeBgProcStatus($bgStatusFilename, "", $e); + exit(1); + } + } + + public function getSharedParams($params, $context) { + $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR)); + $objects = array(); + $ds = new OMVModuleZFSDataset($params['name']); + $mountpoint = $ds->getMountPoint(); + return array( + "mountpoint" => $mountpoint, + "name" => $params['name'], + "type" => $params['type']); + } + + public function createShare($params, $context) { + global $xmlConfig; + $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR)); + + //Get the UUID of the Pool + $pooluuid = OMVModuleZFSUtil::getUUIDbyName($params['name']); + preg_match('/^([A-Za-z0-9]+)\/?.*$/', $params['name'], $result); + $poolname = $result[1]; + unset($result); + + //Check if the UUID is already stored as an mntent object. If it isn't then create it. + $xpath = "//system/fstab/mntent[fsname=" . $pooluuid . "]"; + $object = $xmlConfig->get($xpath); + if(is_null($object)) { + $uuid = OMVUtil::uuid(); + $ds = new OMVModuleZFSDataset($poolname); + $dir = $ds->getMountPoint(); + $object = array( + "uuid" => $uuid, + "fsname" => $pooluuid, + "dir" => $dir, + "type" => "zfs", + "opts" => "rw,relatime,xattr", + "freq" => "0", + "passno" => "2" + ); + $xmlConfig->set("//system/fstab",array("mntent" => $object)); + $dispatcher = &OMVNotifyDispatcher::getInstance(); + $dispatcher->notify(OMV_NOTIFY_CREATE,"org.openmediavault.system.fstab.mntent", $object); + } + + //Get the mntent object and fetch it's uuid. + $object = $xmlConfig->get($xpath); + $mntentref = $object['uuid']; + + // Prepare the configuration object. Use the name of the shared + // folder as the relative directory name of the share. + switch ($params['type']) { + case "Filesystem" || "Clone": + $tmp = new OMVModuleZFSDataset($name); + break; + default: + throw new OMVModuleZFSException("Illegal type provided: " . $params['type']); + break; + } + + $uuid = OMVUtil::uuid(); + $pathName = $tmp->getMountPoint(); + $subdirs = preg_split('/\//',$pathName); + $reldirpath = $subdirs[count($subdirs)-1]; + $object = array( + "uuid" => $uuid, + "name" => $params['sharename'], + "comment" => $params['comment'], + "mntentref" => $mntentref, + "reldirpath" => $reldirpath + ); + + // Set the configuration object. + $success = FALSE; + // Check uniqueness. The share name must be global unique because + // the name is also used when exporting a shared folder via NFS for + // example. + $xpath = sprintf("//system/shares/sharedfolder[name='%s']", + $params['name']); + if(TRUE === $xmlConfig->exists($xpath)) { + throw new OMVException(OMVErrorMsg::E_CONFIG_OBJECT_UNIQUENESS, + gettext("A shared folder with the given name already exists")); + } + + // Add empty list of privileges per default. + $object['privileges'] = array(); + + // Append object to configuration. + $success = $xmlConfig->set("//system/shares", + array("sharedfolder" => $object)); + if(FALSE === $success) { + throw new OMVException(OMVErrorMsg::E_CONFIG_SET_OBJECT_FAILED); + } + + // Append the file mode field to the notification object if set. + // Defaults to 775. + $object['mode'] = "775"; + if(array_key_exists("mode", $params)) { + $object['mode'] = $params['mode']; + } + + // Change group owner of directory to configured default group, + // e.g. 'users'. + if(FALSE === chgrp($pathName, $GLOBALS['OMV_USERMGMT_DEFAULT_GROUP'])) { + throw new OMVException(OMVErrorMsg::E_MISC_FAILURE, + sprintf("Failed to set file group to '%s' for '%s'", + $GLOBALS['OMV_USERMGMT_DEFAULT_GROUP'], $pathName)); + } + + // Set the setgid bit. Setting this permission means that all files + // created in the folder will inherit the group of the folder rather + // than the primary group of the user who creates the file. + $mode = fileperms($pathName) | 02000; + if(FALSE === chmod($pathName, $mode)) { + throw new OMVException(OMVErrorMsg::E_MISC_FAILURE, + sprintf("Failed to set file mode to '%o' for '%s'", + $mode, $pathName)); + } + + // Notify configuration changes. + $dispatcher = &OMVNotifyDispatcher::getInstance(); + $dispatcher->notify(OMV_NOTIFY_CREATE,"org.openmediavault.system.shares.sharedfolder", $object); + // Return the configuration object. + return $object; + + } + +} + +// Register the RPC service. +$rpcServiceMgr = &OMVRpcServiceMgr::getInstance(); // Get the "root" instance for the Services +$rpcServiceMgr->registerService(new OMVRpcServiceZFS()); // Register a new instance of the RPC service described above +?> + diff --git a/images/zfs.png b/images/zfs.png new file mode 100644 index 0000000000000000000000000000000000000000..9dde61b590dbb3b7637754f2e512f24c6b8a3c02 GIT binary patch literal 278 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v_d#0*}aI z1_o|n5N2eUHAey{$X?><>&kwQkxPWnG*#)37Eoxhr;B4q#NoH$j(iOYJkgy0{}I+wxI)z4*}Q$iB}EY@Wq literal 0 HcmV?d00001 diff --git a/images/zfs.svg b/images/zfs.svg new file mode 100644 index 0000000..44a5cf0 --- /dev/null +++ b/images/zfs.svg @@ -0,0 +1,19 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + + diff --git a/images/zfs_disk.png b/images/zfs_disk.png new file mode 100644 index 0000000000000000000000000000000000000000..cf69142271e4e6f2f882c6ba78385515f8dd14e6 GIT binary patch literal 426 zcmV;b0agBqP)xK35;b7;%ZuS4&zsP}fZ)fX8w`^A>9@$s5I-Rw{$>ZJQ+poSg z-G#}OuX~fLqaS*mta$3gP3t;;d-p~Tp`uz?+7eE@cVuZujb@%h6^de}>W$`F6Im6t z)+U6&C}i8FT6gp)Le+$@HZ}F!!UP4`Glb9-qiQBOlw{|V^$!q=C$1y;U{8nSorj81 zGb+LtMMClkgyW5R6Go%fW$E`1D+WaonltV^_80UI&|7UL>Zb0M)6_?5&YUl~hP;ui zI2CWKY5H}yTC^|o>s)h9Ss>LE-(&mMa5Vf(?C;KI%N#jUBndUuNzL&&hT-Gt7o@+O Uz9$)@xBvhE07*qoM6N<$f+_sHs{jB1 literal 0 HcmV?d00001 diff --git a/images/zfs_mag.png b/images/zfs_mag.png new file mode 100644 index 0000000000000000000000000000000000000000..32e7eea0e269e9dbdeeca56fb522e5f0f0fbb3d9 GIT binary patch literal 358 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPqvkb4e!6e=!kj~GZE{-7*l5( zoFd&vl{SU499)qjd~lUx&+c5)Uy3A?tQE=J;uVKAuNgjJFKp;Pv%}puJmPSj#QP16`!2W@bbPeAbQK61+AG|8j=i@?TS={~;FWeFjfgKbLh*2~7aZbCHh# literal 0 HcmV?d00001 diff --git a/images/zfs_snap.png b/images/zfs_snap.png new file mode 100644 index 0000000000000000000000000000000000000000..8d5fcacd9430bca06f150e9aac3c49d3175c3eb9 GIT binary patch literal 866 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKVAk|>aSW-Lla#OkB*pOm{~`%S zFrCz57!et1`R0vm9ybTaiJBiD9-ds-%pM{sAt8~FWKe3b^Zx$&{|P_<(Z}G(u1%uJb(Tiq>MpMPL3fqCYnK1RFvV>t5*z5mM&v> z^ytx#M~@!vzI^FY7*NUo|11YL@-`?4I9$v>tE!{KDiUPaR2^l_V8GE=*x1(nUe<8g z_9;c}1xCl*&xzlwz7{n@lA+iyMD)3g(xH29xl`}``pgV9Mi6KW!$D~gX@$qkPMJsU zyql;r;mOek$@_OryJRfif4NuKoqWzylhxQc)Kk;&n0L0hV$BM>D^(jS{Y5P6p^jN7 zctC;2;o|vN)K-WbWjxV#z#bcmx-Imkw1` z^3c3^%XO{jRrd1p_E1v}avo3+a6P!3(YyP|zmFTjHl!y>CxpuyDGdE?weY`l4Nu8XWB$bWS-SP(y$OOYEbq>}J@?xpjm<$*I{f_#t;=?keqKCxY}2_8 zj;(SqPh12$r!4vP`+L4e)enBp?K(O- zJagvE;fdc@lenYs@i9AK=m8T{Qc7y-RUsjvs53{8E +# @copyright Copyright (c) 2009-2013 Volker Theile +# +# OpenMediaVault is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# any later version. +# +# OpenMediaVault is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with OpenMediaVault. If not, see . + +OMV_PACKAGE := $(shell pwd | sed 's|.*/||') +OMV_POT_DIR := $(CURDIR)/usr/share/openmediavault/locale +OMV_POT_FILE := $(OMV_PACKAGE).pot +OMV_TRANSIFEX_SLUG := $(OMV_PACKAGE)pot + +omv_pull_po: + tx --root="$(CURDIR)/../" pull --all \ + --resource=$(OMV_PACKAGE).$(OMV_TRANSIFEX_SLUG) + +omv_push_pot: + tx --root="$(CURDIR)/../" push --source \ + --resource=$(OMV_PACKAGE).$(OMV_TRANSIFEX_SLUG) + +omv_build_pot: + dh_testdir + dh_clean + echo "Building PO template file ..." >&2 + mkdir -p $(OMV_POT_DIR) + find $(CURDIR) \( -iname *.js -o -iname *.php -o -iname *.inc \) \ + -type f -print0 | xargs -0r xgettext --keyword=_ \ + --output-dir=$(OMV_POT_DIR) --output=$(OMV_POT_FILE) \ + --force-po --no-location --no-wrap --sort-output \ + --package-name=$(OMV_PACKAGE) - + # Remove '#, c-format' comments, otherwise manuall upload of translation + # files confuses Transifex. + sed --in-place '/^#, c-format/d' $(OMV_POT_DIR)/$(OMV_POT_FILE) + +omv_clean_scm: + dh_testdir + echo "Removing SCM files ..." >&2 + find $(CURDIR)/debian/$(OMV_PACKAGE) \( -name .svn -o -name .git \) \ + -type d -print0 -prune | xargs -0r rm -rf + +omv_build_doc: debian/doxygen.conf + mkdir -p debian/doxygen + doxygen $< + +source: clean + dpkg-buildpackage -S -us -uc + +.PHONY: omv_pull_po omv_push_pot omv_build_pot omv_clean_scm omv_build_doc +.PHONY: source -- 2.39.2