diff --git a/src/auth.js b/src/auth.js
index 048051bb9d375333ed4d0cfe072fd0e9b3dd4578..e62afe0e1838851034890fbea4afa1af4d756843 100644
--- a/src/auth.js
+++ b/src/auth.js
@@ -86,9 +86,20 @@ export function getSession() {
 }
 
 export class ResourceHandler {
-  constructor(resource, searchKeys = false) {
+  /* Handler to get and manipulate resource items
+   *
+   * resource: String of the resource to accessm e,g, 'events'
+   * searchKeys: keys in the resource item on which to perform search, i.e.
+   *   when search is set, any of these keys may match the search pattern
+   *   E.g. for an event, this may be ['title_de', 'title_en', 'location']
+   * updateCallback: method with argument of newData
+   *   is called additionally to the promise resolve at any time that data of an item is
+   *   changed
+   */
+  constructor(resource, searchKeys = false, updateCallback = () => {}) {
     this.resource = resource;
     this.searchKeys = searchKeys || config[resource].searchKeys;
+    this.updateCallback = updateCallback;
     this.noPatchKeys = [
       '_etag', '_id', '_created', '_links', '_updated',
       ...(config[resource].notPatchableKeys || [])];
@@ -188,6 +199,7 @@ export class ResourceHandler {
             resetSession();
             reject();
           } else {
+            this.updateCallback(response.data);
             resolve(response.data);
           }
         }).catch((e) => {
@@ -225,6 +237,7 @@ export class ResourceHandler {
             resetSession();
             reject();
           } else {
+            this.updateCallback(response.data);
             resolve(response.data);
           }
         }).catch((e) => {
diff --git a/src/events/newEvent.js b/src/events/newEvent.js
index 859a29b6fbe85972b9df87fa9ed0122aad277a79..55f902fa7bf904fcde68a64358163eb15bb8514d 100644
--- a/src/events/newEvent.js
+++ b/src/events/newEvent.js
@@ -2,7 +2,7 @@ import m from 'mithril';
 import { RaisedButton, RadioGroup, Slider } from 'polythene-mithril';
 import { styler } from 'polythene-core-css';
 import { apiUrl } from 'networkConfig';
-import EditView from '../views/editView';
+import { EditView } from '../views/editView';
 import { fileInput } from '../views/elements';
 
 const style = [
diff --git a/src/groups/groupTool.js b/src/groups/groupTool.js
index 47fb9253ab06b7ef24f2be94e4bf29d05e47be16..3d7364272696979f306e632c71cd52aa2fa41177 100644
--- a/src/groups/groupTool.js
+++ b/src/groups/groupTool.js
@@ -1,16 +1,18 @@
 import m from 'mithril';
 import viewGroup from './viewGroup';
 import newGroup from './newGroup';
+import ItemTool from '../views/itemTool';
 
-export default class GroupView {
+export default class GroupTool extends ItemTool {
   constructor() {
-    this.edit = false;
+    super('groups', { moderator: 1 });
   }
 
-  view() {
-    if (this.edit) {
-      return m(newGroup, { onfinish: () => { this.edit = false; m.redraw(); } });
-    }
-    return m(viewGroup, { onEdit: () => { this.edit = true; } });
+  detailView() {
+    return m(viewGroup, { handler: this.handler, data: this.data });
+  }
+
+  editView() {
+    return m(newGroup, { onCancel: () => { this.modus = 'view'; } });
   }
 }
diff --git a/src/groups/newGroup.js b/src/groups/newGroup.js
index 5b9c01985d6df230b925af7c550cfbc037df080a..7fab50512fd120bb3ae3d898c584fbbdbcc61138 100644
--- a/src/groups/newGroup.js
+++ b/src/groups/newGroup.js
@@ -1,8 +1,8 @@
 import m from 'mithril';
-import { RaisedButton, TextField } from 'polythene-mithril';
+import { TextField } from 'polythene-mithril';
 import SelectList from '../views/selectList';
 import DatalistController from '../listcontroller';
-import EditView from '../views/editView';
+import { EditView, EditLayout } from '../views/editView';
 
 
 export default class NewGroup extends EditView {
@@ -14,23 +14,18 @@ export default class NewGroup extends EditView {
     );
   }
 
-  view() {
+  view({ attrs: { onCancel = () => {} }}) {
     if (!this.data) return '';
-
-    const submitButton = m(RaisedButton, {
-      disabled: !this.valid,
-      label: 'Submit',
-      events: {
-        onclick: () => {
-          // excahgne moderator object with string of id
-          const { moderator } = this.data;
-          if (moderator) { this.data.moderator = `${moderator._id}`; }
-          this.submit();
-        },
+    return m(EditLayout, {
+      title: 'Edit Group',
+      onSubmit: () => {
+        // excahgne moderator object with string of id
+        const { moderator } = this.data;
+        if (moderator) { this.data.moderator = `${moderator._id}`; }
+        this.submit();
       },
-    });
-
-    return m('div.mywrapper', [
+      onCancel,
+    }, m('div.mywrapper', [
       m('h3', 'Add a New Group'),
       ...this.renderPage({
         name: { type: 'text', label: 'Group Name' },
@@ -55,8 +50,6 @@ export default class NewGroup extends EditView {
           },
         })),
       ]),
-      m('br'),
-      submitButton,
-    ]);
+    ]));
   }
 }
diff --git a/src/groups/viewGroup.js b/src/groups/viewGroup.js
index 6214b0f25d63be03bb0f7b9e9189d9716f70b75e..cb9cd74527d55c781d5d9da79b5b1646a7c1cbd6 100644
--- a/src/groups/viewGroup.js
+++ b/src/groups/viewGroup.js
@@ -163,68 +163,46 @@ class EmailTable {
   }
 }
 
-export default class viewGroup extends ItemView {
-  constructor() {
-    super('groups', { moderator: 1 });
-  }
-
-  oninit() {
-    this.handler.getItem(this.id, this.embedded).then((item) => {
-      this.data = item;
-      m.redraw();
-    });
-  }
-
-  view({ attrs: { onEdit } }) {
-    if (!this.data) return '';
-
-    return m('div.maincontainer', {
-      style: { height: '100%', 'overflow-y': 'scroll' },
-    }, [
-      // Button to edit the Group
-      m(RaisedButton, {
-        element: 'div',
-        label: 'Edit Group',
-        border: true,
-        events: { onclick: onEdit },
-      }),
+export default class viewGroup {
+  view({ attrs: { handler, data } }) {
+    return m('div.maincontainer', [
       // this div is the title line
       m('div.maincontainer', [
-        m('h1', { style: { 'margin-top': '0px', 'margin-bottom': '0px' } }, this.data.name),
-        this.data.moderator ? m(Property, {
+        m('h1', { style: { 'margin-top': '0px', 'margin-bottom': '0px' } }, data.name),
+        data.moderator ? m(Property, {
           title: 'Moderator',
-          onclick: () => { m.route.set(`/users/${this.data.moderator._id}`); },
-        }, `${this.data.moderator.firstname} ${this.data.moderator.lastname}`) : '',
+          onclick: () => { m.route.set(`/users/${data.moderator._id}`); },
+        }, `${data.moderator.firstname} ${data.moderator.lastname}`) : '',
       ]),
       m('div.viewcontainer', [
         // now-column layout: This first column are the members
         m('div.viewcontainercolumn', [
           m('h4', 'Members'),
-          m(MembersTable, { group: this.id }),
+          m(MembersTable, { group: data._id }),
         ]),
         // the second column contains receive_from and forward_to emails
         m('div.viewcontainercolumn', [
           m(EmailTable, {
-            list: this.data.receive_from || [],
+            list: data.receive_from || [],
             onSubmit: (newItem) => {
-              const oldList = this.data.receive_from || [];
-              this.handler.patch({
-                _id: this.data._id,
-                _etag: this.data._etag,
+              const oldList = data.receive_from || [];
+              handler.patch({
+                _id: data._id,
+                _etag: data._etag,
                 receive_from: [...oldList, newItem],
-              }).then((newData) => { this.data = newData; });
+              });
             },
             onRemove: (item) => {
-              const oldList = this.data.receive_from;
+              const oldList = data.receive_from;
               // remove the first occurence of the given item-string
               const index = oldList.indexOf(item);
               if (index !== -1) {
                 oldList.splice(index, 1);
-                this.handler.patch({
-                  _id: this.data._id,
-                  _etag: this.data._etag,
+                handler.patch({
+                  _id: data._id,
+                  _etag: data._etag,
                   receive_from: oldList,
-                }).then((newData) => { this.data = newData; });
+                }).then(() => m.redraw());
               }
             },
           }),
diff --git a/src/jobs/newJob.js b/src/jobs/newJob.js
index 37c95210aaca0a3a95f5f6f4368f82b12cf400d8..fad8748ac9e9809c3ca5b7e74427d74e35a26a5b 100644
--- a/src/jobs/newJob.js
+++ b/src/jobs/newJob.js
@@ -1,6 +1,6 @@
 import m from 'mithril';
 import { RaisedButton } from 'polythene-mithril';
-import EditView from '../views/editView';
+import { EditView } from '../views/editView';
 
 
 export default class newJob extends EditView {
diff --git a/src/layout.js b/src/layout.js
index 77f34a0a3962e0cac4927cbafe6f0e2d036fb9b8..40c9b7f0f97c4803875eae8ac178e2a4ed08c91d 100644
--- a/src/layout.js
+++ b/src/layout.js
@@ -1,6 +1,6 @@
 import m from 'mithril';
 import '@material/drawer';
-import { List, ListTile, Icon, Toolbar, ToolbarTitle } from 'polythene-mithril';
+import { List, ListTile, Icon, Toolbar, ToolbarTitle, Dialog } from 'polythene-mithril';
 import { styler } from 'polythene-core-css';
 import { icons } from './views/elements';
 import { resetSession } from './auth';
@@ -106,6 +106,8 @@ export default class Layout {
         ),
         m('div.wrapper-content', children),
       ]),
+      // dialog element will show when Dialog.show() is called, this is only a placeholder
+      m(Dialog),
     ]);
   }
 }
diff --git a/src/users/editUser.js b/src/users/editUser.js
index 8efb2210326ed9655e0b92dceeeaec7b0ba42178..46e1bc1588709b741f613b10feaef56b4de03308 100644
--- a/src/users/editUser.js
+++ b/src/users/editUser.js
@@ -1,6 +1,6 @@
 import m from 'mithril';
 import { RaisedButton, RadioGroup } from 'polythene-mithril';
-import EditView from '../views/editView';
+import { EditView } from '../views/editView';
 
 
 export default class UserEdit extends EditView {
diff --git a/src/views/editView.js b/src/views/editView.js
index c1c6dcc46a6ecb61e73cc4a5446dbfc2c1512676..2021f5d8caa8c9bdc67bcca08aff89b9ffcb0ae6 100644
--- a/src/views/editView.js
+++ b/src/views/editView.js
@@ -1,8 +1,8 @@
 import Ajv from 'ajv';
-import { Checkbox } from 'polythene-mithril';
+import { Checkbox, IconButton, Toolbar, ToolbarTitle, Button } from 'polythene-mithril';
 import { apiUrl } from 'networkConfig';
 import ItemView from './itemView';
-import { textInput, datetimeInput, numInput } from './elements';
+import { textInput, datetimeInput, numInput, icons } from './elements';
 
 const m = require('mithril');
 
@@ -15,7 +15,23 @@ const objectNameForResource = {
   events: 'Event',
 };
 
-export default class EditView extends ItemView {
+export class EditLayout {
+  view({ attrs: { title, onSubmit = () => {}, onCancel = () => {} }, children }) {
+    return m('div', { style: { 'background-color': 'white' } }, [
+      m(Toolbar, [
+        m(IconButton, {
+          icon: { svg: { content: m.trust(icons.clear) } },
+          events: { onclick: onCancel },
+        }),
+        m(ToolbarTitle, title),
+        m(Button, { label: 'submit', events: { onclick: onSubmit } }),
+      ]),
+      children,
+    ]);
+  }
+}
+
+export class EditView extends ItemView {
   /* Extension of ItemView to edit a data item
    *
    * Requires:
diff --git a/src/views/itemTool.js b/src/views/itemTool.js
new file mode 100644
index 0000000000000000000000000000000000000000..7b04c6d5fe28975f12feeca86ad4e909296ba381
--- /dev/null
+++ b/src/views/itemTool.js
@@ -0,0 +1,105 @@
+import m from 'mithril';
+import {
+  RaisedButton,
+  Dialog,
+  Button,
+  Toolbar,
+  IconButton,
+  ToolbarTitle,
+} from 'polythene-mithril';
+import { ResourceHandler } from '../auth';
+import { icons } from './elements';
+
+
+export default class ItemTool {
+  constructor(resource, embedded) {
+    this.id = m.route.param('id');
+    if (this.id) {
+      this.modus = 'view';
+    } else {
+      this.modus = 'new';
+    }
+    this.handler = new ResourceHandler(resource, false, (newData) => { this.data = newData; });
+    this.embedded = embedded || {};
+    // by default, we go to the resource list after deletion of an item
+    // this may be overwritten with other behaviour
+    this.onDelete = () => { console.log('on delete'); m.route.set(`/${resource}`); };
+  }
+
+  oninit() {
+    if (this.id) {
+      this.handler.getItem(this.id, this.embedded).then((item) => {
+        this.data = item;
+        m.redraw();
+      });
+    }
+  }
+
+  creationView() {
+    console.log(`creation for ${this.resource} not found`);
+    return null;
+  }
+
+  /*
+   * Should display this.data
+   */
+  detailView() {
+    console.log(`detail view for ${this.resource} not found`);
+    return null;
+  }
+
+  /*
+   * To edit an existing item. Has to implement this.onSubmit, which is called
+   */
+  editView() {
+    console.log(`edit view for ${this.resource} not found`);
+    return null;
+  }
+
+  delete() {
+    Dialog.show({
+      body: 'Are you sure you want to delete this item?',
+      backdrop: true,
+      footerButtons: [
+        m(Button, {
+          label: 'Cancel',
+          events: { onclick: () => Dialog.hide() },
+        }),
+        m(Button, {
+          label: 'Delete',
+          events: {
+            onclick: () => {
+              Dialog.hide();
+              this.handler.delete(this.data).then(this.onDelete);
+            },
+          },
+        })],
+    });
+  }
+
+  view() {
+    if (this.modus === 'new') {
+      return this.creationView();
+    } else if (this.modus === 'edit') {
+      return this.editView();
+    }
+    if (!this.data) return '';
+    return m('div', { style: { height: '100%', 'overflow-y': 'scroll' } }, [
+      m('div', { style: { display: 'flex' } }, [
+        m(RaisedButton, {
+          element: 'div',
+          label: 'Edit',
+          border: true,
+          events: { onclick: () => { this.modus = 'edit'; } },
+        }),
+        m(RaisedButton, {
+          className: 'red-row-button',
+          label: 'Delete',
+          border: true,
+          events: { onclick: () => this.delete() },
+        }),
+      ]),
+      this.detailView(),
+    ]);
+  }
+}
diff --git a/src/views/itemView.js b/src/views/itemView.js
index bed55516635cb9327af137a005e1a894f9aa1d9b..6fda6b46716f20aa2903ff27b8b437c1083411bc 100644
--- a/src/views/itemView.js
+++ b/src/views/itemView.js
@@ -1,9 +1,9 @@
+import m from 'mithril';
 import { ResourceHandler } from '../auth';
-
-const m = require('mithril');
+import { Dialog, Button } from 'polythene-mithril';
 
 export default class ItemView {
-  /* Basic class show a data item
+  /* Basic class to show a data item
    *
    *  Required:
    *  - call constructor with 'resource'