summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/pad/static/src/js/pad.js227
-rw-r--r--addons/pad/static/tests/pad_tests.js151
-rw-r--r--addons/pad/views/pad.xml6
3 files changed, 296 insertions, 88 deletions
diff --git a/addons/pad/static/src/js/pad.js b/addons/pad/static/src/js/pad.js
index 8fb09df0173..76f6ad9b9ae 100644
--- a/addons/pad/static/src/js/pad.js
+++ b/addons/pad/static/src/js/pad.js
@@ -3,116 +3,167 @@ odoo.define('pad.pad', function (require) {
var AbstractField = require('web.AbstractField');
var core = require('web.core');
-var field_registry = require('web.field_registry');
+var fieldRegistry = require('web.field_registry');
var _t = core._t;
-
var FieldPad = AbstractField.extend({
template: 'FieldPad',
content: "",
events: {
- 'click .oe_pad_switch': function() {
- this.$el.toggleClass('oe_pad_fullscreen mb0');
- this.$('.oe_pad_switch').toggleClass('fa-expand fa-compress');
- },
+ 'click .oe_pad_switch': '_onToggleFullScreen',
},
supportedFieldTypes: ['char'],
- init: function() {
- var self = this;
- this._super.apply(this, arguments);
- this.configured = false;
- this._configured_deferred = $.Deferred();
- this._url_request_deferred = $.Deferred();
+ /**
+ * @override
+ */
+ willStart: function () {
+ if (this.isPadConfigured === undefined) {
+ return this._rpc({
+ method: 'pad_is_configured',
+ model: this.model,
+ }).then(function (result) {
+ // we write on the prototype to share the information between
+ // all pad widgets instances, across all actions
+ FieldPad.prototype.isPadConfigured = result;
+ });
+ }
+ return $.when();
+ },
+ /**
+ * @override
+ */
+ start: function () {
+ if (!this.isPadConfigured) {
+ this.$(".oe_unconfigured").removeClass('hidden');
+ this.$(".oe_configured").addClass('hidden');
+ return;
+ }
+ return this._super.apply(this, arguments);
+ },
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
- this.trigger_up('perform_model_rpc', {
- method: 'pad_is_configured',
- model: this.model,
- on_success: function (data) {
- self.set_configured(!!data);
- self._configured_deferred.resolve();
- },
- on_fail: function () {
- self.set_configured(true);
- self._configured_deferred.resolve();
- },
- });
+ /**
+ * If we had to generate an url, we wait for the generation to be completed,
+ * so the current record will be associated with the correct pad url.
+ *
+ * @override
+ */
+ commitChanges: function () {
+ return this.urlDef;
},
- render_readonly: function() {
- var self = this;
- this._configured_deferred.then(function() {
- if (self.configured){
- if (_.str.startsWith(self.value, 'http')) {
- self.trigger_up('perform_model_rpc', {
- method: 'pad_get_content',
- model: self.model,
- args: [self.value],
- on_success: function(data) {
- self.$('.oe_pad_content').removeClass('oe_pad_loading').html('<div class="oe_pad_readonly"><div>');
- self.$('.oe_pad_readonly').html(data);
- },
- on_fail: function() {
- self.$('.oe_pad_content').text(_t('Unable to load pad'));
- }
- });
- } else {
- self.$('.oe_pad_content').addClass('oe_pad_loading').show().text(_t("This pad will be initialized on first edit"));
- }
- }
- });
+ /**
+ * @override
+ */
+ isSet: function () {
+ return true;
},
- render_edit: function() {
- var self = this;
- this._configured_deferred.then(function() {
- if (! self.value || !_.str.startsWith(self.value, 'http')) {
- self.trigger_up('perform_model_rpc', {
- method: 'pad_generate_url',
- model: self.model,
- context: {
- model: self.model,
- field_name: self.name,
- object_id: self.model
- },
- on_success: function(data) {
- if (! data.url) {
- self.set_configured(false);
- } else {
- self.set_value(data.url);
- }
- self._url_request_deferred.resolve();
- },
- });
- } else {
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Note that this method has some serious side effects: performing rpcs and
+ * setting the value of this field. This is not conventional and should not
+ * be copied in other code, unless really necessary.
+ *
+ * @override
+ * @private
+ */
+ _renderEdit: function () {
+ if (_.str.startsWith(this.value, 'http')) {
+ // here, we have a valid url, so we can simply display an iframe
+ // with the correct src attribute
+ var userName = encodeURIComponent(this.getSession().userName);
+ var url = this.value + '?showChat=false&userName=' + userName;
+ var content = '<iframe width="100%" height="100%" frameborder="0" src="' + url + '"></iframe>';
+ this.$('.oe_pad_content').html(content);
+ } else if (this.value) {
+ // it looks like the field does not contain a valid url, so we just
+ // display it (it cannot be edited in that case)
+ this.$('.oe_pad_content').text(this.value);
+ } else {
+ // It is totally discouraged to have a render method that does
+ // non-rendering work, especially since the work in question
+ // involves doing RPCs and changing the value of the field.
+ // However, this is kind of necessary in this case, because the
+ // value of the field is actually only the url of the pad. The
+ // actual content will be loaded in an iframe. We could do this
+ // work in the basic model, but the basic model does not know that
+ // this widget is in edit or readonly, and we really do not want to
+ // create a pad url everytime a task without a pad is viewed.
+ var self = this;
+ this.urlDef = this._rpc({
+ method: 'pad_generate_url',
+ model: this.model,
+ context: {
+ model: this.model,
+ field_name: this.name,
+ object_id: this.res_id
+ },
+ }, {
+ shadow: true
+ }).then(function (result) {
// We need to write the url of the pad to trigger
// the write function which updates the actual value
// of the field to the value of the pad content
- self.set_value(self.value);
- self._url_request_deferred.resolve();
- }
- self._url_request_deferred.then(function() {
- if (_.str.startsWith(self.value, 'http')) {
- var content = '<iframe width="100%" height="100%" frameborder="0" src="' + self.value + '?showChat=false&userName=' + encodeURIComponent(self.session.username) + '"></iframe>';
- self.$('.oe_pad_content').html(content);
- }
- else {
- self.$('.oe_pad_content').text(self.value);
- }
+ self._setValue(result.url);
});
- });
+ }
},
- set_configured: function(configured) {
- this.configured = configured;
- if (!configured) {
- this.$(".oe_unconfigured").removeClass('hidden');
- this.$(".oe_configured").addClass('hidden');
+ /**
+ * @override
+ * @private
+ */
+ _renderReadonly: function () {
+ if (_.str.startsWith(this.value, 'http')) {
+ var self = this;
+ this.$('.oe_pad_content')
+ .addClass('oe_pad_loading')
+ .text(_t("Loading"));
+ this._rpc({
+ method: 'pad_get_content',
+ model: this.model,
+ args: [this.value]
+ }, {
+ shadow: true
+ }).then(function (data) {
+ self.$('.oe_pad_content')
+ .removeClass('oe_pad_loading')
+ .html('<div class="oe_pad_readonly"><div>');
+ self.$('.oe_pad_readonly').html(data);
+ }).fail(function () {
+ self.$('.oe_pad_content').text(_t('Unable to load pad'));
+ });
+ } else {
+ this.$('.oe_pad_content')
+ .addClass('oe_pad_loading')
+ .show()
+ .text(_t("This pad will be initialized on first edit"));
}
},
- is_set: function() {
- return true;
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ * @private
+ */
+ _onToggleFullScreen: function () {
+ this.$el.toggleClass('oe_pad_fullscreen mb0');
+ this.$('.oe_pad_switch').toggleClass('fa-expand fa-compress');
},
});
-field_registry.add('pad', FieldPad);
+fieldRegistry.add('pad', FieldPad);
+
+return FieldPad;
});
diff --git a/addons/pad/static/tests/pad_tests.js b/addons/pad/static/tests/pad_tests.js
new file mode 100644
index 00000000000..69342f5de6e
--- /dev/null
+++ b/addons/pad/static/tests/pad_tests.js
@@ -0,0 +1,151 @@
+odoo.define('pad.pad_tests', function (require) {
+"use strict";
+
+var FieldPad = require('pad.pad');
+var FormView = require('web.FormView');
+var testUtils = require('web.test_utils');
+
+var createView = testUtils.createView;
+
+QUnit.module('pad widget', {
+ beforeEach: function () {
+ this.data = {
+ task: {
+ fields: {
+ description: {string: "Description", type: "char"},
+ },
+ records: [
+ {id: 1, description: false},
+ {id: 2, description: "https://pad.odoo.pad/p/test-03AK6RCJT"},
+ ],
+ pad_is_configured: function () {
+ return true;
+ },
+ pad_generate_url: function (route, args) {
+ return {
+ url:'https://pad.odoo.pad/p/test/' + args.context.object_id
+ };
+ },
+ pad_get_content: function () {
+ return "we should rewrite this server in haskell";
+ },
+ },
+ };
+ },
+});
+
+ QUnit.test('pad widget display help if server not configured', function (assert) {
+ assert.expect(4);
+
+ var form = createView({
+ View: FormView,
+ model: 'task',
+ data: this.data,
+ arch:'<form>' +
+ '<sheet>' +
+ '<group>' +
+ '<field name="description" widget="pad"/>' +
+ '</group>' +
+ '</sheet>' +
+ '</form>',
+ res_id: 1,
+ mockRPC: function (route, args) {
+ if (args.method === 'pad_is_configured') {
+ return $.when(false);
+ }
+ return this._super.apply(this, arguments);
+ },
+ });
+ assert.ok(form.$('p.oe_unconfigured').is(':visible'),
+ "help message should be visible");
+ assert.notOk(form.$('p.oe_pad_content').is(':visible'),
+ "content should not be visible");
+ form.$buttons.find('.o_form_button_edit').click();
+ assert.ok(form.$('p.oe_unconfigured').is(':visible'),
+ "help message should be visible");
+ assert.notOk(form.$('p.oe_pad_content').is(':visible'),
+ "content should not be visible");
+ form.destroy();
+ delete FieldPad.prototype.isPadConfigured;
+ });
+
+ QUnit.test('pad widget works, basic case', function (assert) {
+ assert.expect(5);
+
+ var form = createView({
+ View: FormView,
+ model: 'task',
+ data: this.data,
+ arch:'<form>' +
+ '<sheet>' +
+ '<group>' +
+ '<field name="description" widget="pad"/>' +
+ '</group>' +
+ '</sheet>' +
+ '</form>',
+ res_id: 1,
+ mockRPC: function (route, args) {
+ if (route === 'https://pad.odoo.pad/p/test/1?showChat=false&userName=batman') {
+ assert.ok(true, "should have an iframe with correct src");
+ return $.when(true);
+ }
+ return this._super.apply(this, arguments);
+ },
+ session: {
+ userName: "batman",
+ },
+ });
+ assert.notOk(form.$('p.oe_unconfigured').is(':visible'),
+ "help message should not be visible");
+ assert.ok(form.$('.oe_pad_content').is(':visible'),
+ "content should be visible");
+ assert.strictEqual(form.$('.oe_pad_content:contains(This pad will be)').length, 1,
+ "content should display a message when not initialized");
+
+ form.$buttons.find('.o_form_button_edit').click();
+
+ assert.strictEqual(form.$('.oe_pad_content iframe').length, 1,
+ "should have an iframe");
+
+ form.destroy();
+ delete FieldPad.prototype.isPadConfigured;
+ });
+
+ QUnit.test('pad widget works, with existing data', function (assert) {
+ assert.expect(2);
+
+ var contentDef = $.Deferred();
+
+ var form = createView({
+ View: FormView,
+ model: 'task',
+ data: this.data,
+ arch:'<form>' +
+ '<sheet>' +
+ '<group>' +
+ '<field name="description" widget="pad"/>' +
+ '</group>' +
+ '</sheet>' +
+ '</form>',
+ res_id: 2,
+ mockRPC: function (route, args) {
+ var result = this._super.apply(this, arguments);
+ if (args.method === 'pad_get_content') {
+ return contentDef.then(_.constant(result));
+ }
+ return result;
+ },
+ session: {
+ userName: "batman",
+ },
+ });
+ assert.strictEqual(form.$('.oe_pad_content').text(), "Loading",
+ "should display loading message");
+ contentDef.resolve();
+ assert.strictEqual(form.$('.oe_pad_content').text(), "we should rewrite this server in haskell",
+ "should display proper value");
+ form.destroy();
+ delete FieldPad.prototype.isPadConfigured;
+ });
+
+});
diff --git a/addons/pad/views/pad.xml b/addons/pad/views/pad.xml
index 56bc0b1a997..126f8c84f16 100644
--- a/addons/pad/views/pad.xml
+++ b/addons/pad/views/pad.xml
@@ -6,4 +6,10 @@
<script type="text/javascript" src="/pad/static/src/js/pad.js" />
</xpath>
</template>
+
+ <template id="qunit_suite" name="pad tests" inherit_id="web.qunit_suite">
+ <xpath expr="//script[last()]" position="after">
+ <script type="text/javascript" src="/pad/static/tests/pad_tests.js"></script>
+ </xpath>
+ </template>
</odoo>