You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

232 lines
7.4 KiB

  1. odoo.define('web.domain_field', function (require) {
  2. "use strict";
  3. var pyeval = require('web.pyeval');
  4. var session = require('web.session');
  5. var original_pyeval = pyeval.eval;
  6. var original_ensure_evaluated = pyeval.ensure_evaluated;
  7. var py = window.py;
  8. /** copied from pyeval and not modified but required since not publicly
  9. exposed by web.pyeval**/
  10. // recursively wraps JS objects passed into the context to attributedicts
  11. // which jsonify back to JS objects
  12. function wrap(value) {
  13. if (value === null) { return py.None; }
  14. switch (typeof value) {
  15. case 'undefined': throw new Error("No conversion for undefined");
  16. case 'boolean': return py.bool.fromJSON(value);
  17. case 'number': return py.float.fromJSON(value);
  18. case 'string': return py.str.fromJSON(value);
  19. }
  20. switch(value.constructor) {
  21. case Object: return wrapping_dict.fromJSON(value);
  22. case Array: return wrapping_list.fromJSON(value);
  23. }
  24. throw new Error("ValueError: unable to wrap " + value);
  25. }
  26. var wrapping_dict = py.type('wrapping_dict', null, {
  27. __init__: function () {
  28. this._store = {};
  29. },
  30. __getitem__: function (key) {
  31. var k = key.toJSON();
  32. if (!(k in this._store)) {
  33. throw new Error("KeyError: '" + k + "'");
  34. }
  35. return wrap(this._store[k]);
  36. },
  37. __getattr__: function (key) {
  38. return this.__getitem__(py.str.fromJSON(key));
  39. },
  40. __len__: function () {
  41. return Object.keys(this._store).length
  42. },
  43. __nonzero__: function () {
  44. return py.PY_size(this) > 0 ? py.True : py.False;
  45. },
  46. get: function () {
  47. var args = py.PY_parseArgs(arguments, ['k', ['d', py.None]]);
  48. if (!(args.k.toJSON() in this._store)) { return args.d; }
  49. return this.__getitem__(args.k);
  50. },
  51. fromJSON: function (d) {
  52. var instance = py.PY_call(wrapping_dict);
  53. instance._store = d;
  54. return instance;
  55. },
  56. toJSON: function () {
  57. return this._store;
  58. },
  59. });
  60. var wrapping_list = py.type('wrapping_list', null, {
  61. __init__: function () {
  62. this._store = [];
  63. },
  64. __getitem__: function (index) {
  65. return wrap(this._store[index.toJSON()]);
  66. },
  67. __len__: function () {
  68. return this._store.length;
  69. },
  70. __nonzero__: function () {
  71. return py.PY_size(this) > 0 ? py.True : py.False;
  72. },
  73. fromJSON: function (ar) {
  74. var instance = py.PY_call(wrapping_list);
  75. instance._store = ar;
  76. return instance;
  77. },
  78. toJSON: function () {
  79. return this._store;
  80. },
  81. });
  82. function wrap_context (context) {
  83. for (var k in context) {
  84. if (!context.hasOwnProperty(k)) { continue; }
  85. var val = context[k];
  86. if (val === null) { continue; }
  87. if (val.constructor === Array) {
  88. context[k] = wrapping_list.fromJSON(val);
  89. } else if (val.constructor === Object
  90. && !py.PY_isInstance(val, py.object)) {
  91. context[k] = wrapping_dict.fromJSON(val);
  92. }
  93. }
  94. return context;
  95. }
  96. function ensure_evaluated (args, kwargs) {
  97. for (var i=0; i<args.length; ++i) {
  98. args[i] = eval_arg(args[i]);
  99. }
  100. for (var k in kwargs) {
  101. if (!kwargs.hasOwnProperty(k)) { continue; }
  102. kwargs[k] = eval_arg(kwargs[k]);
  103. }
  104. }
  105. function eval_contexts (contexts, evaluation_context) {
  106. evaluation_context = _.extend(pyeval.context(), evaluation_context || {});
  107. return _(contexts).reduce(function (result_context, ctx) {
  108. // __eval_context evaluations can lead to some of `contexts`'s
  109. // values being null, skip them as well as empty contexts
  110. if (_.isEmpty(ctx)) { return result_context; }
  111. if (_.isString(ctx)) {
  112. // wrap raw strings in context
  113. ctx = { __ref: 'context', __debug: ctx };
  114. }
  115. var evaluated = ctx;
  116. switch(ctx.__ref) {
  117. case 'context':
  118. evaluation_context.context = evaluation_context;
  119. evaluated = py.eval(ctx.__debug, wrap_context(evaluation_context));
  120. break;
  121. case 'compound_context':
  122. var eval_context = eval_contexts([ctx.__eval_context]);
  123. evaluated = eval_contexts(
  124. ctx.__contexts, _.extend({}, evaluation_context, eval_context));
  125. break;
  126. }
  127. // add newly evaluated context to evaluation context for following
  128. // siblings
  129. _.extend(evaluation_context, evaluated);
  130. return _.extend(result_context, evaluated);
  131. }, {});
  132. }
  133. /** end of unmodified methods copied from pyeval **/
  134. // We need to override the original method to be able to call our
  135. //specialized version of pyeval for domain fields
  136. function eval_arg (arg) {
  137. if (typeof arg !== 'object' || !arg.__ref) { return arg; }
  138. switch(arg.__ref) {
  139. case 'domain': case 'compound_domain':
  140. return domain_field_pyeval('domains', [arg]);
  141. case 'context': case 'compound_context':
  142. return original_pyeval('contexts', [arg]);
  143. default:
  144. throw new Error(_t("Unknown nonliteral type ") + ' ' + arg.__ref);
  145. }
  146. }
  147. // override eval_domains to add 3 lines in order to be able to use a field
  148. //value as domain
  149. function eval_domains (domains, evaluation_context) {
  150. evaluation_context = _.extend(pyeval.context(), evaluation_context ||
  151. {});
  152. var result_domain = [];
  153. _(domains).each(function (domain) {
  154. if (_.isString(domain)) {
  155. // Modified part or the original method
  156. if(domain in evaluation_context) {
  157. result_domain.push.apply(
  158. result_domain, $.parseJSON(evaluation_context[domain]));
  159. return;
  160. }
  161. // end of modifications
  162. // wrap raw strings in domain
  163. domain = { __ref: 'domain', __debug: domain };
  164. }
  165. switch(domain.__ref) {
  166. case 'domain':
  167. evaluation_context.context = evaluation_context;
  168. result_domain.push.apply(
  169. result_domain, py.eval(domain.__debug, wrap_context(evaluation_context)));
  170. break;
  171. case 'compound_domain':
  172. var eval_context = eval_contexts([domain.__eval_context]);
  173. result_domain.push.apply(
  174. result_domain, eval_domains(
  175. domain.__domains, _.extend(
  176. {}, evaluation_context, eval_context)));
  177. break;
  178. default:
  179. result_domain.push.apply(result_domain, domain);
  180. }
  181. });
  182. return result_domain;
  183. }
  184. // override pyeval in order to call our specialized implementation of
  185. // eval_domains
  186. function domain_field_pyeval (type, object, context, options) {
  187. switch(type) {
  188. case 'domain':
  189. case 'domains':
  190. if (type === 'domain')
  191. object = [object];
  192. return eval_domains(object, context);
  193. default:
  194. return original_pyeval(type, object, context, options);
  195. }
  196. }
  197. // override sync_eval in order to call our specialized implementation of
  198. // eval_domains
  199. function sync_eval_domains_and_contexts (source) {
  200. var contexts = ([session.user_context] || []).concat(source.contexts);
  201. // see Session.eval_context in Python
  202. return {
  203. context: domain_field_pyeval('contexts', contexts),
  204. domain: domain_field_pyeval('domains', source.domains),
  205. group_by: domain_field_pyeval('groupbys', source.group_by_seq || [])
  206. };
  207. }
  208. pyeval.eval = domain_field_pyeval;
  209. pyeval.ensure_evaluated = ensure_evaluated;
  210. pyeval.sync_eval_domains_and_contexts = sync_eval_domains_and_contexts;
  211. });