This commit is contained in:
Yuri Kuznetsov
2022-06-17 12:35:55 +03:00
parent f2c5fdf9b5
commit 56dfe8d88a
18 changed files with 692 additions and 158 deletions
+29 -29
View File
@@ -69,30 +69,30 @@ function (
/** Bull */Bull,
Ui,
Utils,
AclManager,
Cache,
Storage,
Settings,
Language,
Metadata,
FieldManager,
User,
Preferences,
ModelFactory,
CollectionFactory,
PreLoader,
BaseController,
Router,
DateTime,
LayoutManager,
ThemeManager,
SessionStorage,
ViewHelper,
WebSocketManager,
/** typeof module:acl-manager.Class */AclManager,
/** typeof module:cache.Class */Cache,
/** typeof module:storage.Class */Storage,
/** typeof module:models/settings.Class */Settings,
/** typeof module:language.Class */Language,
/** typeof module:metadata.Class */Metadata,
/** typeof module:field-manager.Class */FieldManager,
/** typeof module:models/user.Class */User,
/** typeof module:models/preferences.Class */Preferences,
/** typeof module:model-factory.Class */ModelFactory,
/** typeof module:collection-factory.Class */CollectionFactory,
/** typeof module:pre-loader.Class */PreLoader,
/** typeof module:controllers/base.Class */BaseController,
/** typeof module:router.Class */Router,
/** typeof module:date-time.Class */DateTime,
/** typeof module:layout-manager.Class */LayoutManager,
/** typeof module:theme-manager.Class */ThemeManager,
/** typeof module:session-storage.Class */SessionStorage,
/** typeof module:view-helper.Class */ViewHelper,
/** typeof module:web-socket-manager.Class */WebSocketManager,
Ajax,
NumberUtil,
PageTitle,
BroadcastChannel
/** typeof module:number.Class */NumberUtil,
/** typeof module:page-title.Class */PageTitle,
/** typeof module:broadcast-channel.Class */BroadcastChannel
) {
/**
* A main application class.
@@ -249,6 +249,8 @@ function (
auth: null,
/**
* A base controller.
*
* @private
* @type {module:controllers/base.Class}
*/
@@ -286,6 +288,7 @@ function (
viewFactory: null,
/**
* @type {Function}
* @private
*/
viewLoader: null,
@@ -712,8 +715,6 @@ function (
initView: function () {
let helper = this.viewHelper = new ViewHelper();
// @todo Use `helper.container`.
helper.layoutManager = new LayoutManager(this.cache, this.id);
helper.settings = this.settings;
helper.config = this.settings;
@@ -723,19 +724,18 @@ function (
helper.modelFactory = this.modelFactory;
helper.collectionFactory = this.collectionFactory;
helper.storage = this.storage;
helper.sessionStorage = this.sessionStorage;
helper.dateTime = this.dateTime;
helper.language = this.language;
helper.metadata = this.metadata;
helper.fieldManager = this.fieldManager;
helper.cache = this.cache;
helper.storage = this.storage;
helper.themeManager = this.themeManager;
helper.sessionStorage = this.sessionStorage;
helper.basePath = this.basePath;
helper.appParams = this.appParams;
helper.webSocketManager = this.webSocketManager;
helper.numberUtil = this.numberUtil;
helper.pageTitle = new PageTitle(this.settings);
helper.basePath = this.basePath;
helper.appParams = this.appParams;
this.viewLoader = (viewName, callback) => {
require(Utils.composeViewClassName(viewName), callback);
+40 -14
View File
@@ -26,10 +26,21 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('controllers/base', 'controller', function (Dep) {
define('controllers/base', ['controller'], function (Dep) {
return Dep.extend({
/**
* A base controller.
*
* @class
* @name Class
* @extends module:controller.Class
* @memberOf module:controllers/base
*/
return Dep.extend(/** @lends module:controllers/base.Class# */{
/**
* Log in.
*/
login: function () {
var viewName = this.getConfig().get('loginView') || 'views/login';
@@ -64,6 +75,9 @@ define('controllers/base', 'controller', function (Dep) {
});
},
/**
* @private
*/
normalizeLoginData: function (userName, data) {
return {
auth: {
@@ -79,10 +93,9 @@ define('controllers/base', 'controller', function (Dep) {
};
},
actionLogin: function () {
this.login();
},
/**
* Log out.
*/
logout: function () {
var title = this.getConfig().get('applicationName') || 'EspoCRM';
$('head title').text(title);
@@ -90,6 +103,21 @@ define('controllers/base', 'controller', function (Dep) {
this.trigger('logout');
},
/**
* Clear cache.
*/
clearCache: function () {
this.entire('views/clear-cache', {
cache: this.getCache()
}, view => {
view.render();
});
},
actionLogin: function () {
this.login();
},
actionLogout: function () {
this.logout();
},
@@ -98,20 +126,18 @@ define('controllers/base', 'controller', function (Dep) {
this.clearCache();
},
clearCache: function () {
this.entire('views/clear-cache', {
cache: this.getCache()
}, (view) => {
view.render();
});
},
/**
* Error Not Found.
*/
error404: function () {
this.entire('views/base', {template: 'errors/404'}, (view) => {
view.render();
});
},
/**
* Error Forbidden.
*/
error403: function () {
this.entire('views/base', {template: 'errors/403'}, (view) => {
view.render();
+2 -2
View File
@@ -112,8 +112,8 @@ define('language', ['ajax'], function (Ajax) {
* Translate a label.
*
* @param {string} name An item name.
* @param {string} [category='labels'] A category.
* @param {string} [scope='Global'] A scope.
* @param {string|null} [category='labels'] A category.
* @param {string|null} [scope='Global'] A scope.
* @returns {string}
*/
translate: function (name, category, scope) {
+1 -1
View File
@@ -26,7 +26,7 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('models/preferences', 'model', function (Dep) {
define('models/preferences', ['model'], function (Dep) {
return Dep.extend({
+24 -2
View File
@@ -26,12 +26,28 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('models/settings', 'model', function (Dep) {
define('models/settings', ['model'], function (Dep) {
return Dep.extend({
/**
* A config.
*
* @class
* @name Class
* @extends module:model.Class
* @memberOf module:models/settings
*/
return Dep.extend(/** @lends module:models/settings.Class# */{
/**
* @inheritDoc
*/
name: 'Settings',
/**
* Load.
*
* @returns {Promise}
*/
load: function () {
return new Promise(resolve => {
this.fetch()
@@ -39,6 +55,12 @@ define('models/settings', 'model', function (Dep) {
});
},
/**
* Get a value by a path.
*
* @param {string} arr A path.
* @returns {*} Null if not set.
*/
getByPath: function (arr) {
if (!arr.length) {
return null;
+131 -4
View File
@@ -28,14 +28,93 @@
define('search-manager', [], function () {
let SearchManager = function (collection, type, storage, dateTime, defaultData, emptyOnReset) {
/**
* Search data.
*
* @typedef {Object} module:search-manager~data
*
* @property {string} primary A primary filter.
* @property {Object.<string,boolean>} bool Bool filters.
* @property {Object} advanced Advanced filters (field filters). Contains data needed for both
* the backend and frontend.
*/
/**
* A where item. Sent to the backend.
*
* @typedef {Object} module:search-manager~whereItem
*
* @property {string} type A type.
* @property {string} [attribute] An attribute (field).
* @property {module:search-manager~whereItem[]|string|number|boolean|null} [value] A value.
* @property {boolean} [dateTime] Is a date-time item.
* @property {string} [timeZone] A time-zone (for date-time items).
*/
/**
* A search manager.
*
* @class
* @name Class
* @memberOf module:search-manager
*
* @param {module:collection.Class} collection A collection.
* @param {string|null} type A type. Used for a storage key.
* @param {module:storage.Class|null} storage A storage.
* @param {module:date-time.Class|null} dateTime A date-time util.
* @param {module:search-manager~data|null} [defaultData=null] Default search data.
* @param {boolean} [emptyOnReset=false] To empty on reset.
*/
let SearchManager = function (
collection,
type,
storage,
dateTime,
defaultData,
emptyOnReset
) {
/**
* @private
* @type {module:collection.Class}
*/
this.collection = collection;
/**
* An entity type.
*
* @public
* @type {string}
*/
this.scope = collection.name;
/**
* @private
* @type {module:storage.Class|null}
*/
this.storage = storage;
/**
* @private
* @type {string}
*/
this.type = type || 'list';
/**
* @private
* @type {module:date-time.Class|null}
*/
this.dateTime = dateTime;
/**
* @private
* @type {boolean}
*/
this.emptyOnReset = emptyOnReset;
/**
* @private
* @type {Object}
*/
this.emptyData = {
textFilter: '',
bool: {},
@@ -58,10 +137,11 @@ define('search-manager', [], function () {
this.sanitizeData();
};
_.extend(SearchManager.prototype, {
data: null,
_.extend(SearchManager.prototype, /** @lends module:search-manager.Class# */{
/**
* @private
*/
sanitizeData: function () {
if (!('advanced' in this.data)) {
this.data.advanced = {};
@@ -76,6 +156,11 @@ define('search-manager', [], function () {
}
},
/**
* Get a where clause. The where clause to be sent to the backend.
*
* @returns {module:search-manager~whereItem[]}
*/
getWhere: function () {
let where = [];
@@ -131,6 +216,9 @@ define('search-manager', [], function () {
return where;
},
/**
* @private
*/
getWherePart: function (name, defs) {
var attribute = name;
@@ -182,6 +270,11 @@ define('search-manager', [], function () {
};
},
/**
* Load stored data.
*
* @returns {module:search-manager.Class}
*/
loadStored: function () {
this.data =
this.storage.get(this.type + 'Search', this.scope) ||
@@ -193,28 +286,53 @@ define('search-manager', [], function () {
return this;
},
/**
* Get data.
*
* @returns {module:search-manager~data}
*/
get: function () {
return this.data;
},
/**
* Set advanced filters.
*
* @param {Object} advanced Advanced filters.
*/
setAdvanced: function (advanced) {
this.data = Espo.Utils.clone(this.data);
this.data.advanced = advanced;
},
/**
* Set bool filters.
*
* @param {Object.<string, boolean>} bool Bool filters.
*/
setBool: function (bool) {
this.data = Espo.Utils.clone(this.data);
this.data.bool = bool;
},
/**
* Set a primary filter.
*
* @param {string} primary A filter.
*/
setPrimary: function (primary) {
this.data = Espo.Utils.clone(this.data);
this.data.primary = primary;
},
/**
* Set data.
*
* @param {module:search-manager~data} data Data.
*/
set: function (data) {
this.data = data;
@@ -223,6 +341,9 @@ define('search-manager', [], function () {
}
},
/**
* Empty data.
*/
empty: function () {
this.data = Espo.Utils.clone(this.emptyData);
@@ -231,6 +352,9 @@ define('search-manager', [], function () {
}
},
/**
* Reset.
*/
reset: function () {
if (this.emptyOnReset) {
this.empty();
@@ -245,6 +369,9 @@ define('search-manager', [], function () {
}
},
/**
* @private
*/
getDateTimeWhere: function (type, field, value) {
var where = {
field: field
+34 -1
View File
@@ -28,10 +28,26 @@
define('session-storage', ['storage'], function (Dep) {
return Dep.extend({
/**
* A session storage. Cleared when a page session ends.
*
* @class
* @name Class
* @memberOf module:session-storage
*/
return Dep.extend(/** @lends module:session-storage.Class# */{
/**
* @private
*/
storageObject: sessionStorage,
/**
* Get a value.
*
* @param {string} name A name.
* @returns {*} Null if not set.
*/
get: function (name) {
try {
var stored = this.storageObject.getItem(name);
@@ -62,6 +78,12 @@ define('session-storage', ['storage'], function (Dep) {
return null;
},
/**
* Set (store) a value.
*
* @param {string} name A name.
* @param {*} value A value.
*/
set: function (name, value) {
if (value instanceof Object || Array.isArray(value) || value === true || value === false) {
value = '__JSON__:' + JSON.stringify(value);
@@ -75,10 +97,21 @@ define('session-storage', ['storage'], function (Dep) {
}
},
/**
* Has a value.
*
* @param {string} name A name.
* @returns {boolean}
*/
has: function (name) {
return this.storageObject.getItem(name) !== null;
},
/**
* Clear a value.
*
* @param {string} name A name.
*/
clear: function (name) {
for (let i in this.storageObject) {
if (i === name) {
+60 -2
View File
@@ -28,28 +28,66 @@
define('storage', [], function () {
/**
* A storage. Data is saved across browser sessions, has no expiration time.
*
* @class
* @name Class
* @memberOf module:storage
*/
let Storage = function () {};
_.extend(Storage.prototype, {
_.extend(Storage.prototype, /** @lends module:storage.Class# */{
/**
* @protected
*/
prefix: 'espo',
/**
* @protected
*/
storageObject: localStorage,
/**
* @private
* @param {string} type
* @returns {string}
*/
composeFullPrefix: function (type) {
return this.prefix + '-' + type;
},
/**
* @private
* @param {string} type
* @param {string} name
* @returns {string}
*/
composeKey: function (type, name) {
return this.composeFullPrefix(type) + '-' + name;
},
/**
* @private
* @param {string} type
*/
checkType: function (type) {
if (typeof type === 'undefined' && toString.call(type) !== '[object String]' || type === 'cache') {
if (
typeof type === 'undefined' &&
toString.call(type) !== '[object String]' || type === 'cache'
) {
throw new TypeError("Bad type \"" + type + "\" passed to Espo.Storage.");
}
},
/**
* Has a value.
*
* @param {string} type A type (category).
* @param {string} name A name.
* @returns {boolean}
*/
has: function (type, name) {
this.checkType(type);
@@ -58,6 +96,13 @@ define('storage', [], function () {
return this.storageObject.getItem(key) !== null;
},
/**
* Get a value.
*
* @param {string} type A type (category).
* @param {string} name A name.
* @returns {*} Null if not stored.
*/
get: function (type, name) {
this.checkType(type);
@@ -100,6 +145,13 @@ define('storage', [], function () {
return null;
},
/**
* Set (store) a value.
*
* @param {string} type A type (category).
* @param {string} name A name.
* @param {*} value A value.
*/
set: function (type, name, value) {
this.checkType(type);
@@ -118,6 +170,12 @@ define('storage', [], function () {
}
},
/**
* Clear a value.
*
* @param {string} type A type (category).
* @param {string} name A name.
*/
clear: function (type, name) {
let reText;
+61 -5
View File
@@ -28,20 +28,39 @@
define('theme-manager', [], function () {
/**
* A theme manager.
*
* @class
* @name Class
* @memberOf module:theme-manager
*
* @param {module:models/settings.Class} config A config.
* @param {module:models/preferences.Class} preferences Preferences.
* @param {module:metadata.Class} metadata Metadata.
*/
let ThemeManager = function (config, preferences, metadata) {
this.config = config;
this.preferences = preferences;
this.metadata = metadata;
};
_.extend(ThemeManager.prototype, {
_.extend(ThemeManager.prototype, /** module:theme-manager.Class# */{
/**
* @private
*/
defaultParams: {
screenWidthXs: 768,
dashboardCellHeight: 155,
dashboardCellMargin: 19,
},
/**
* Get a theme name for the current user.
*
* @returns {string}
*/
getName: function () {
if (!this.config.get('userThemesDisabled')) {
let name = this.preferences.get('theme');
@@ -54,6 +73,11 @@ define('theme-manager', [], function () {
return this.config.get('theme');
},
/**
* Get a theme name currently applied to the DOM.
*
* @returns {string|null} Null if not applied.
*/
getAppliedName: function () {
let name = window.getComputedStyle(document.body).getPropertyValue('--theme-name');
@@ -64,6 +88,11 @@ define('theme-manager', [], function () {
return name.trim();
},
/**
* Whether a current theme is applied to the DOM.
*
* @returns {boolean}
*/
isApplied: function () {
let appliedName = this.getAppliedName();
@@ -74,8 +103,14 @@ define('theme-manager', [], function () {
return this.getName() === appliedName;
},
/**
* Get a stylesheet path for a current theme.
*
* @returns {string}
*/
getStylesheet: function () {
let link = this.metadata.get(['themes', this.getName(), 'stylesheet']) || 'client/css/espo/espo.css';
let link = this.metadata.get(['themes', this.getName(), 'stylesheet']) ||
'client/css/espo/espo.css';
if (this.config.get('cacheTimestamp')) {
link += '?r=' + this.config.get('cacheTimestamp').toString();
@@ -84,6 +119,11 @@ define('theme-manager', [], function () {
return link;
},
/**
* Get an iframe stylesheet path for a current theme.
*
* @returns {string}
*/
getIframeStylesheet: function () {
let link = this.metadata.get(['themes', this.getName(), 'stylesheetIframe']) ||
'client/css/espo/espo-iframe.css';
@@ -95,6 +135,11 @@ define('theme-manager', [], function () {
return link;
},
/**
* Get an iframe-fallback stylesheet path for a current theme.
*
* @returns {string}
*/
getIframeFallbackStylesheet: function () {
let link = this.metadata.get(['themes', this.getName(), 'stylesheetIframeFallback']) ||
'client/css/espo/espo-iframe.css';
@@ -106,10 +151,22 @@ define('theme-manager', [], function () {
return link;
},
/**
* Get a theme parameter.
*
* @param {string} name A parameter name.
* @returns {*} Null if not set.
*/
getParam: function (name) {
return this.metadata.get(['themes', this.getName(), name]) || this.defaultParams[name] || null;
return this.metadata.get(['themes', this.getName(), name]) ||
this.defaultParams[name] || null;
},
/**
* Whether a current theme is different from a system default theme.
*
* @returns {boolean}
*/
isUserTheme: function () {
if (!this.config.get('userThemesDisabled')) {
let name = this.preferences.get('theme');
@@ -122,8 +179,7 @@ define('theme-manager', [], function () {
}
return false;
}
},
});
return ThemeManager;
+260 -80
View File
@@ -26,19 +26,28 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('view-helper', ['lib!marked', 'lib!dompurify'], function (marked, DOMPurify) {
define('view-helper', ['lib!marked', 'lib!dompurify', 'lib!handlebars'],
function (marked, DOMPurify, /** typeof Handlebars */Handlebars) {
/**
* @class Espo.ViewHelper
* A view helper.
*
* @class
* @name Class
* @memberOf module:view-helper
*/
let ViewHelper = function () {
this._registerHandlebarsHelpers();
/**
* @private
*/
this.mdBeforeList = [
{
regex: /\&\#x60;\&\#x60;\&\#x60;\n?([\s\S]*?)\&\#x60;\&\#x60;\&\#x60;/g,
value: function (s, string) {
return '<pre><code>' +string.replace(/\*/g, '&#42;').replace(/\~/g, '&#126;') + '</code></pre>';
return '<pre><code>' +string.replace(/\*/g, '&#42;').replace(/\~/g, '&#126;') +
'</code></pre>';
}
},
{
@@ -78,97 +87,159 @@ define('view-helper', ['lib!marked', 'lib!dompurify'], function (marked, DOMPuri
});
};
_.extend(ViewHelper.prototype, /** @lends Espo.ViewHelper */{
_.extend(ViewHelper.prototype, /** @lends module:view-helper.Class# */{
/**
* A layout manager.
*
* @type {module:layout-manager.Class}
*/
layoutManager: null,
/**
* @property {Espo.Modles.Settings} settings
* A config.
*
* @type {module:models/settings.Class}
*/
settings: null,
/**
* @property {Espo.Modles.Settings} settings
* A config.
*
* @type {module:models/settings.Class}
*/
config: null,
/**
* A current user.
*
* @type {module:models/user.Class}
*/
user: null,
/**
* A preferences.
*
* @type {module:models/preferences.Class}
*/
preferences: null,
/**
* @property {Espo.Language} language
* An ACL manager.
*
* @type {module:acl-manager.Class}
*/
acl: null,
/**
* A model factory.
*
* @type {module:model-factory.Class}
*/
modelFactory: null,
/**
* A collection factory.
*
* @type {module:collection-factory.Class}
*/
collectionFactory: null,
/**
* A storage.
*
* @type {module:storage.Class}
*/
storage: null,
/**
* A session storage.
*
* @type {module:session-storage.Class}
*/
sessionStorage: null,
/**
* A date-time util.
*
* @type {module:date-time.Class}
*/
dateTime: null,
/**
* A language.
*
* @type {module:language.Class}
*/
language: null,
/**
* @param {string} name
* @returns {*}
* A metadata.
*
* @type {module:metadata.Class}
*/
getAppParam: function (name) {
return (this.appParams || {})[name];
},
metadata: null,
/**
* @param {string} text
* @returns {string}
* A field-manager util.
*
* @type {module:field-manager.Class}
*/
stripTags: function (text) {
text = text || '';
if (typeof text === 'string' || text instanceof String) {
return text.replace(/<\/?[^>]+(>|$)/g, '');
}
return text;
},
fieldManager: null,
/**
* @param {string} text
* @returns {string}
* A cache.
*
* @type {module:cache.Class}
*/
escapeString: function (text) {
return Handlebars.Utils.escapeExpression(text);
},
cache: null,
/**
* @param {string} id
* @param {'small'|'medium'|'large'} size
* @param {int} width
* @param {string} additionalClassName
* @returns {string}
* A theme manager.
*
* @type {module:theme-manager.Class}
*/
getAvatarHtml: function (id, size, width, additionalClassName) {
if (this.config.get('avatarsDisabled')) {
return '';
}
themeManager: null,
var t;
/**
* A web-socket manager
*
* @type {module:web-socket-manager.Class}
*/
webSocketManager: null,
var cache = this.cache;
/**
* A number util.
*
* @type {module:number.Class}
*/
numberUtil: null,
if (cache) {
t = cache.get('app', 'timestamp');
}
else {
t = Date.now();
}
/**
* A page-title util.
*
* @type {module:page-title.Class}
*/
pageTitle: null,
var basePath = this.basePath || '';
var size = size || 'small';
/**
* A base path.
*
* @type {string}
*/
basePath: '',
var width = (width || 16).toString();
var className = 'avatar';
if (additionalClassName) {
className += ' ' + additionalClassName;
}
return '<img class="'+className+'" width="'+width+'" src="'+basePath+
'?entryPoint=avatar&size='+size+'&id=' + id + '&t='+t+'">';
},
/**
* Application parameters.
*
* @type {Object}
*/
appParams: null,
/**
* @private
*/
_registerHandlebarsHelpers: function () {
Handlebars.registerHelper('img', img => {
return new Handlebars.SafeString("<img src=\"img/" + img + "\"></img>");
});
@@ -268,7 +339,7 @@ define('view-helper', ['lib!marked', 'lib!dompurify'], function (marked, DOMPuri
options.hash.text ||
this.language.translate(label, 'labels', scope);
className = options.hash.className || '';
let className = options.hash.className || '';
if (className) {
className = ' ' + className;
@@ -297,7 +368,7 @@ define('view-helper', ['lib!marked', 'lib!dompurify'], function (marked, DOMPuri
});
Handlebars.registerHelper('complexText', (text, options) => {
return this.transfromMarkdownText(text, options.hash);
return this.transformMarkdownText(text, options.hash);
});
Handlebars.registerHelper('translateOption', (name, options) => {
@@ -400,19 +471,100 @@ define('view-helper', ['lib!marked', 'lib!dompurify'], function (marked, DOMPuri
},
/**
* @param {string} text
* @returns {string}
* Get an application parameter.
*
* @param {string} name
* @returns {*}
*/
transfromMarkdownInlineText: function (text) {
return this.transfromMarkdownText(text, {inline: true});
getAppParam: function (name) {
return (this.appParams || {})[name];
},
/**
* Strip tags.
*
* @deprecated
* @param {string} text
* @param {{inline: boolean, linksInNewTab: boolean}} options
* @returns {string}
*/
transfromMarkdownText: function (text, options) {
stripTags: function (text) {
text = text || '';
if (typeof text === 'string' || text instanceof String) {
return text.replace(/<\/?[^>]+(>|$)/g, '');
}
return text;
},
/**
* Escape a string.
*
* @param {string} text A string.
* @returns {string}
*/
escapeString: function (text) {
return Handlebars.Utils.escapeExpression(text);
},
/**
* Get a user avatar HTML.
*
* @param {string} id A user ID.
* @param {'small'|'medium'|'large'} [size='small'] A size.
* @param {int} [width=16]
* @param {string} [additionalClassName] An additional class-name.
* @returns {string}
*/
getAvatarHtml: function (id, size, width, additionalClassName) {
if (this.config.get('avatarsDisabled')) {
return '';
}
var t;
var cache = this.cache;
if (cache) {
t = cache.get('app', 'timestamp');
}
else {
t = Date.now();
}
var basePath = this.basePath || '';
var size = size || 'small';
var width = (width || 16).toString();
var className = 'avatar';
if (additionalClassName) {
className += ' ' + additionalClassName;
}
return '<img class="'+className+'" width="'+width+'" src="'+basePath+
'?entryPoint=avatar&size='+size+'&id=' + id + '&t='+t+'">';
},
/**
* A Markdown text to HTML (one-line).
*
* @param {string} text A text.
* @returns {string} HTML.
*/
transformMarkdownInlineText: function (text) {
return this.transformMarkdownText(text, {inline: true});
},
/**
* A Markdown text to HTML.
*
* @param {string} text A text.
* @param {{inline?: boolean, linksInNewTab?: boolean}} [options] Options.
* @returns {string} HTML.
*/
transformMarkdownText: function (text, options) {
text = text || '';
text = Handlebars.Utils.escapeExpression(text).replace(/&gt;+/g, '>');
@@ -445,9 +597,11 @@ define('view-helper', ['lib!marked', 'lib!dompurify'], function (marked, DOMPuri
},
/**
* @param {string} scope
* @param {boolean} noWhiteSpace
* @param {string} additionalClassName
* Get a color-icon HTML for a scope.
*
* @param {string} scope A scope.
* @param {boolean} [noWhiteSpace=false] No white space.
* @param {string} [additionalClassName] An additional class-name.
* @returns {string}
*/
getScopeColorIconHtml: function (scope, noWhiteSpace, additionalClassName) {
@@ -481,8 +635,10 @@ define('view-helper', ['lib!marked', 'lib!dompurify'], function (marked, DOMPuri
},
/**
* @param {type} text
* @param {Object} options
* Sanitize HTML.
*
* @param {type} text HTML.
* @param {Object} [options] Options.
* @returns {string}
*/
sanitizeHtml: function (text, options) {
@@ -490,7 +646,9 @@ define('view-helper', ['lib!marked', 'lib!dompurify'], function (marked, DOMPuri
},
/**
* @param {string} value
* Moderately sanitize HTML.
*
* @param {string} value HTML.
* @returns {string}
*/
moderateSanitizeHtml: function (value) {
@@ -527,7 +685,9 @@ define('view-helper', ['lib!marked', 'lib!dompurify'], function (marked, DOMPuri
},
/**
* @param {string} html
* Strip event handlers in HTML.
*
* @param {string} html HTML.
* @returns {string}
*/
stripEventHandlersInHtml: function (html) {
@@ -566,11 +726,11 @@ define('view-helper', ['lib!marked', 'lib!dompurify'], function (marked, DOMPuri
continue;
}
if (!lastQuote && html[j - 1] === "=" && (html[j] === "'" || html[j] === '"')){
if (!lastQuote && html[j - 1] === "=" && (html[j] === "'" || html[j] === '"')) {
lastQuote = html[j];
}
if (!lastQuote && html[j - 2] === " " && html[j - 1] === "o" && html[j] === "n"){
if (!lastQuote && html[j - 2] === " " && html[j - 1] === "o" && html[j] === "n") {
strip = j - 2;
}
@@ -585,7 +745,9 @@ define('view-helper', ['lib!marked', 'lib!dompurify'], function (marked, DOMPuri
},
/**
* @param {$} $el
* Calculate a content container height.
*
* @param {$} $el Element.
* @returns {number}
*/
calculateContentContainerHeight: function ($el) {
@@ -615,9 +777,11 @@ define('view-helper', ['lib!marked', 'lib!dompurify'], function (marked, DOMPuri
},
/**
* @param {Espo.View} view
* @param {string} type
* @param {string} scope
* Process view-setup-handlers.
*
* @param {module:view} view A view.
* @param {string} type A view-setup-handler type.
* @param {string} [scope] A scope.
*/
processSetupHandlers: function (view, type, scope) {
scope = scope || view.scope;
@@ -655,6 +819,22 @@ define('view-helper', ['lib!marked', 'lib!dompurify'], function (marked, DOMPuri
view.wait(promise);
}
},
/**
* @deprecated Use `transformMarkdownText`.
* @internal Used in extensions.
*/
transfromMarkdownText: function (text, options) {
this.transformMarkdownText(text, options);
},
/**
* @deprecated Use `transformMarkdownInlineText`.
* @internal Used in extensions.
*/
transfromMarkdownInlineText: function (text) {
return this.transformMarkdownInlineText(text);
},
});
return ViewHelper;
+4 -4
View File
@@ -113,16 +113,16 @@ define('view', [], function () {
},
/**
* Get the view-helper.
* Get a view-helper.
*
* @returns {module:view-helper}
* @returns {module:view-helper.Class}
*/
getHelper: function () {
return this._helper;
},
/**
* Get the current user.
* Get a current user.
*
* @returns {module:models/user.Class}
*/
@@ -477,7 +477,7 @@ define('view', [], function () {
}
if (message) {
message = this.getHelper().transfromMarkdownText(message, {linksInNewTab: true}).toString();
message = this.getHelper().transformMarkdownText(message, {linksInNewTab: true}).toString();
}
let confirmText = o.confirmText || this.translate('Yes');
@@ -45,7 +45,7 @@ define('views/admin/complex-expression/modals/add-function', ['views/modal', 'mo
data: function () {
var text = this.translate('formulaFunctions', 'messages', 'Admin')
.replace('{documentationUrl}', this.documentationUrl);
text = this.getHelper().transfromMarkdownText(text, {linksInNewTab: true}).toString();
text = this.getHelper().transformMarkdownText(text, {linksInNewTab: true}).toString();
return {
functionDataList: this.functionDataList,
@@ -83,10 +83,11 @@ define('views/admin/field-manager/modals/add-field', 'views/modal', function (De
if (text === type) {
$el.addClass('hidden');
return;
}
var text = this.getHelper().transfromMarkdownText(text, {linksInNewTab: true}).toString();
text = this.getHelper().transformMarkdownText(text, {linksInNewTab: true}).toString();
Espo.Ui.popover($el, {
content: text,
@@ -45,7 +45,7 @@ define('views/admin/formula/modals/add-function', ['views/modal', 'model'], func
data: function () {
var text = this.translate('formulaFunctions', 'messages', 'Admin')
.replace('{documentationUrl}', this.documentationUrl);
text = this.getHelper().transfromMarkdownText(text, {linksInNewTab: true}).toString();
text = this.getHelper().transformMarkdownText(text, {linksInNewTab: true}).toString();
return {
functionDataList: this.functionDataList,
+37 -6
View File
@@ -34,49 +34,58 @@ define('views/fields/base', ['view'], function (Dep) {
* @todo Document all options.
* @todo Document events.
*
* @class Espo.Views.Fields.Base
* @extends Espo.View
* @class
* @name Class
* @extends module:view.Class
* @memberOf module:views/fields/base
*/
return Dep.extend(/** @lends Espo.Views.Fields.Base */ {
return Dep.extend(/** @lends module:views/fields/base.Class# */ {
/**
* A field type.
*
* @type {string}
*/
type: 'base',
/**
* List mode template.
*
* @type {string}
*/
listTemplate: 'fields/base/list',
/**
* List-link mode template.
*
* @type {string}
*/
listLinkTemplate: 'fields/base/list-link',
/**
* Detail mode template.
*
* @type {string}
*/
detailTemplate: 'fields/base/detail',
/**
* Edit mode template.
*
* @type {string}
*/
editTemplate: 'fields/base/edit',
/**
* Search mode template.
*
* @type {string}
*/
searchTemplate: 'fields/base/search',
/**
* A validation list. There should be a `validate{Name}` method for each item.
*
* @type {Array.<string>}
*/
validations: ['required'],
@@ -93,68 +102,89 @@ define('views/fields/base', ['view'], function (Dep) {
/**
* A field name.
*
* @type {string}
*/
name: null,
/**
* Definitions.
*
* @type {Object}
*/
defs: null,
/**
* Field params.
*
* @type {Object}
*/
params: null,
/**
* A mode.
*
* @type {string}
*/
mode: null,
/**
* Search params.
*
* @type {Object|null}
*/
searchParams: null,
/**
* @private
*/
_timeout: null,
/**
* Inline edit disabled.
*
* @type {boolean}
*/
inlineEditDisabled: false,
/**
* Field is disabled.
*
* @type {boolean}
*/
disabled: false,
/**
* Field is read-only.
*
* @type {boolean}
*/
readOnly: false,
/**
* @type {string[]|null}
*/
attributeList: null,
/**
* Attribute values before edit.
*
* @type {Object}
*/
initialAttributes: null,
VALIDATION_POPOVER_TIMEOUT: 3000,
/**
* @type [Function]
* @internal
*/
validateCallback: null,
/**
* A record helper.
* @type {Espo.ViewRecordHelper}
* A record-view helper.
*
* @type {module:record-view-helper.Class}
*/
recordHelper: null,
@@ -713,7 +743,8 @@ define('views/fields/base', ['view'], function (Dep) {
}
tooltipText = tooltipText || this.translate(this.name, 'tooltips', this.model.name) || '';
tooltipText = this.getHelper().transfromMarkdownText(tooltipText, {linksInNewTab: true}).toString();
tooltipText = this.getHelper()
.transformMarkdownText(tooltipText, {linksInNewTab: true}).toString();
let hidePopover = () => {
$('body').off('click.popover-' + this.cid);
+2 -2
View File
@@ -26,7 +26,8 @@
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
define('views/list', ['views/main', 'search-manager'], function (Dep, SearchManager) {
define('views/list', ['views/main', 'search-manager'],
function (Dep, /** typeof module:search-manager.Class */SearchManager) {
/**
* A list view page.
@@ -409,7 +410,6 @@ define('views/list', ['views/main', 'search-manager'], function (Dep, SearchMana
);
searchManager.scope = this.scope;
searchManager.loadStored();
collection.where = searchManager.getWhere();
+2 -2
View File
@@ -482,10 +482,10 @@ define('views/stream/panel', ['views/record/panels/relationship', 'lib!Textcompl
var $a = this.$el.find('.buttons-panel a.stream-post-info');
var message = this.getHelper().transfromMarkdownInlineText(
var message = this.getHelper().transformMarkdownInlineText(
this.translate('infoMention', 'messages', 'Stream')
) + '<br><br>' +
this.getHelper().transfromMarkdownInlineText(
this.getHelper().transformMarkdownInlineText(
this.translate('infoSyntax', 'messages', 'Stream') + ':'
) + '<br>';
+1 -1
View File
@@ -270,7 +270,7 @@ define('views/user/record/edit', ['views/record/edit', 'views/user/record/detail
var msg = this.translate('setupSmtpBefore', 'messages', 'User').replace('{url}', '#Admin/outboundEmails');
msg = this.getHelper().transfromMarkdownInlineText(msg);
msg = this.getHelper().transformMarkdownInlineText(msg);
return msg;
},