Index: /Dev/trunk/src/client/qed-client/model/classes/_Class.js
===================================================================
--- /Dev/trunk/src/client/qed-client/model/classes/_Class.js	(revision 492)
+++ /Dev/trunk/src/client/qed-client/model/classes/_Class.js	(revision 493)
@@ -34,5 +34,6 @@
         save: function(obj) {
             return this._store.put(this._doSerialize(obj))
-            .otherwise(lang.hitch(this,'_deserializeError'));
+            .then(lang.hitch(this,'_doDeserialize'),
+                  lang.hitch(this,'_deserializeError'));
         },
         remove: function(objOrId,rev) {
Index: /Dev/trunk/src/client/qed-client/model/widgets/QuestionEditorToolkit.js
===================================================================
--- /Dev/trunk/src/client/qed-client/model/widgets/QuestionEditorToolkit.js	(revision 492)
+++ /Dev/trunk/src/client/qed-client/model/widgets/QuestionEditorToolkit.js	(revision 493)
@@ -30,6 +30,4 @@
             { type: "Header" },
             { type: "Text" },
-            { type: "Image" },
-            { type: "ExternalMedia" },
             { type: "Divider" }
         ],
@@ -44,6 +42,4 @@
             "Header": "Header",
             "Text": "Text",
-            "Image": "Image",
-            "ExternalMedia": "External media",
             "Divider": "Divider",
             "StringInput": "Text line",
@@ -56,6 +52,4 @@
             "Header": "Header",
             "Text": "TextBox",
-            "Image": "Image",
-            "ExternalMedia": "External",
             "Divider": "Divider",
             "StringInput": "Text",
Index: /Dev/trunk/src/client/qed-client/model/widgets/SurveyRunWidget.js
===================================================================
--- /Dev/trunk/src/client/qed-client/model/widgets/SurveyRunWidget.js	(revision 492)
+++ /Dev/trunk/src/client/qed-client/model/widgets/SurveyRunWidget.js	(revision 493)
@@ -17,6 +17,12 @@
             if ( !value.startDate ) { delete value.startDate; }
             if ( !value.endDate ) { delete value.endDate; }
-            value.respondentCanDeleteOwnResponse = value.respondentCanDeleteOwnResponse[0] === true;
+            value.respondentCanDeleteOwnResponse =
+                value.respondentCanDeleteOwnResponse.length > 0;
             return value;
+        },
+        _setValueAttr: function(value) {
+            value.respondentCanDeleteOwnResponse =
+                value.respondentCanDeleteOwnResponse ? ["on"] : [];
+            this.inherited(arguments);
         }
     });
Index: /Dev/trunk/src/client/qed-client/model/widgets/SurveySummary.js
===================================================================
--- /Dev/trunk/src/client/qed-client/model/widgets/SurveySummary.js	(revision 492)
+++ /Dev/trunk/src/client/qed-client/model/widgets/SurveySummary.js	(revision 493)
@@ -19,5 +19,5 @@
             this.titleNode.innerHTML = survey.title || "";
             domAttr.set(this.titleNode, "href", survey && surveys.getObjectPath(survey));
-            this.descriptionNode.innerHTML = survey.description;
+            this.descriptionNode.innerHTML = survey.description || "";
             this.questionsNode.innerHTML = survey.questions.length;
         }
Index: /Dev/trunk/src/client/qed-client/model/widgets/questions/MultipleChoiceInputConfigWidget.js
===================================================================
--- /Dev/trunk/src/client/qed-client/model/widgets/questions/MultipleChoiceInputConfigWidget.js	(revision 492)
+++ /Dev/trunk/src/client/qed-client/model/widgets/questions/MultipleChoiceInputConfigWidget.js	(revision 493)
@@ -52,11 +52,7 @@
         _getValueAttr: function() {
             var value = this.inherited(arguments);
-            value.allowMultiple = value.allowMultiple && value.allowMultiple.length > 0;
+            value.allowMultiple = value.allowMultiple.length > 0;
             value.type = this.type;
             return value;
-        },
-        _setValueAttr: function(value) {
-            value.allowMultiple = value.allowMultiple === true ? ["on"] : [];
-            this.inherited(arguments);
         },
         onAddItem: function(evt) {
Index: /Dev/trunk/src/client/qed-client/model/widgets/questions/NumberInputConfigWidget.js
===================================================================
--- /Dev/trunk/src/client/qed-client/model/widgets/questions/NumberInputConfigWidget.js	(revision 492)
+++ /Dev/trunk/src/client/qed-client/model/widgets/questions/NumberInputConfigWidget.js	(revision 493)
@@ -10,4 +10,7 @@
             var value = this.inherited(arguments);
             value.type = this.type;
+            if ( !value.min ) { delete value.min; }
+            if ( !value.max ) { delete value.max; }
+            if ( !value.places ) { delete value.places; }
             return value;
         }
