ooor 1.3.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ # OOOR: Open Object On Rails
2
+ # Copyright (C) 2009-2010 Akretion LTDA (<http://www.akretion.com>).
3
+ # Author: Raphaël Valyi
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'base64'
19
+ module Base64
20
+ def serialize_binary_from_file(binary_path)
21
+ return Base64.encode64(File.read(binary_path))
22
+ end
23
+
24
+ def serialize_binary_from_content(content)
25
+ return Base64.encode64(content)
26
+ end
27
+
28
+ def unserialize_binary(content)
29
+ return Base64.decode64(content)
30
+ end
31
+ end
@@ -1,3 +1,20 @@
1
+ # OOOR: Open Object On Rails
2
+ # Copyright (C) 2009-2010 Akretion LTDA (<http://www.akretion.com>).
3
+ # Author: Raphaël Valyi
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
1
18
  #proxies all 'common' class of OpenERP server/bin/service/web_service.py properly
2
19
  module CommonService
3
20
  def global_login(user, password)
@@ -1,9 +1,27 @@
1
+ # OOOR: Open Object On Rails
2
+ # Copyright (C) 2009-2010 Akretion LTDA (<http://www.akretion.com>).
3
+ # Author: Raphaël Valyi
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
1
18
  #proxies all 'db' class of OpenERP server/bin/service/web_service.py properly
2
19
  module DbService
3
- def create(password=@config[:db_password], db_name='ooor_db', demo=true, lang='en-US', user_password=@config[:password] || 'admin')
20
+ def create(password=@config[:db_password], db_name='ooor_db', demo=true, lang='en_US', user_password=@config[:password] || 'admin')
4
21
  process_id = OpenObjectResource.try_with_pretty_error_log { OpenObjectResource.client(@base_url + "/db").call("create", password, db_name, demo, lang, user_password) }
5
22
  @config[:database] = db_name
6
- @config[:username] = user_password
23
+ @config[:username] = 'admin'
24
+ @config[:passowrd] = user_password
7
25
  while get_progress('admin', process_id) == [0, []]
8
26
  @logger.info "..."
9
27
  sleep(0.5)
@@ -1,11 +1,25 @@
1
+ # OOOR: Open Object On Rails
2
+ # Copyright (C) 2009-2010 Akretion LTDA (<http://www.akretion.com>).
3
+ # Author: Raphaël Valyi
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
1
18
  require 'xmlrpc/client'
2
19
  require 'rubygems'
3
20
  require 'active_resource'
4
21
  require 'app/ui/form_model'
5
22
  require 'app/models/uml'
6
- require 'set'
7
-
8
- #TODO implement passing session credentials to RPC methods (concurrent access of different user credentials in Rails)
9
23
 
10
24
  class OpenObjectResource < ActiveResource::Base
11
25
  include UML
@@ -25,27 +39,31 @@ class OpenObjectResource < ActiveResource::Base
25
39
  #similar to Object#const_get but for OpenERP model key
26
40
  def const_get(model_key)
27
41
  klass_name = class_name_from_model_key(model_key)
28
- klass = (self.scope_prefix ? Object.const_get(self.scope_prefix) : Object).const_defined?(klass_name) ? (self.scope_prefix ? Object.const_get(self.scope_prefix) : Object).const_get(klass_name) : @ooor.define_openerp_model(model_key, self.scope_prefix)
42
+ klass = (self.scope_prefix ? Object.const_get(self.scope_prefix) : Object).const_defined?(klass_name) ? (self.scope_prefix ? Object.const_get(self.scope_prefix) : Object).const_get(klass_name) : @ooor.define_openerp_model({'model' => model_key}, self.scope_prefix)
29
43
  klass.reload_fields_definition unless klass.fields_defined
30
44
  klass
31
45
  end
32
46
 
