summaryrefslogtreecommitdiff
path: root/addons/portal/static/src/js/portal_chatter.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/portal/static/src/js/portal_chatter.js')
-rw-r--r--addons/portal/static/src/js/portal_chatter.js311
1 files changed, 311 insertions, 0 deletions
diff --git a/addons/portal/static/src/js/portal_chatter.js b/addons/portal/static/src/js/portal_chatter.js
new file mode 100644
index 00000000..134efbf6
--- /dev/null
+++ b/addons/portal/static/src/js/portal_chatter.js
@@ -0,0 +1,311 @@
+odoo.define('portal.chatter', function (require) {
+'use strict';
+
+var core = require('web.core');
+const dom = require('web.dom');
+var publicWidget = require('web.public.widget');
+var time = require('web.time');
+var portalComposer = require('portal.composer');
+
+var qweb = core.qweb;
+var _t = core._t;
+
+/**
+ * Widget PortalChatter
+ *
+ * - Fetch message fron controller
+ * - Display chatter: pager, total message, composer (according to access right)
+ * - Provider API to filter displayed messages
+ */
+var PortalChatter = publicWidget.Widget.extend({
+ template: 'portal.Chatter',
+ xmlDependencies: ['/portal/static/src/xml/portal_chatter.xml'],
+ events: {
+ 'click .o_portal_chatter_pager_btn': '_onClickPager',
+ 'click .o_portal_chatter_js_is_internal': 'async _onClickUpdateIsInternal',
+ },
+
+ /**
+ * @constructor
+ */
+ init: function (parent, options) {
+ var self = this;
+ this.options = {};
+ this._super.apply(this, arguments);
+
+ // underscorize the camelcased option keys
+ _.each(options, function (val, key) {
+ self.options[_.str.underscored(key)] = val;
+ });
+ // set default options
+ this.options = _.defaults(this.options, {
+ 'allow_composer': true,
+ 'display_composer': false,
+ 'csrf_token': odoo.csrf_token,
+ 'message_count': 0,
+ 'pager_step': 10,
+ 'pager_scope': 5,
+ 'pager_start': 1,
+ 'is_user_public': true,
+ 'is_user_employee': false,
+ 'is_user_publisher': false,
+ 'hash': false,
+ 'pid': false,
+ 'domain': [],
+ });
+
+ this.set('messages', []);
+ this.set('message_count', this.options['message_count']);
+ this.set('pager', {});
+ this.set('domain', this.options['domain']);
+ this._currentPage = this.options['pager_start'];
+ },
+ /**
+ * @override
+ */
+ willStart: function () {
+ return Promise.all([
+ this._super.apply(this, arguments),
+ this._chatterInit()
+ ]);
+ },
+ /**
+ * @override
+ */
+ start: function () {
+ // bind events
+ this.on("change:messages", this, this._renderMessages);
+ this.on("change:message_count", this, function () {
+ this._renderMessageCount();
+ this.set('pager', this._pager(this._currentPage));
+ });
+ this.on("change:pager", this, this._renderPager);
+ this.on("change:domain", this, this._onChangeDomain);
+ // set options and parameters
+ this.set('message_count', this.options['message_count']);
+ this.set('messages', this.preprocessMessages(this.result['messages']));
+
+
+ var defs = [];
+ defs.push(this._super.apply(this, arguments));
+
+ // instanciate and insert composer widget
+ if (this.options['display_composer']) {
+ this._composer = new portalComposer.PortalComposer(this, this.options);
+ defs.push(this._composer.replace(this.$('.o_portal_chatter_composer')));
+ }
+
+ return Promise.all(defs);
+ },
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * Fetch the messages and the message count from the server for the
+ * current page and current domain.
+ *
+ * @param {Array} domain
+ * @returns {Promise}
+ */
+ messageFetch: function (domain) {
+ var self = this;
+ return this._rpc({
+ route: '/mail/chatter_fetch',
+ params: self._messageFetchPrepareParams(),
+ }).then(function (result) {
+ self.set('messages', self.preprocessMessages(result['messages']));
+ self.set('message_count', result['message_count']);
+ });
+ },
+ /**
+ * Update the messages format
+ *
+ * @param {Array<Object>}
+ * @returns {Array}
+ */
+ preprocessMessages: function (messages) {
+ _.each(messages, function (m) {
+ m['author_avatar_url'] = _.str.sprintf('/web/image/%s/%s/author_avatar/50x50', 'mail.message', m.id);
+ m['published_date_str'] = _.str.sprintf(_t('Published on %s'), moment(time.str_to_datetime(m.date)).format('MMMM Do YYYY, h:mm:ss a'));
+ });
+ return messages;
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * @returns {Deferred}
+ */
+ _chatterInit: function () {
+ var self = this;
+ return this._rpc({
+ route: '/mail/chatter_init',
+ params: this._messageFetchPrepareParams()
+ }).then(function (result) {
+ self.result = result;
+ self.options = _.extend(self.options, self.result['options'] || {});
+ return result;
+ });
+ },
+ /**
+ * Change the current page by refreshing current domain
+ *
+ * @private
+ * @param {Number} page
+ * @param {Array} domain
+ */
+ _changeCurrentPage: function (page, domain) {
+ this._currentPage = page;
+ var d = domain ? domain : _.clone(this.get('domain'));
+ this.set('domain', d); // trigger fetch message
+ },
+ _messageFetchPrepareParams: function () {
+ var self = this;
+ var data = {
+ 'res_model': this.options['res_model'],
+ 'res_id': this.options['res_id'],
+ 'limit': this.options['pager_step'],
+ 'offset': (this._currentPage - 1) * this.options['pager_step'],
+ 'allow_composer': this.options['allow_composer'],
+ };
+ // add token field to allow to post comment without being logged
+ if (self.options['token']) {
+ data['token'] = self.options['token'];
+ }
+ // add domain
+ if (this.get('domain')) {
+ data['domain'] = this.get('domain');
+ }
+ return data;
+ },
+ /**
+ * Generate the pager data for the given page number
+ *
+ * @private
+ * @param {Number} page
+ * @returns {Object}
+ */
+ _pager: function (page) {
+ page = page || 1;
+ var total = this.get('message_count');
+ var scope = this.options['pager_scope'];
+ var step = this.options['pager_step'];
+
+ // Compute Pager
+ var pageCount = Math.ceil(parseFloat(total) / step);
+
+ page = Math.max(1, Math.min(parseInt(page), pageCount));
+ scope -= 1;
+
+ var pmin = Math.max(page - parseInt(Math.floor(scope / 2)), 1);
+ var pmax = Math.min(pmin + scope, pageCount);
+
+ if (pmax - scope > 0) {
+ pmin = pmax - scope;
+ } else {
+ pmin = 1;
+ }
+
+ var pages = [];
+ _.each(_.range(pmin, pmax + 1), function (index) {
+ pages.push(index);
+ });
+
+ return {
+ "page_count": pageCount,
+ "offset": (page - 1) * step,
+ "page": page,
+ "page_start": pmin,
+ "page_previous": Math.max(pmin, page - 1),
+ "page_next": Math.min(pmax, page + 1),
+ "page_end": pmax,
+ "pages": pages
+ };
+ },
+ _renderMessages: function () {
+ this.$('.o_portal_chatter_messages').html(qweb.render("portal.chatter_messages", {widget: this}));
+ },
+ _renderMessageCount: function () {
+ this.$('.o_message_counter').replaceWith(qweb.render("portal.chatter_message_count", {widget: this}));
+ },
+ _renderPager: function () {
+ this.$('.o_portal_chatter_pager').replaceWith(qweb.render("portal.pager", {widget: this}));
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ _onChangeDomain: function () {
+ var self = this;
+ this.messageFetch().then(function () {
+ var p = self._currentPage;
+ self.set('pager', self._pager(p));
+ });
+ },
+ /**
+ * @private
+ * @param {MouseEvent} event
+ */
+ _onClickPager: function (ev) {
+ ev.preventDefault();
+ var page = $(ev.currentTarget).data('page');
+ this._changeCurrentPage(page);
+ },
+
+ /**
+ * Toggle is_internal state of message. Update both node data and
+ * classes to ensure DOM is updated accordingly to RPC call result.
+ * @private
+ * @returns {Promise}
+ */
+ _onClickUpdateIsInternal: function (ev) {
+ ev.preventDefault();
+
+ var $elem = $(ev.currentTarget);
+ return this._rpc({
+ route: '/mail/update_is_internal',
+ params: {
+ message_id: $elem.data('message-id'),
+ is_internal: ! $elem.data('is-internal'),
+ },
+ }).then(function (result) {
+ $elem.data('is-internal', result);
+ if (result === true) {
+ $elem.addClass('o_portal_message_internal_on');
+ $elem.removeClass('o_portal_message_internal_off');
+ } else {
+ $elem.addClass('o_portal_message_internal_off');
+ $elem.removeClass('o_portal_message_internal_on');
+ }
+ });
+ },
+});
+
+publicWidget.registry.portalChatter = publicWidget.Widget.extend({
+ selector: '.o_portal_chatter',
+
+ /**
+ * @override
+ */
+ async start() {
+ const proms = [this._super.apply(this, arguments)];
+ const chatter = new PortalChatter(this, this.$el.data());
+ proms.push(chatter.appendTo(this.$el));
+ await Promise.all(proms);
+ // scroll to the right place after chatter loaded
+ if (window.location.hash === `#${this.el.id}`) {
+ dom.scrollTo(this.el, {duration: 0});
+ }
+ },
+});
+
+return {
+ PortalChatter: PortalChatter,
+};
+});