Index: /Dev/trunk/src/client/qed-client/model/widgets/questions/TextInputConfigWidget.js
===================================================================
--- /Dev/trunk/src/client/qed-client/model/widgets/questions/TextInputConfigWidget.js	(revision 492)
+++ /Dev/trunk/src/client/qed-client/model/widgets/questions/TextInputConfigWidget.js	(revision 493)
@@ -10,4 +10,5 @@
             var value = this.inherited(arguments);
             value.type = this.type;
+            if ( !value.maxLength ) { delete value.maxLength; }
             return value;
         }
Index: /Dev/trunk/src/client/qed-client/model/widgets/questions/templates/MultipleChoiceInputConfigRowWidget.html
===================================================================
--- /Dev/trunk/src/client/qed-client/model/widgets/questions/templates/MultipleChoiceInputConfigRowWidget.html	(revision 492)
+++ /Dev/trunk/src/client/qed-client/model/widgets/questions/templates/MultipleChoiceInputConfigRowWidget.html	(revision 493)
@@ -1,3 +1,7 @@
 <form>
-  <div data-dojo-type="dijit/form/ValidationTextBox" name="text" required="required" data-dojo-attach-point="textBox"></div><div data-dojo-type="dijit/form/Button" data-dojo-attach-event="onClick:onDestroy">X</div>
+  <div data-dojo-type="dijit/form/ValidationTextBox"
+       name="text" required="required"
+       data-dojo-attach-point="textBox"></div>
+  <div data-dojo-type="dijit/form/Button"
+       data-dojo-attach-event="onClick:onDestroy">X</div>
 </form>
Index: /Dev/trunk/src/client/qed-client/model/widgets/questions/templates/MultipleChoiceInputConfigWidget.html
===================================================================
--- /Dev/trunk/src/client/qed-client/model/widgets/questions/templates/MultipleChoiceInputConfigWidget.html	(revision 492)
+++ /Dev/trunk/src/client/qed-client/model/widgets/questions/templates/MultipleChoiceInputConfigWidget.html	(revision 493)
@@ -6,4 +6,5 @@
   <div data-dojo-attach-point="itemsNode">
   </div>
-  <div data-dojo-type="dijit/form/Button" data-dojo-attach-event="onClick:onAddItem">Add item</div>
+  <div data-dojo-type="dijit/form/Button"
+       data-dojo-attach-event="onClick:onAddItem">Add item</div>
 </form>
Index: /Dev/trunk/src/client/qed-client/model/widgets/templates/SurveyRunWidget.html
===================================================================
--- /Dev/trunk/src/client/qed-client/model/widgets/templates/SurveyRunWidget.html	(revision 492)
+++ /Dev/trunk/src/client/qed-client/model/widgets/templates/SurveyRunWidget.html	(revision 493)
@@ -25,6 +25,5 @@
         <input type="text" name="respondentCanDeleteOwnResponse"
                class="qedField"
-               data-dojo-type="dijit/form/CheckBox"
-               data-dojo-props="'value':true" />
+               data-dojo-type="dijit/form/CheckBox" />
     </div>
 
Index: /Dev/trunk/src/client/qed-client/pages/question.js
===================================================================
--- /Dev/trunk/src/client/qed-client/pages/question.js	(revision 492)
+++ /Dev/trunk/src/client/qed-client/pages/question.js	(revision 493)
@@ -25,19 +25,31 @@
         _refresh: function() {
             this.titleNode.innerHTML = this.object.title || "";
-            this.propertiesForm.set('value',this.object);
-            this.contentList.set('value',this.object.content);
+            this.propertiesForm.set('value',this.object,null);
+            this.contentList.set('value',this.object.content,null);
         },
         _handlePropertiesChange: function() {
-            lang.mixin(this.object,this.propertiesForm.get('value'));
+            this._updateObject();
             this.markDirty();
             this._refresh();
         },
         _handleContentChange: function() {
-            this.object.content = this.contentList.get('value');
+            this._updateObject();
             this.markDirty();
             this._refresh();
         },