47
+ def create(attributes = {}, context={}, default_get_list=false, reload=true)
48
+ self.new(attributes, default_get_list, context).tap { |resource| resource.save(context, reload) }
49
+ end
50
+
33
51
  def reload_fields_definition(force = false)
34
- if not (self.to_s.match('IrModel') || self.to_s.match('IrModelFields')) and (force or not @fields_defined)#TODO have a way to force reloading @field_ids too eventually
35
- fields = (self.scope_prefix ? Object.const_get(self.scope_prefix) : Object).const_get('IrModelFields').find(@field_ids)
52
+ if force or not @fields_defined
53
+ @fields_defined = true
36
54
  @fields = {}
37
- fields.each do |field|
38
- case field.attributes['ttype']
55
+ rpc_execute("fields_get").each do |k, field|
56
+ case field['type']
39
57
  when 'many2one'
40
- @many2one_relations[field.attributes['name']] = field
58
+ @many2one_relations[k] = field
41
59
  when 'one2many'
42
- @one2many_relations[field.attributes['name']] = field
60
+ @one2many_relations[k] = field
43
61
  when 'many2many'
44
- @many2many_relations[field.attributes['name']] = field
62
+ @many2many_relations[k] = field
45
63
  when 'reference'
46
- @polymorphic_m2o_relations[field.attributes['name']] = field
64
+ @polymorphic_m2o_relations[k] = field
47
65
  else
48
- @fields[field.attributes['name']] = field
66
+ @fields[k] = field
49
67
  end
50
68
  end
51
69
  @relations_keys = @many2one_relations.keys + @one2many_relations.keys + @many2many_relations.keys + @polymorphic_m2o_relations.keys
@@ -60,7 +78,6 @@ class OpenObjectResource < ActiveResource::Base
60
78
  end
61
79
  logger.info "#{fields.size} fields loaded in model #{self.class}"
62
80
  end
63
- @fields_defined = true
64
81
  end
65
82
 
66
83
  # ******************** remote communication ********************
@@ -72,7 +89,7 @@ class OpenObjectResource < ActiveResource::Base
72
89
 
73
90
  def client(url)
74
91
  @clients ||= {}
75
- @clients[url] ||= XMLRPC::Client.new2(url, nil, 180)
92
+ @clients[url] ||= XMLRPC::Client.new2(url, nil, 900)
76
93
  end
77
94
 
78
95
  #corresponding method for OpenERP osv.execute(self, db, uid, obj, method, *args, **kw) method
@@ -163,7 +180,7 @@ class OpenObjectResource < ActiveResource::Base
163
180
  def cast_map_to_ruby!(map)
164
181
  map.each do |k, v|
165
182
  if self.fields[k] && v.is_a?(String) && !v.empty?
166
- case self.fields[k].ttype
183
+ case self.fields[k]['type']
167
184
  when 'datetime'
168
185
  map[k] = Time.parse(v)
169
186
  when 'date'
@@ -231,7 +248,7 @@ class OpenObjectResource < ActiveResource::Base
231
248
  record.each_pair do |k,v|
232
249
  r[k.to_sym] = v
233
250
  end
234
- active_resources << instantiate_record(r, prefix_options)
251
+ active_resources << instantiate_record(r, prefix_options, context)
235
252
  end
236
253
  unless is_collection
237
254
  return active_resources[0]
@@ -240,8 +257,8 @@ class OpenObjectResource < ActiveResource::Base
240
257
  end
241
258
 
242
259
  #overriden because loading default fields is all the rage but we don't want them when reading a record
243
- def instantiate_record(record, prefix_options = {})
244
- new(record, [], {}).tap do |resource|
260
+ def instantiate_record(record, prefix_options = {}, context = {})
261
+ new(record, [], context).tap do |resource|
245
262
  resource.prefix_options = prefix_options
246
263
  end
247
264
  end
@@ -251,7 +268,21 @@ class OpenObjectResource < ActiveResource::Base
251
268
 
252
269
  # ******************** instance methods ********************
