Since last few weeks I'm working on EspoCRM - open source business software.
I'll edit the post with explanation. just a quick backup. Ref. here: ORM, How to manage entities and perform queries - EspoCRM Documentation
Here are some steps to get started:
Here's how you can incorporate your custom search.js script for the Lead entity in a "upgrade safe" way while your pull request is approved:
Define your own search.js class in the custom namespace. for example:
client/custom/src/views/record/search.js
Define a custom header list view class that will invoke the custom search instead of the core class, for example:
client/custom/src/views/header/list.js
Define a custom clientDefs metadata script to set the custom header list view as the "list view" for your target Entity ("Lead") class:
custom/Espo/Custom/Resources/metadata/clientDefs/Lead.json
Clear cache and rebuild.
Define your own search.js class in the custom namespace. for example:
client/custom/src/views/record/search.js
Code:
define('custom:views/record/search', 'views/record/search', function (Dep) { return Dep.extend({ WRITE HERE ALL YOUR CUSTOM SEARCH CODE }); });
client/custom/src/views/header/list.js
Code:
define('custom:views/header/list', 'views/list', function (Dep) { return Dep.extend({ searchView: 'custom:views/record/search' )}; });
custom/Espo/Custom/Resources/metadata/clientDefs/Lead.json
Code:
{ "views": { "list": "custom:views/header/list" } }
Clear cache and rebuild.
===:errors:===
VM232:2755 crbug/1173575, non-JS module files deprecated.
Failed to load resource: the server responded with a status of 404 ()
Failed to launch 'chrome-error://chromewebdata/' because the scheme does not have a registered handler.
VirtualProtectEx, ERROR_INVALID_PARAMETER (error 87)
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview This file defines a singleton which provides access to all data
* that is available as soon as the page's resources are loaded (before DOM
* content has finished loading). This data includes both localized strings and
* any data that is important to have ready from a very early stage (e.g. things
* that must be displayed right away).
*
* Note that loadTimeData is not guaranteed to be consistent between page
* refreshes (https://crbug.com/740629) and should not contain values that might
* change if the page is re-opened later.
*/
// #import {assert} from './assert.m.js';
// #import {parseHtmlSubset} from './parse_html_subset.m.js';
/**
* @typedef {{
* substitutions: (!Array<string>|undefined),
* attrs: (!Array<string>|undefined),
* tags: (!Array<string>|undefined),
* }}
*/
/* #export */ let SanitizeInnerHtmlOpts;
// eslint-disable-next-line no-var
/* #export */ /** @type {!LoadTimeData} */ var loadTimeData;
class LoadTimeData {
constructor() {
/** @type {Object} */
this.data_;
}
/**
* Sets the backing object.
*
* Note that there is no getter for |data_| to discourage abuse of the form:
*
* var value = loadTimeData.data()['key'];
*
* @param {Object} value The de-serialized page data.
*/
set data(value) {
expect(!this.data_, 'Re-setting data.');
this.data_ = value;
}
/**
* Returns a JsEvalContext for |data_|.
* @returns {JsEvalContext}
*/
createJsEvalContext() {
return new JsEvalContext(this.data_);
}
/**
* @param {string} id An ID of a value that might exist.
* @return {boolean} True if |id| is a key in the dictionary.
*/
valueExists(id) {
return id in this.data_;
}
/**
* Fetches a value, expecting that it exists.
* @param {string} id The key that identifies the desired value.
* @return {*} The corresponding value.
*/
getValue(id) {
expect(this.data_, 'No data. Did you remember to include strings.js?');
const value = this.data_[id];
expect(typeof value !== 'undefined', 'Could not find value for ' + id);
return value;
}
/**
* As above, but also makes sure that the value is a string.
* @param {string} id The key that identifies the desired string.
* @return {string} The corresponding string value.
*/
getString(id) {
const value = this.getValue(id);
expectIsType(id, value, 'string');
return /** @type {string} */ (value);
}
/**
* Returns a formatted localized string where $1 to $9 are replaced by the
* second to the tenth argument.
* @param {string} id The ID of the string we want.
* @param {...(string|number)} var_args The extra values to include in the
* formatted output.
* @return {string} The formatted string.
*/
getStringF(id, var_args) {
const value = this.getString(id);
if (!value) {
return '';
}
const args = Array.prototype.slice.call(arguments);
args[0] = value;
return this.substituteString.apply(this, args);
}
/**
* Make a string safe for use with with Polymer bindings that are
* inner-h-t-m-l (or other innerHTML use).
* @param {string} rawString The unsanitized string.
* @param {SanitizeInnerHtmlOpts=} opts Optional additional allowed tags and
* attributes.
* @return {string}
*/
sanitizeInnerHtml(rawString, opts) {
opts = opts || {};
return parseHtmlSubset('<b>' + rawString + '</b>', opts.tags, opts.attrs)
.firstChild.innerHTML;
}
/**
* Returns a formatted localized string where $1 to $9 are replaced by the
* second to the tenth argument. Any standalone $ signs must be escaped as
* $$.
* @param {string} label The label to substitute through.
* This is not an resource ID.
* @param {...(string|number)} var_args The extra values to include in the
* formatted output.
* @return {string} The formatted string.
*/
substituteString(label, var_args) {
const varArgs = arguments;
return label.replace(/\$(.|$|\n)/g, function(m) {
assert(m.match(/\$[$1-9]/), 'Unescaped $ found in localized string.');
return m === '$$' ? '$' : varArgs[m[1]];
});
}
/**
* Returns a formatted string where $1 to $9 are replaced by the second to
* tenth argument, split apart into a list of pieces describing how the
* substitution was performed. Any standalone $ signs must be escaped as $$.
* @param {string} label A localized string to substitute through.
* This is not an resource ID.
* @param {...(string|number)} var_args The extra values to include in the
* formatted output.
* @return {!Array<!{value: string, arg: (null|string)}>} The formatted
* string pieces.
*/
getSubstitutedStringPieces(label, var_args) {
const varArgs = arguments;
// Split the string by separately matching all occurrences of $1-9 and of
// non $1-9 pieces.
const pieces = (label.match(/(\$[1-9])|(([^$]|\$([^1-9]|$))+)/g) ||
[]).map(function(p) {
// Pieces that are not $1-9 should be returned after replacing $$
// with $.
if (!p.match(/^\$[1-9]$/)) {
assert(
(p.match(/\$/g) || []).length % 2 === 0,
'Unescaped $ found in localized string.');
return {value: p.replace(/\$\$/g, '$'), arg: null};
}
// Otherwise, return the substitution value.
return {value: varArgs[p[1]], arg: p};
});
return pieces;
}
/**
* As above, but also makes sure that the value is a boolean.
* @param {string} id The key that identifies the desired boolean.
* @return {boolean} The corresponding boolean value.
*/
getBoolean(id) {
const value = this.getValue(id);
expectIsType(id, value, 'boolean');
return /** @type {boolean} */ (value);
}
/**
* As above, but also makes sure that the value is an integer.
* @param {string} id The key that identifies the desired number.
* @return {number} The corresponding number value.
*/
getInteger(id) {
const value = this.getValue(id);
expectIsType(id, value, 'number');
expect(value === Math.floor(value), 'Number isn\'t integer: ' + value);
return /** @type {number} */ (value);
}
/**
* Override values in loadTimeData with the values found in |replacements|.
* @param {Object} replacements The dictionary object of keys to replace.
*/
overrideValues(replacements) {
expect(
typeof replacements === 'object',
'Replacements must be a dictionary object.');
for (const key in replacements) {
this.data_[key] = replacements[key];
}
}
}
/**
* Checks condition, displays error message if expectation fails.
* @param {*} condition The condition to check for truthiness.
* @param {string} message The message to display if the check fails.
*/
function expect(condition, message) {
if (!condition) {
console.error(
'Unexpected condition on ' + document.location.href + ': ' + message);
}
}
/**
* Checks that the given value has the given type.
* @param {string} id The id of the value (only used for error message).
* @param {*} value The value to check the type on.
* @param {string} type The type we expect |value| to be.
*/
function expectIsType(id, value, type) {
expect(
typeof value === type, '[' + value + '] (' + id + ') is not a ' + type);
}
expect(!loadTimeData, 'should only include this file once');
loadTimeData = new LoadTimeData;
// Expose |loadTimeData| directly on |window|. This is only necessary by the
// auto-generated load_time_data.m.js, since within a JS module the scope is
// local.
window.loadTimeData = loadTimeData;
/* #ignore */ console.warn('crbug/1173575, non-JS module files deprecated.');
Comments
Post a Comment