]> git.datanom.net - omvzfs.git/commitdiff
GUI part from Niclas as well as debian build script
authorMichael Rasmussen <mir@datanom.net>
Wed, 16 Jul 2014 00:19:47 +0000 (02:19 +0200)
committerMichael Rasmussen <mir@datanom.net>
Wed, 16 Jul 2014 00:19:47 +0000 (02:19 +0200)
12 files changed:
build-package.sh [new file with mode: 0755]
gui/js/omv/module/admin/service/zfs/Overview.js [new file with mode: 0644]
gui/js/omv/module/admin/service/zfs/TreePanel.js [new file with mode: 0644]
gui/js/omv/module/admin/service/zfs/Zfs.js [new file with mode: 0644]
gui/rpc/zfs.inc [new file with mode: 0644]
images/zfs.png [new file with mode: 0644]
images/zfs.svg [new file with mode: 0644]
images/zfs_disk.png [new file with mode: 0644]
images/zfs_mag.png [new file with mode: 0644]
images/zfs_snap.png [new file with mode: 0644]
openmediavault-zfs/README.md [new file with mode: 0644]
openmediavault-zfs/openmediavault.mk [new file with mode: 0644]

diff --git a/build-package.sh b/build-package.sh
new file mode 100755 (executable)
index 0000000..4a30205
--- /dev/null
@@ -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 (file)
index 0000000..fe72e89
--- /dev/null
@@ -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 (file)
index 0000000..26a25ce
--- /dev/null
@@ -0,0 +1,646 @@
+/**
+ * This file is part of OpenMediaVault.
+ *
+ * @license   http://www.gnu.org/licenses/gpl.html GPL Version 3
+ * @author    Volker Theile <volker.theile@openmediavault.org>
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+// 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 (file)
index 0000000..0b5a037
--- /dev/null
@@ -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 (file)
index 0000000..bdf8c44
--- /dev/null
@@ -0,0 +1,313 @@
+<?php
+
+require_once("openmediavault/object.inc");
+require_once("openmediavault/config.inc");
+require_once("openmediavault/error.inc");
+require_once("openmediavault/util.inc");
+require_once("openmediavault/rpcservice.inc");
+require_once("openmediavault/notify.inc");
+require_once("zfs/Utils.php");
+require_once("zfs/Dataset.php");
+require_once("zfs/Snapshot.php");
+require_once("zfs/Zvol.php");
+
+class OMVRpcServiceZFS extends OMVRpcServiceAbstract {
+       public function getName() { return "ZFS";} // RPC Service name. Same as in .js files
+
+               /* Initialize the RPC service. Different methods of the RPC service are declared here*/
+               public function initialize() {
+                       $this->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 (file)
index 0000000..9dde61b
Binary files /dev/null and b/images/zfs.png differ
diff --git a/images/zfs.svg b/images/zfs.svg
new file mode 100644 (file)
index 0000000..44a5cf0
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
+ width="32.000000pt" height="32.000000pt" viewBox="0 0 32.000000 32.000000"
+ preserveAspectRatio="xMidYMid meet">
+<metadata>
+Created by potrace 1.11, written by Peter Selinger 2001-2013
+</metadata>
+<g transform="translate(0.000000,32.000000) scale(0.100000,-0.100000)"
+fill="#000000" stroke="none">
+<path d="M0 160 l0 -160 160 0 160 0 0 160 0 160 -160 0 -160 0 0 -160z m300
+0 l0 -140 -140 0 -140 0 0 140 0 140 140 0 140 0 0 -140z"/>
+<path d="M64 219 c-29 -30 -29 -30 -4 -21 19 7 42 5 89 -11 l63 -20 -81 -25
+c-44 -14 -82 -31 -85 -39 -3 -8 -2 -12 3 -9 4 3 33 0 63 -6 63 -13 103 -6 118
+23 10 19 10 20 -9 10 -23 -13 -80 -14 -99 -3 -8 6 6 13 39 20 29 7 68 22 87
+34 l36 22 -50 11 c-27 7 -66 19 -88 28 -50 22 -47 22 -82 -14z"/>
+</g>
+</svg>
diff --git a/images/zfs_disk.png b/images/zfs_disk.png
new file mode 100644 (file)
index 0000000..cf69142
Binary files /dev/null and b/images/zfs_disk.png differ
diff --git a/images/zfs_mag.png b/images/zfs_mag.png
new file mode 100644 (file)
index 0000000..32e7eea
Binary files /dev/null and b/images/zfs_mag.png differ
diff --git a/images/zfs_snap.png b/images/zfs_snap.png
new file mode 100644 (file)
index 0000000..8d5fcac
Binary files /dev/null and b/images/zfs_snap.png differ
diff --git a/openmediavault-zfs/README.md b/openmediavault-zfs/README.md
new file mode 100644 (file)
index 0000000..2e64fca
--- /dev/null
@@ -0,0 +1,2 @@
+openmediavault-zfs
+==================
diff --git a/openmediavault-zfs/openmediavault.mk b/openmediavault-zfs/openmediavault.mk
new file mode 100644 (file)
index 0000000..0233acb
--- /dev/null
@@ -0,0 +1,63 @@
+# -*- mode: makefile; coding: utf-8 -*-
+#
+# This file is part of OpenMediaVault.
+#
+# @license   http://www.gnu.org/licenses/gpl.html GPL Version 3
+# @author    Volker Theile <volker.theile@openmediavault.org>
+# @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 <http://www.gnu.org/licenses/>.
+
+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
This page took 0.102958 seconds and 5 git commands to generate.