253
270
 
254
- attr_accessor :relations, :loaded_relations, :ir_model_data_id
271
+ attr_accessor :relations, :loaded_relations, :ir_model_data_id, :object_session
272
+
273
+ def object_db; object_session[:database] || self.class.database || self.class.ooor.config[:database]; end
274
+ def object_uid;object_session[:user_id] || self.class.user_id || self.class.ooor.config[:user_id]; end
275
+ def object_pass; object_session[:password] || self.class.password || self.class.ooor.config[:password]; end
276
+
277
+ #try to wrap the object context inside the query.
278
+ def rpc_execute(method, *args)
279
+ if args[-1].is_a? Hash
280
+ args[-1] = self.class.ooor.global_context.merge(object_session[:context]).merge(args[-1])
281
+ elsif args.is_a?(Array)
282
+ args += [self.class.ooor.global_context.merge(object_session[:context])]
283
+ end
284
+ self.class.rpc_execute_with_all(object_db, object_uid, object_pass, self.class.openerp_model, method, *args)
285
+ end
255
286
 
256
287
  def cast_relations_to_openerp!
257
288
  @relations.reject! do |k, v| #reject non asigned many2one or empty list
@@ -282,7 +313,7 @@ class OpenObjectResource < ActiveResource::Base
282
313
  @relations[k] = new_rel
283
314
  else
284
315
  self.class.many2one_relations.each do |k2, field| #try to cast the relation to an inherited o2m or m2m:
285
- linked_class = self.class.const_get(field.relation)
316
+ linked_class = self.class.const_get(field['relation'])
286
317
  new_rel = self.cast_relation(k, v, linked_class.one2many_relations, linked_class.many2many_relations)
287
318
  @relations[k] = new_rel and break if new_rel
288
319
  end
@@ -323,13 +354,12 @@ class OpenObjectResource < ActiveResource::Base
323
354
  end
324
355
 
325
356
  def display_available_fields
326
- msg = "\n*** DIRECTLY AVAILABLE FIELDS ON OBJECT #{self} ARE: ***"
327
- msg << "\n\n" << self.class.fields.sort {|a,b| a[1].ttype<=>b[1].ttype}.map {|i| "#{i[1].ttype} --- #{i[0]}"}.join("\n")
328
- msg << "\n\n" << self.class.many2one_relations.map {|k, v| "many2one --- #{v.relation} --- #{k}"}.join("\n")
329
- msg << "\n\n" << self.class.one2many_relations.map {|k, v| "one2many --- #{v.relation} --- #{k}"}.join("\n")
330
- msg << "\n\n" << self.class.many2many_relations.map {|k, v| "many2many --- #{v.relation} --- #{k}"}.join("\n")
331
- msg << "\n\n" << self.class.polymorphic_m2o_relations.map {|k, v| "polymorphic_m2o --- #{v.relation} --- #{k}"}.join("\n")
332
- msg << "\n\nYOU CAN ALSO USE THE INHERITED FIELDS FROM THE INHERITANCE MANY2ONE RELATIONS OR THE OBJECT METHODS...\n\n"
357
+ msg = "\n*** AVAILABLE FIELDS ON OBJECT #{self} ARE: ***"
358
+ msg << "\n\n" << self.class.fields.sort {|a,b| a[1]['type']<=>b[1]['type']}.map {|i| "#{i[1]['type']} --- #{i[0]}"}.join("\n")
359
+ msg << "\n\n" << self.class.many2one_relations.map {|k, v| "many2one --- #{v['relation']} --- #{k}"}.join("\n")
360
+ msg << "\n\n" << self.class.one2many_relations.map {|k, v| "one2many --- #{v['relation']} --- #{k}"}.join("\n")
361
+ msg << "\n\n" << self.class.many2many_relations.map {|k, v| "many2many --- #{v['relation']} --- #{k}"}.join("\n")
362
+ msg << "\n\n" << self.class.polymorphic_m2o_relations.map {|k, v| "polymorphic_m2o --- #{v['relation']} --- #{k}"}.join("\n")
333
363
  self.class.logger.debug msg