+        _updateObject: function() {
+            var pValid = this.propertiesForm.validate();
+            if ( pValid ) {
+                lang.mixin(this.object,this.propertiesForm.get('value'));
+            }
+            var cValid = this.contentList.validate();
+            if ( cValid ) {
+                this.object.content = this.contentList.get('value');
+            }
+            this._isValid = pValid && cValid;
+            return this._isValid;
+        },
         _save: function() {
-            if ( this._isValid ) {
+            if ( this._updateObject() ) {
                 return this.inherited(arguments);
             } else {
@@ -65,6 +77,4 @@
         },
         markDirty: function() {
-            this._isValid = this.propertiesForm.validate() &&
-                            this.contentList.validate();
             this.saveBtn.set('disabled',!this._isValid);
             this.saveAndCloseBtn.set('disabled',!this._isValid);
@@ -73,5 +83,4 @@
         },
         markClean: function() {
-            this._isValid = true;
             this.saveBtn.set('disabled',true);
             this.saveAndCloseBtn.set('disabled',true);
Index: /Dev/trunk/src/client/qed-client/pages/survey.js
===================================================================
--- /Dev/trunk/src/client/qed-client/pages/survey.js	(revision 492)
+++ /Dev/trunk/src/client/qed-client/pages/survey.js	(revision 493)
@@ -59,7 +59,6 @@
         _refresh: function() {
             this.titleNode.innerHTML = this.object.title || "(set title in properties)";
-            this.propertiesForm.set('value',{survey:this.object});
-            this.questionList.set('value',
-                                  this.object.questions);
+            this.propertiesForm.set('value',{survey:this.object},null);
+            this.questionList.set('value',this.object.questions,null);
         },
         _includeQuestion: function(question) {
Index: /Dev/trunk/src/client/qed-client/pages/surveyRun.js
===================================================================
--- /Dev/trunk/src/client/qed-client/pages/surveyRun.js	(revision 492)
+++ /Dev/trunk/src/client/qed-client/pages/surveyRun.js	(revision 493)
@@ -22,4 +22,5 @@
         templateString: template,
         classStore: surveyRuns,
+        _isValid: false,
         startup: function() {
             if ( this._started ) { return; }
@@ -30,6 +31,6 @@
         _refresh: function() {
             this.titleNode.innerHTML = this.object.title || "";
-            this.surveySummaryWidget.set('value',this.object.survey);
-            this.surveyRunWidget.set('value',this.object);
+            this.surveySummaryWidget.set('value',this.object.survey,null);
+            this.surveyRunWidget.set('value',this.object,null);
             this._refreshURL();
             this._loadResponses();
@@ -96,9 +97,9 @@
         },
         _updateObject: function() {
-            var valid = this.surveyRunWidget.validate();
-            if ( valid ) {
+            this._isValid = this.surveyRunWidget.validate();
+            if ( this._isValid ) {
                 lang.mixin(this.object,this.surveyRunWidget.get('value'));
             }
-            return valid;
+            return this._isValid;
         },
         _onSave: function(evt) {
@@ -122,6 +123,6 @@
         },
         markDirty: function() {
-            this.saveBtn.set('disabled',false);
-            this.saveAndCloseBtn.set('disabled',false);
+            this.saveBtn.set('disabled',!this._isValid);
+            this.saveAndCloseBtn.set('disabled',!this._isValid);
             this.discardBtn.set('label','Discard & Close');
             this.inherited(arguments);
Index: v/trunk/src/client/qed-client/widgets/_ComplexValueMixin.coffee
===================================================================
--- /Dev/trunk/src/client/qed-client/widgets/_ComplexValueMixin.coffee	(revision 492)
+++ 	(revision )
@@ -1,49 +1,0 @@
-define [
-    "dijit/_Container",
-    "dijit/form/_FormMixin",
-    "dijit/registry",
-    "dojo/_base/array",
-    "dojo/_base/declare",
-    "dojo/_base/event",
-    "dojo/on"
-], (_Container, _FormMixin, registry, array, declare, event, _on) ->
-    declare [_Container,_FormMixin],
-        name: ""
-        value: null
-        disabled: false
-        readOnly: false
-
-        postCreate: () ->
-            @inherited arguments
-            if @domNode.tagName.toLowerCase() isnt "form"
-                console.warn "Not scoping a _ComplexValueMixin in a
-                        form element can cause name clashes. E.g.
-                        radio buttons might stop working correctly.
-                        It is recommended to use <form> as the root
-                        element in your template for", @declaredClass
-            @own ( _on @domNode, 'submit', (evt) => @_handleSubmit evt )
-
-        _setDisabledAttr: (value) ->
-            @_set "disabled", value
-            array.forEach @_getDescendantFormWidgets(), (child) =>
-                child.set "disabled", value
-
-        _setReadOnlyAttr: (value) ->
-            @_set "readOnly", value
-            array.forEach @_getDescendantFormWidgets(), (child) =>
-                child.set "readOnly", value
-
-        focus: () ->
-            children = @_getDescendantFormWidgets()
-            children[0].focus() if children.length > 0
-
-        _handleSubmit: (evt) ->
-            node = @domNode
-            until not node or ( widget and
-                                typeof widget._onSubmit is "function" )
-                node = node.parentNode
-                widget = registry.byNode node if node
-            widget._onSubmit(evt) if widget
-            # we always stop the event, since this is a widget after all
-            event.stop evt if evt
-            false
Index: /Dev/trunk/src/client/qed-client/widgets/_ComplexValueMixin.js
===================================================================
--- /Dev/trunk/src/client/qed-client/widgets/_ComplexValueMixin.js	(revision 493)
+++ /Dev/trunk/src/client/qed-client/widgets/_ComplexValueMixin.js	(revision 493)
@@ -0,0 +1,107 @@
+define([
+    "dijit/_Container",
+    "dijit/form/_FormMixin",
+    "dijit/registry",
+    "dojo/_base/array",
+    "dojo/_base/declare",
+    "dojo/_base/event",
+    "dojo/_base/lang",
+    "dojo/on"
+], function(_Container, _FormMixin, registry, array, declare, event, lang, on) {
+    return declare([_Container, _FormMixin], {
+        name: "",
+        disabled: false,
+        readOnly: false,
+        _changeActive: false,
+        postCreate: function() {
+            this.inherited(arguments);
+            if ( this.domNode.tagName.toLowerCase() !== "form" ) {
+                console.warn("Not scoping a _ComplexValueMixin in a form element can cause name clashes. E.g. radio buttons might stop working correctly. It is recommended to use <form> as the root element in your template for", this.declaredClass);
+            }
+            this.own(on(this.domNode, 'submit', lang.hitch(this,'_handleSubmit')));
+        },
+        startup: function() {
+            if (this._started) { return; }
+            this.inherited(arguments);
+            this._connectChildren();
+            this._changeActive = true;
+        },
+        _connectChildren: function() {
+            var children = this._getDescendantFormWidgets();
+            array.forEach(children, lang.hitch(this,function(child) {
+                this.own(child.on('change',lang.hitch(this,'_handleChange')));
+            }));
+        },
+        _setDisabledAttr: function(value) {
+            this._set("disabled", value);
+            array.forEach(this._getDescendantFormWidgets(), function(child) {
+                child.set("disabled", value);
+            });
+        },
+        // Yuk, this function is taken directly from _FromMixin only
+        // to add the priorityChange parameter
+		_setValueAttr: function(obj, priorityChange) {
+			var map = { };
+			array.forEach(this._getDescendantFormWidgets(), function(widget){
+				if(!widget.name){ return; }
+				var entry = map[widget.name] || (map[widget.name] = [] );
+				entry.push(widget);
+			});
+
+			for(var name in map){
+				if (map.hasOwnProperty(name)) {
+                    var widgets = map[name],
+                        values = lang.getObject(name, false, obj);
+
+                    if (values === undefined) {
+                        continue;
+                    }
+                    values = [].concat(values);
+                    if (typeof widgets[0].checked === 'boolean') {
+                        array.forEach(widgets, function(w){
+                            w.set('value', array.indexOf(values, w._get('value')) !== -1, priorityChange);
+                        });
+                    } else if (widgets[0].multiple) {
+                        widgets[0].set('value', values, priorityChange);
+                    } else {
+                        array.forEach(widgets, function(w, i){
+                            w.set('value', values[i], priorityChange);
+                        });
+                    }
+				}
+			}
+		},
+        _setReadOnlyAttr: function(value) {
+            this._set("readOnly", value);
+            array.forEach(this._getDescendantFormWidgets(), function(child) {
+                child.set("readOnly", value);
+            });
+        },
+        focus: function() {
+            var children = this._getDescendantFormWidgets();
+            if ( children.length > 0 ) {
+                children[0].focus();
+            }
+        },
+        _handleChange: function() {
+            if ( this._changeActive ) {
+                this.onChange(this.get('value'));
+            }
+        },
+        onChange: function(){},
+        _handleSubmit: function(evt) {
+            var node = this.domNode;
+            var widget;
+            while ( node ) {
+                node = node.parentNode;
+                widget = registry.byNode(node);
+                if ( widget && typeof widget._onSubmit === "function" ) {
+                    widget._onSubmit(evt);
+                    break;
+                }
+            }
+            if (evt) { event.stop(evt); }
+            return false;
+        }
+    });
+});
Index: /Dev/trunk/src/server/config/couchdb-schema.json
===================================================================
--- /Dev/trunk/src/server/config/couchdb-schema.json	(revision 492)
+++ /Dev/trunk/src/server/config/couchdb-schema.json	(revision 493)
@@ -99,9 +99,23 @@
         "type": "object",
         "oneOf": [
+          { "$ref": "#/definitions/content/Header" },
           { "$ref": "#/definitions/content/Text" },
+          { "$ref": "#/definitions/content/Divider" },
           { "$ref": "#/definitions/content/StringInput" },
-          { "$ref": "#/definitions/content/ScaleInput" }
+          { "$ref": "#/definitions/content/TextInput" },
+          { "$ref": "#/definitions/content/NumberInput" },
+          { "$ref": "#/definitions/content/ScaleInput" },
+          { "$ref": "#/definitions/content/MultipleChoiceInput" }
         ]
       },
+      "Header": {
+        "type": "object",
+        "properties": {
+          "type": { "type": "string", "pattern": "Header" },
+          "text": { "type": "string" }
+        },
+        "required": ["type","text"],
+        "additionalProperties": false
+      },
       "Text": {
         "type": "object",
@@ -113,8 +127,37 @@
         "additionalProperties": false
       },
+      "Divider": {
+        "type": "object",
+        "properties": {
+          "type": { "type": "string", "pattern": "Divider" }
+        },
+        "additionalProperties": false
+      },
       "StringInput": {
         "type": "object",
         "properties": {
           "type": { "type": "string", "pattern": "StringInput" },
+          "text": { "type": "string" }
+        },
+        "required":["type","text"],
+        "additionalProperties": false
+      },
+      "TextInput": {
+        "type": "object",
+        "properties": {
+          "type": { "type": "string", "pattern": "TextInput" },
+          "maxLength": { "type": "integer" },
+          "text": { "type": "string" }
+        },
+        "required":["type","text"],
+        "additionalProperties": false
+      },
+      "NumberInput": {
+        "type": "object",
+        "properties": {
+          "type": { "type": "string", "pattern": "NumberInput" },
+          "min": { "type": "integer" },
+          "max": { "type": "integer" },
+          "places": { "type": "integer" },
           "text": { "type": "string" }
         },
@@ -144,4 +187,21 @@
         "required":["type","min","max","items"],
         "additionalProperties": false
+      },
+      "MultipleChoiceInput": {
+        "type": "object",
+        "properties": {
+          "type": { "type": "string", "pattern": "MultipleChoiceInput" },
+          "allowMultiple": { "type": "boolean" },
+          "items": { "type": "array", "items": {
+              "type": "object",
+              "properties": {
+                  "text": { "type": "string" }
+              },
+              "required": ["text"],
+              "additionalProperties": false
+          } }
+        },
+        "required":["type","items"],
+        "additionalProperties": false
       }
     }
Index: /Dev/trunk/src/server/util/validator.js
===================================================================
--- /Dev/trunk/src/server/util/validator.js	(revision 492)
+++ /Dev/trunk/src/server/util/validator.js	(revision 493)
@@ -22,3 +22,9 @@
 });
 
-module.exports = tv4.validateResult.bind(tv4);
+module.exports = function() {
+    var result = tv4.validateResult.apply(tv4,arguments);
+    if ( result.missing.length > 0 ) {
+        result.valid = false;
+    }
+    return result;
+};
