diff --git a/.meteor/versions b/.meteor/versions
index 2a41d6f75..6ee9f0fe1 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -137,7 +137,6 @@ tap:i18n@1.7.0
templating@1.1.7
templating-tools@1.0.2
tmeasday:check-npm-versions@0.1.1
-tmeasday:healthcheck-handler@0.0.2
tmeasday:publish-counts@0.7.3
tracker@1.0.11
twitter@1.1.7
diff --git a/packages/nova-forms/lib/EditDocument.jsx b/packages/nova-forms/lib/EditDocument.jsx
index f60bac4a1..69bfebf98 100644
--- a/packages/nova-forms/lib/EditDocument.jsx
+++ b/packages/nova-forms/lib/EditDocument.jsx
@@ -17,17 +17,25 @@ const EditDocument = React.createClass({
labelFunction: React.PropTypes.func
},
+ getFields() {
+ const collection = this.props.collection;
+ const fields = collection.getInsertableFields(this.props.currentUser);
+ return fields;
+ },
+
submitForm(data) {
- console.log(data)
-
+ const fields = this.getFields();
const document = this.props.document;
- const modifier = {$set: _.compactObject(Utils.flatten(data))};
+ // put all keys with data on $set
+ const set = _.compactObject(Utils.flatten(data));
+ // put all keys without data on $unset
+ const unsetKeys = _.difference(fields, _.keys(set));
+ const unset = _.object(unsetKeys, unsetKeys.map(()=>true));
+ const modifier = {$set: set, $unset: unset};
const collection = this.props.collection;
const methodName = this.props.methodName ? this.props.methodName : collection._name+'.edit';
- console.log(modifier)
-
Meteor.call(methodName, document._id, modifier, (error, document) => {
if (error) {
console.log(error)
@@ -49,7 +57,7 @@ const EditDocument = React.createClass({
const document = this.props.document;
const collection = this.props.collection;
- const fields = collection.getInsertableFields(this.props.currentUser);
+ const fields = this.getFields();
const style = {
maxWidth: "800px",
@@ -59,7 +67,11 @@ const EditDocument = React.createClass({
return (
- {fields.map(fieldName => {SmartForms.getComponent(fieldName, collection.simpleSchema()._schema[fieldName], this.props.labelFunction, document)}
)}
+ {fields.map(fieldName =>
+
+ {SmartForms.getComponent(fieldName, collection.simpleSchema()._schema[fieldName], this.props.labelFunction, document)}
+
+ )}
diff --git a/packages/nova-forms/lib/component.js b/packages/nova-forms/lib/component.js
new file mode 100644
index 000000000..69d1f1573
--- /dev/null
+++ b/packages/nova-forms/lib/component.js
@@ -0,0 +1,159 @@
+'use strict';
+
+var React = require('react');
+
+module.exports = {
+
+ propTypes: {
+ layout: React.PropTypes.string,
+ validatePristine: React.PropTypes.bool,
+ rowClassName: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.array,
+ React.PropTypes.object
+ ]),
+ labelClassName: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.array,
+ React.PropTypes.object
+ ]),
+ elementWrapperClassName: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.array,
+ React.PropTypes.object
+ ])
+ },
+
+ contextTypes: {
+ layout: React.PropTypes.string,
+ validatePristine: React.PropTypes.bool,
+ rowClassName: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.array,
+ React.PropTypes.object
+ ]),
+ labelClassName: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.array,
+ React.PropTypes.object
+ ]),
+ elementWrapperClassName: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.array,
+ React.PropTypes.object
+ ])
+ },
+
+ getDefaultProps: function() {
+ return {
+ disabled: false,
+ validatePristine: false,
+ onChange: function() {},
+ onFocus: function() {},
+ onBlur: function() {}
+ };
+ },
+
+ /**
+ * Accessors for "special" properties.
+ *
+ * The following methods are used to merge master default properties that
+ * are optionally set on the parent form. This to to allow customising these
+ * properties 'as a whole' for the form, while retaining the ability to
+ * override the properties on a component basis.
+ *
+ * Also see the parent-context mixin.
+ */
+ getLayout: function() {
+ var defaultProperty = this.context.layout || 'horizontal';
+ return this.props.layout ? this.props.layout : defaultProperty;
+ },
+
+ getValidatePristine: function() {
+ var defaultProperty = this.context.validatePristine || false;
+ return this.props.validatePristine ? this.props.validatePristine : defaultProperty;
+ },
+
+ getRowClassName: function() {
+ return [this.context.rowClassName, this.props.rowClassName];
+ },
+
+ getLabelClassName: function() {
+ return [this.context.labelClassName, this.props.labelClassName];
+ },
+
+ getElementWrapperClassName: function() {
+ return [this.context.elementWrapperClassName, this.props.elementWrapperClassName];
+ },
+
+ getRowProperties: function() {
+ return {
+ label: this.props.label,
+ rowClassName: this.getRowClassName(),
+ labelClassName: this.getLabelClassName(),
+ elementWrapperClassName: this.getElementWrapperClassName(),
+ layout: this.getLayout(),
+ required: this.isRequired(),
+ hasErrors: this.showErrors()
+ };
+ },
+
+ hashString: function(string) {
+ var hash = 0;
+ for (var i = 0; i < string.length; i++) {
+ hash = (((hash << 5) - hash) + string.charCodeAt(i)) & 0xFFFFFFFF;
+ }
+ return hash;
+ },
+
+ /**
+ * getId
+ *
+ * The ID is used as an attribute on the form control, and is used to allow
+ * associating the label element with the form control.
+ *
+ * If we don't explicitly pass an `id` prop, we generate one based on the
+ * `name` and `label` properties.
+ */
+ getId: function() {
+ if (this.props.id) {
+ return this.props.id;
+ }
+ var label = (typeof this.props.label === 'undefined' ? '' : this.props.label);
+ return [
+ 'frc',
+ this.props.name.split('[').join('_').replace(']', ''),
+ this.hashString(JSON.stringify(label))
+ ].join('-');
+ },
+
+ renderHelp: function() {
+ if (!this.props.help) {
+ return '';
+ }
+ return (
+ {this.props.help}
+ );
+ },
+
+ renderErrorMessage: function() {
+ if (!this.showErrors()) {
+ return '';
+ }
+ var errorMessages = this.getErrorMessages() || [];
+ return errorMessages.map((message, key) => {
+ return (
+ {message}
+ );
+ });
+ },
+
+ showErrors: function() {
+ if (this.isPristine() === true) {
+ if (this.getValidatePristine() === false) {
+ return false;
+ }
+ }
+ return (this.isValid() === false);
+ }
+};
\ No newline at end of file
diff --git a/packages/nova-forms/lib/row.js b/packages/nova-forms/lib/row.js
new file mode 100644
index 000000000..f685e45f3
--- /dev/null
+++ b/packages/nova-forms/lib/row.js
@@ -0,0 +1,126 @@
+/*jshint node:true */
+
+'use strict';
+
+var React = require('react');
+var classNames = require('classnames/dedupe');
+
+var Row = React.createClass({
+
+ propTypes: {
+ label: React.PropTypes.node,
+ rowClassName: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.array,
+ React.PropTypes.object
+ ]),
+ labelClassName: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.array,
+ React.PropTypes.object
+ ]),
+ elementWrapperClassName: React.PropTypes.oneOfType([
+ React.PropTypes.string,
+ React.PropTypes.array,
+ React.PropTypes.object
+ ]),
+ required: React.PropTypes.bool,
+ hasErrors: React.PropTypes.bool,
+ fakeLabel: React.PropTypes.bool,
+ layout: React.PropTypes.oneOf(['horizontal', 'vertical', 'elementOnly']),
+ htmlFor: React.PropTypes.string
+ },
+
+ getDefaultProps: function() {
+ return {
+ label: '',
+ rowClassName: '',
+ labelClassName: '',
+ elementWrapperClassName: '',
+ required: false,
+ hasErrors: false,
+ fakeLabel: false
+ };
+ },
+
+ renderLabel: function() {
+
+ if (this.props.layout === 'elementOnly') {
+ return '';
+ }
+
+ var labelClassNames = [];
+ labelClassNames.push('control-label');
+
+ if (this.props.layout === 'horizontal') {
+ labelClassNames.push('col-sm-3');
+ }
+
+ labelClassNames.push(this.props.labelClassName);
+
+ if (this.props.fakeLabel) {
+ return (
+
+
+ {this.props.label}
+ {this.props.required ? ' *' : null}
+
+
+ );
+ }
+ return (
+
+ );
+ },
+
+ render: function() {
+
+ if (this.props.layout === 'elementOnly') {
+ return (
+
+ {this.props.children}
+
+ );
+ }
+
+ var cssClasses = {
+ row: ['form-group'],
+ elementWrapper: []
+ };
+
+ if (this.props.hasErrors) {
+ cssClasses.row.push('has-error');
+ cssClasses.row.push('has-feedback');
+ }
+
+ var element = this.props.children;
+ if (this.props.layout === 'horizontal') {
+
+ // Horizontal layout needs a 'row' class for Bootstrap 4
+ cssClasses.row.push('row');
+
+ cssClasses.elementWrapper.push('col-sm-9');
+ cssClasses.elementWrapper.push(this.props.elementWrapperClassName);
+
+ element = (
+
+ {this.props.children}
+
+ );
+ }
+
+ cssClasses.row.push(this.props.rowClassName);
+ return (
+
+ {this.renderLabel()}
+ {element}
+
+ );
+ }
+
+});
+
+module.exports = Row;
\ No newline at end of file
diff --git a/packages/nova-forms/lib/smart-forms.jsx b/packages/nova-forms/lib/smart-forms.jsx
index f3aaf8ffa..f140771b6 100644
--- a/packages/nova-forms/lib/smart-forms.jsx
+++ b/packages/nova-forms/lib/smart-forms.jsx
@@ -5,7 +5,7 @@ import FRC from 'formsy-react-components';
import Utils from './utils.js';
const Checkbox = FRC.Checkbox;
-const CheckboxGroup = FRC.CheckboxGroup;
+// const CheckboxGroup = FRC.CheckboxGroup;
const Input = FRC.Input;
const RadioGroup = FRC.RadioGroup;
const Select = FRC.Select;
@@ -38,7 +38,7 @@ SmartForms.getComponent = (fieldName, field, labelFunction, document) => {
return ;
// note: checkboxgroup cause React refs error, so use RadioGroup for now
case "checkboxgroup":
- return ;
+ return ;
case "radiogroup":
return ;
case "select":
@@ -48,4 +48,80 @@ SmartForms.getComponent = (fieldName, field, labelFunction, document) => {
}
}
-export default SmartForms;
\ No newline at end of file
+export default SmartForms;
+
+
+import ComponentMixin from './component';
+import Row from './row';
+
+const CheckboxGroup = React.createClass({
+
+ mixins: [Formsy.Mixin, ComponentMixin],
+
+ propTypes: {
+ name: React.PropTypes.string.isRequired,
+ options: React.PropTypes.array.isRequired
+ },
+
+ getDefaultProps: function() {
+ return {
+ label: '',
+ help: null
+ };
+ },
+
+ changeCheckbox: function() {
+ var value = [];
+ this.props.options.forEach(function(option, key) {
+ if (this.refs['element-' + key].checked) {
+ value.push(option.value);
+ }
+
+ }.bind(this));
+ this.setValue(value);
+ this.props.onChange(this.props.name, value);
+ },
+
+ renderElement: function() {
+ var _this = this;
+ var controls = this.props.options.map(function(checkbox, key) {
+ var checked = (_this.getValue().indexOf(checkbox.value) !== -1);
+ var disabled = _this.isFormDisabled() || checkbox.disabled || _this.props.disabled;
+ return (
+
+
+
+ );
+ });
+ return controls;
+ },
+
+ render: function() {
+
+ if (this.getLayout() === 'elementOnly') {
+ return (
+ {this.renderElement()}
+ );
+ }
+
+ return (
+
+ {this.renderElement()}
+ {this.renderHelp()}
+ {this.renderErrorMessage()}
+
+ );
+ }
+});
diff --git a/sample_settings.json b/sample_settings.json
index 2958064e5..7e02f3cc2 100644
--- a/sample_settings.json
+++ b/sample_settings.json
@@ -17,7 +17,7 @@
"postInterval": 20,
"RSSLinksPointTo": "link",
"commentInterval": 20,
- "maxPostsPerDay": 10
+ "maxPostsPerDay": 10,
"startInvitesCount": 5,
"postsPerPage": 10,
@@ -27,7 +27,7 @@
"facebookPage": "http://facebook.com/foo",
"googleAnalyticsId":"123foo",
- "embedlyKey":"123foo",
+ "embedlyKey":"123foo"
},