334
364
  end
335
365
 
@@ -339,49 +369,55 @@ class OpenObjectResource < ActiveResource::Base
339
369
  end
340
370
 
341
371
  #takes care of reading OpenERP default field values.
342
- #FIXME: until OpenObject explicits inheritances, we load all default values of all related fields, unless specified in default_get_list
343
372
  def initialize(attributes = {}, default_get_list=false, context={})
344
- @attributes = {}
373
+ @attributes = {}
345
374
  @prefix_options = {}
346
375
  @ir_model_data_id = attributes.delete(:ir_model_data_id)
347
- if ['ir.model', 'ir.model.fields'].index(self.class.openerp_model) || default_get_list == []
376
+ @object_session = {}
377
+ @object_session[:user_id] = context.delete :user_id
378
+ @object_session[:database] = context.delete :database
379
+ @object_session[:password] = context.delete :password
380
+ @object_session[:context] = context
381
+ if default_get_list == []
348
382
  load(attributes)
349
383
  else
350
- self.class.reload_fields_definition() unless self.class.fields_defined
351
- default_get_list ||= Set.new(self.class.many2one_relations.reject {|k, v| attributes.keys.index(k.to_sym)}.collect {|k, field| self.class.const_get(field.relation).fields.keys}.flatten + self.class.fields.keys).to_a
352
- load(self.class.rpc_execute("default_get", default_get_list, context).symbolize_keys!.merge(attributes.symbolize_keys!))
384
+ load(rpc_execute("default_get", rpc_execute("fields_get").keys, @object_session[:context]).symbolize_keys!.merge(attributes.symbolize_keys!))
353
385
  end
354
386
  end
355
387
 
388
+ def save(context={}, reload=true)
389
+ new? ? create(context, reload) : update(context, reload)
390
+ end
391
+
356
392
  #compatible with the Rails way but also supports OpenERP context
357
393
  def create(context={}, reload=true)
358
- self.id = self.class.rpc_execute('create', to_openerp_hash!, context)
394
+ self.id = rpc_execute('create', to_openerp_hash!, context)
359
395
  IrModelData.create(:model => self.class.openerp_model, :module => @ir_model_data_id[0], :name=> @ir_model_data_id[1], :res_id => self.id) if @ir_model_data_id
360
396
  reload_from_record!(self.class.find(self.id, :context => context)) if reload
361
397
  end
362
398
 
363
399
  #compatible with the Rails way but also supports OpenERP context
364
400
  def update(context={}, reload=true)
365
- self.class.rpc_execute('write', self.id, to_openerp_hash!, context)
401
+ rpc_execute('write', self.id, to_openerp_hash!, context)
366
402
  reload_from_record!(self.class.find(self.id, :context => context)) if reload
367
403
  end
368
404
 
369
405
  #compatible with the Rails way but also supports OpenERP context
370
406
  def destroy(context={})
371
- self.class.rpc_execute('unlink', [self.id], context)
407
+ rpc_execute('unlink', [self.id], context)
372
408
  end
373
409
 
374
410
  #OpenERP copy method, load persisted copied Object
375
411
  def copy(defaults={}, context={})
376
- self.class.find(self.class.rpc_execute('copy', self.id, defaults, context), :context => context)
412
+ self.class.find(rpc_execute('copy', self.id, defaults, context), :context => context)
377
413
  end
378
414
 
379
415
  #Generic OpenERP rpc method call
380
- def call(method, *args) self.class.rpc_execute(method, *args) end
416
+ def call(method, *args) rpc_execute(method, *args) end
381
417
 
382
418
  #Generic OpenERP on_change method
383
419
  def on_change(on_change_method, field_name, field_value, *args)
