Files
odoo_source/addons/knowledge/static/src/js/knowledge_controller.js
T
abd-msyukyu-odoo 4ed03d5940 [IMP] knowledge: reorder functions alphabetically
Small reorganisation of the code to order functions alphabetically in
knowledge_controller and knowledge_renderer

Task-2858428

X-original-commit: 874dc4a93af72c706ff756b29af96f7030a3423a
Part-of: odoo/odoo#93088
2022-06-08 15:46:02 +02:00

434 lines
14 KiB
JavaScript

/** @odoo-module */
import { _t, bus } from 'web.core';
import Dialog from 'web.Dialog';
import FormController from 'web.FormController';
import { MoveArticleToDialog } from './widgets/knowledge_dialogs.js';
import emojis from '@mail/js/emojis';
const disallowedEmojis = ['💩', '👎', '💔', '😭', '😢', '😐', '😕', '😞', '😢', '💀'];
const emojisRandomPickerSource = emojis.filter(emoji => !disallowedEmojis.includes(emoji.unicode));
const KnowledgeArticleFormController = FormController.extend({
events: Object.assign({}, FormController.prototype.events, {
'click .o_knowledge_add_icon': '_onAddRandomIcon',
'click .o_knowledge_add_cover': '_onAddCover',
'click #knowledge_search_bar': '_onSearch',
'click .o_breadcrumb_article_name': '_onArticleBreadcrumbClick',
'change .o_breadcrumb_article_name': '_onRename',
'click i.o_toggle_favorite': '_onToggleFavorite',
'input .o_breadcrumb_article_name': '_adjustInputSize',
}),
custom_events: Object.assign({}, FormController.prototype.custom_events, {
create: '_onCreate',
duplicate: '_onDuplicate',
move: '_onMove',
open_move_to_modal: '_onOpenMoveToModal',
reload_tree: '_onReloadTree',
emoji_click: '_onEmojiClick',
}),
/**
* Register the fact that the current @see FormController is one from
* Knowledge in order not to try and search for a matching record for
* @see KnowledgeService .
*
* @override
*/
init: function (parent, model, renderer, params) {
/**
* This property is used to specify that the current form view will
* get/use records stored in the @see KnowledgeService instead of
* replacing them (default). TODO ABD: maybe set this as an option of
* the form view (XML) so that other form views can easily specify that
* they will get/use records from the KnowledgeService instead of
* providing them.
*/
this.ignoreKnowledgeRecordSearch = true;
this.renderer = renderer;
this._super.apply(this, arguments);
this.onFieldSavedListeners = new Map();
},
/**
* @override
*/
saveRecord: async function () {
const modifiedFields = await this._super(...arguments);
const { data } = this.model.get(this.handle);
for (const field of modifiedFields) {
if (this.onFieldSavedListeners.has(field)) {
this.onFieldSavedListeners.get(field).forEach(listener => {
listener.call(this, data[field]);
});
}
}
return modifiedFields;
},
/**
* @override
* @returns {Promise}
*/
start: function () {
return this._super.apply(this, arguments).then(() => {
this._onFieldSaved('icon', unicode => {
const { id } = this.getState();
this.renderer._setEmoji(id, unicode);
});
});
},
// Listeners:
/**
* @param {Event} event
*/
_adjustInputSize: function (event) {
event.target.setAttribute('size', event.target.value.length);
},
_onAddCover: async function() {
if (this.mode === 'readonly') {
await this._setMode('edit');
}
this.$('.o_input_file').click();
},
_onAddRandomIcon: function() {
this.trigger_up('field_changed', {
dataPointID: this.handle,
changes: {
'icon': emojisRandomPickerSource[Math.floor(Math.random() * emojisRandomPickerSource.length)].unicode,
}
});
},
/**
* When the user clicks on the name of the article, checks if the article
* name hasn't been set yet. If it hasn't, it will look for a title in the
* body of the article and set it as the name of the article.
* @param {Event} event
*/
_onArticleBreadcrumbClick: async function (event) {
const name = event.currentTarget.value;
if (name === _t('New Article')) {
const $articleTitle = this.$('.o_knowledge_editor h1');
if ($articleTitle.text().length !== 0) {
this.$('.o_breadcrumb_article_name').val($articleTitle.text());
this.trigger_up('field_changed', {
dataPointID: this.handle,
changes: {
'name': $articleTitle.text(),
}
});
this._rename(await this._getId(), $articleTitle.text());
}
}
},
/**
* @param {OdooEvent} event
*/
_onCreate: async function (event) {
await this._create(event.data);
},
/**
* @param {OdooEvent} event
*/
_onDuplicate: async function (event) {
const handle = await this.model.duplicateRecord(this.handle);
const { res_id } = this.model.get(handle);
this.do_action('knowledge.ir_actions_server_knowledge_home_page', {
additional_context: { res_id }
});
},
/**
* @param {Event} event
*/
_onEmojiClick: async function (event) {
const { id } = this.getState();
const { articleId, unicode } = event.data;
if (articleId === id) {
this.trigger_up('field_changed', {
dataPointID: this.handle,
changes: {
icon: unicode
}
});
} else {
const result = await this._rpc({
model: 'knowledge.article',
method: 'write',
args: [[articleId], { icon: unicode }],
});
if (result) {
this.renderer._setEmoji(articleId, unicode);
}
}
},
/**
* @override
* @param {Event} event
*/
_onFieldChanged: async function (event) {
this._super(...arguments);
const { changes } = event.data;
for (const field of this._getFieldsToForceSave()) {
if (changes.hasOwnProperty(field)) {
await this.saveRecord(this.handle, {
reload: false,
stayInEdit: true
});
return;
}
}
},
/**
* @param {String} name - field name
* @param {Function} callback
*/
_onFieldSaved: function (name, callback) {
if (this.onFieldSavedListeners.has(name)) {
this.onFieldSavedListeners.get(name).push(callback);
} else {
this.onFieldSavedListeners.set(name, [callback]);
}
},
/**
* When the user clicks on a field in readonly mode, a new 'quick_edit' event
* will be triggered. To prevent the view from switching to the edit mode when
* the article is locked, we will overwrite the `_onQuickEdit` handler. This
* function will now ignore the event if the article is locked.
* @override
*/
_onQuickEdit: function () {
const { data } = this.model.get(this.handle);
if (data.is_locked) {
return;
}
this._super.apply(this, arguments);
},
/**
* @param {Event} event
*/
_onMove: function (event) {
this._confirmMove(event.data);
},
/**
* Opens the "Move To" modal
* @param {OdooEvent} event
*/
_onOpenMoveToModal: async function (event) {
const id = await this._getId();
const state = this.model.get(this.handle);
const dialog = new MoveArticleToDialog(this, {}, {
state: state,
/**
* @param {String} value
*/
onSave: async value => {
const params = { article_id: id };
if (typeof value === 'number') {
params.target_parent_id = value;
} else {
params.newCategory = value;
params.oldCategory = state.data.category;
}
this._confirmMove({...params,
onSuccess: () => {
dialog.close();
this.reload();
},
onReject: () => {}
});
}
});
dialog.open();
},
/**
* @param {Event} event
*/
_onReloadTree: function (event) {
// TODO JBN: Create a widget for the tree and reload it without reloading the whole view.
this.reload();
},
/**
* Callback function called when the user renames the active article.
* The function will update the name of the articles in the aside block.
* @param {Event} event
*/
_onRename: async function (event) {
var name = event.currentTarget.value;
if (name.length === 0) {
name = _t('New Article');
}
const id = await this._getId();
await this._rename(id, name);
},
/**
* @param {Event} event
*/
_onSearch: function (event) {
// TODO: change to this.env.services.commandes.openMainPalette when form views are migrated to owl
bus.trigger("openMainPalette", {
searchValue: "?",
});
},
/**
* @param {Event} event
*/
_onToggleFavorite: async function (event) {
const id = await this._getId();
const active = await this._toggleFavorite(id);
const $target = $(event.target);
$target.toggleClass('fa-star-o', !active);
$target.toggleClass('fa-star', active);
$target.attr('title', active ? _t('Remove from favorites') : _t('Add to favorites'));
this._rpc({
route: '/knowledge/tree_panel/favorites',
params: {
active_article_id: id,
}
}).then(template => {
const $dom = $(template);
this.$(".o_favorite_container").replaceWith($dom);
this.renderer._setTreeFavoriteListener();
this.renderer._renderEmojiPicker($dom);
});
},
// API calls:
/**
* @param {Object} data
* @param {integer} data.article_id
* @param {String} data.oldCategory
* @param {String} data.newCategory
* @param {integer} [data.target_parent_id]
* @param {integer} [data.before_article_id]
* @param {Function} data.onSuccess
* @param {Function} data.onReject
*/
_confirmMove: async function (data) {
data['params'] = {
is_private: data.newCategory === 'private'
};
if (typeof data.target_parent_id !== 'undefined') {
data['params'].parent_id = data.target_parent_id;
}
if (typeof data.before_article_id !== 'undefined') {
data['params'].before_article_id = data.before_article_id;
}
if (data.newCategory === data.oldCategory) {
await this._move(data);
} else {
let message, confirmation_message;
if (data.newCategory === 'workspace') {
message = _t("Are you sure you want to move this article to the Workspace? It will be shared with all internal users.");
confirmation_message = _t("Move to Workspace");
} else if (data.newCategory === 'private') {
message = _t("Are you sure you want to move this to private? Only you will be able to access it.");
confirmation_message = _t("Set as Private");
}
Dialog.confirm(this, message, {
cancel_callback: data.onReject,
buttons: [{
text: confirmation_message,
classes: 'btn-primary',
close: true,
click: async () => {
await this._move(data);
}
}, {
text: _t("Discard"),
close: true,
click: data.onReject,
}],
});
}
},
/**
* @param {Object} data
* @param {String} data.category
* @param {integer} data.target_parent_id
*/
_create: async function (data) {
const articleId = await this._rpc({
model: 'knowledge.article',
method: 'article_create',
args: [[]],
kwargs: {
is_private: data.category === 'private',
parent_id: data.target_parent_id ? data.target_parent_id : false
},
});
if (!articleId) {
return;
}
this.do_action('knowledge.ir_actions_server_knowledge_home_page', {
stackPosition: 'replaceCurrentAction',
additional_context: {
res_id: articleId
}
});
},
/**
* @returns {Array[String]}
*/
_getFieldsToForceSave: function () {
return ['full_width', 'icon', 'cover'];
},
/**
* @returns {integer}
*/
_getId: async function () {
let state = this.getState();
if (typeof state.id === 'undefined') {
await this.saveRecord(this.handle);
state = this.getState();
}
return state.id;
},
/**
* @param {Object} data
* @param {integer} data.article_id
* @param {Function} data.onSuccess
* @param {Function} data.onReject
* @param {Object} data.params
* @return {Promise}
*/
_move: function (data) {
return this._rpc({
model: 'knowledge.article',
method: 'move_to',
args: [data.article_id],
kwargs: data.params
}).then(result => {
if (result) {
data.onSuccess();
} else {
data.onReject();
}
}).catch(error => {
data.onReject();
})
},
/**
* @param {integer} id - Target id
* @param {string} name - Target Name
*/
_rename: async function (id, name) {
this.$(`.o_knowledge_tree .o_article[data-article-id="${id}"] > .o_article_handle > .o_article_name`).text(name);
this.$(`.o_breadcrumb_article_name`).val(name);
},
/**
* @param {integer} id - article id
* @returns {Promise}
*/
_toggleFavorite: function (id) {
return this._rpc({
model: 'knowledge.article',
method: 'action_toggle_favorite',
args: [id]
});
},
});
export {
KnowledgeArticleFormController,
};