384
- result = self.class.rpc_execute(on_change_method, self.id && [id] || [], *args)
420
+ result = self.class.rpc_execute_with_all(object_db, object_uid, object_pass, self.class.openerp_model, on_change_method, self.id && [id] || [], *args) #OpenERP doesn't accept context systematically in on_change events unfortunately
385
421
  if result["warning"]
386
422
  self.class.logger.info result["warning"]["title"]
387
423
  self.class.logger.info result["warning"]["message"]
@@ -390,9 +426,9 @@ class OpenObjectResource < ActiveResource::Base
390
426
  end
391
427
 
392
428
  #wrapper for OpenERP exec_workflow Business Process Management engine
393
- def wkf_action(action, context={})
394
- self.class.rpc_exec_workflow(action, self.id) #FIXME looks like OpenERP exec_workflow doesn't accept context but it might be a bug
395
- reload_from_record!(self.class.find(self.id, :context => context))
429
+ def wkf_action(action, context={}, reload=true)
430
+ self.class.rpc_exec_workflow_with_all(object_db, object_uid, object_pass, self.class.openerp_model, action, self.id) #FIXME looks like OpenERP exec_workflow doesn't accept context but it might be a bug
431
+ reload_from_record!(self.class.find(self.id, :context => context)) if reload
396
432
  end
397
433
 
398
434
  def old_wizard_step(wizard_name, step='init', wizard_id=nil, form={}, context={})
@@ -408,11 +444,11 @@ class OpenObjectResource < ActiveResource::Base
408
444
  def relationnal_result(method_name, *arguments)
409
445
  self.class.reload_fields_definition unless self.class.fields_defined
410
446
  if self.class.many2one_relations.has_key?(method_name)
411
- load_relation(self.class.many2one_relations[method_name].relation, @relations[method_name][0], *arguments)
447
+ load_relation(self.class.many2one_relations[method_name]['relation'], @relations[method_name][0], *arguments)
412
448
  elsif self.class.one2many_relations.has_key?(method_name)
413
- load_relation(self.class.one2many_relations[method_name].relation, @relations[method_name], *arguments)
449
+ load_relation(self.class.one2many_relations[method_name]['relation'], @relations[method_name], *arguments)
414
450
  elsif self.class.many2many_relations.has_key?(method_name)
415
- load_relation(self.class.many2many_relations[method_name].relation, @relations[method_name], *arguments)
451
+ load_relation(self.class.many2many_relations[method_name]['relation'], @relations[method_name], *arguments)
416
452
  elsif self.class.polymorphic_m2o_relations.has_key?(method_name)
417
453
  values = @relations[method_name].split(',')
418
454
  load_relation(values[0], values[1].to_i, *arguments)
@@ -426,18 +462,18 @@ class OpenObjectResource < ActiveResource::Base
426
462
  is_assign = method_name.end_with?('=')
427
463
  method_key = method_name.sub('=', '')
428
464
  return super if attributes.has_key?(method_key)
429
- return self.class.rpc_execute(method_name, *arguments) unless arguments.empty? || is_assign
465
+ return rpc_execute(method_name, *arguments) unless arguments.empty? || is_assign
430
466
 
431
467
  self.class.reload_fields_definition() unless self.class.fields_defined
432
468
 
433
469
  if is_assign
434
- known_relations = self.class.relations_keys + self.class.many2one_relations.collect {|k, field| self.class.const_get(field.relation).relations_keys}.flatten
470
+ known_relations = self.class.relations_keys + self.class.many2one_relations.collect {|k, field| self.class.const_get(field['relation']).relations_keys}.flatten
435
471
  if known_relations.index(method_key)
436
472
  @relations[method_key] = arguments[0]
437
473
  @loaded_relations[method_key] = arguments[0]
438
474
  return
439
475
  end
440
- know_fields = self.class.fields.keys + self.class.many2one_relations.collect {|k, field| self.class.const_get(field.relation).fields.keys}.flatten
476
+ know_fields = self.class.fields.keys + self.class.many2one_relations.collect {|k, field| self.class.const_get(field['relation']).fields.keys}.flatten
441
477
  @attributes[method_key] = arguments[0] and return if know_fields.index(method_key)
442
478
  end
443
479
 
@@ -450,27 +486,9 @@ class OpenObjectResource < ActiveResource::Base
450
486
  result = relationnal_result(method_name, *arguments)
451
487
  @loaded_relations[method_name] = result and return result if result
452
488
 
453
- #maybe the relation is inherited or could be inferred from a related field
454
- self.class.many2one_relations.each do |k, field| #TODO could be recursive eventually
455
- if @relations[k] #we only care if instance has a relation
456
- related_model = self.class.const_get(field.relation)
457
- related_model.reload_fields_definition() unless related_model.fields_defined
458
- if related_model.relations_keys.index(method_key)
459
- @loaded_relations[k] ||= load_relation(field.relation, @relations[k][0], *arguments)
460
- model = @loaded_relations[k]
461
- model.loaded_relations[method_key] ||= model.relationnal_result(method_key, *arguments)
462
- return model.loaded_relations[method_key] if model.loaded_relations[method_key]
463
- end
464
- elsif is_assign
465
- klazz = self.class.const_get(field.relation)
466
- @relations[method_key] = arguments[0] and return if klazz.relations_keys.index(method_key)
467
- @attributes[method_key] = arguments[0] and return if klazz.fields.keys.index(method_key)
468
- end
469
- end
470
-
471
489
  if id
472
490
  arguments += [{}] unless arguments.last.is_a?(Hash)
473
- self.class.rpc_execute(method_key, [id], *arguments) #we assume that's an action
491
+ rpc_execute(method_key, [id], *arguments) #we assume that's an action
474
492
  else
475
493
  super
476
494
  end
@@ -1,3 +1,20 @@
1
+ # OOOR: Open Object On Rails
2
+ # Copyright (C) 2009-2010 Akretion LTDA (<http://www.akretion.com>).
3
+ # Author: Raphaël Valyi
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
1
18
  module UML
2
19
  #usage: UML.print_uml or with options: UML.print_uml(:all, : detailed) or MyOpenObjectResource.print_uml or UML.print_uml([list_of_classes], :all, :detailed)
3
20
 
@@ -17,7 +34,7 @@ module UML
17
34
  def self.display_fields(clazz)
18
35
  s = ""
19
36
  clazz.reload_fields_definition if clazz.fields.empty?
20
- clazz.fields.sort {|a,b| a[1].ttype <=> b[1].ttype}.each {|i| s << "+ #{i[1].ttype} : #{i[0]}\\l\\n"}
37
+ clazz.fields.sort {|a,b| a[1]['type'] <=> b[1]['type']}.each {|i| s << "+ #{i[1]['type']} : #{i[0]}\\l\\n"}
21
38
  s
22
39
  end
23
40
 
@@ -170,8 +187,8 @@ module UML
170
187
  private
171
188
 
172
189
  def self.get_target(is_reverse, local, enabled_targets, field, model)
173
- if (is_reverse && !local) || (!enabled_targets) || enabled_targets.index(field.relation)
174
- model.const_get(field.relation)
190
+ if (is_reverse && !local) || (!enabled_targets) || enabled_targets.index(field['relation'])
191
+ model.const_get(field['relation'])
175
192
  else
176
193
  false
177
194
  end
@@ -1,3 +1,20 @@
1
+ # OOOR: Open Object On Rails
2
+ # Copyright (C) 2009-2010 Akretion LTDA (<http://www.akretion.com>).
3
+ # Author: Raphaël Valyi
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
1
18
  module ActionWindowModule
2
19
  def open(mode='tree', ids=nil)
3
20
  if view_mode.index(mode)