ext_ooor 2.3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +354 -0
- data/Rakefile +5 -0
- data/bin/ooor +43 -0
- data/lib/ext_ooor.rb +5 -0
- data/lib/ext_ooor/version.rb +5 -0
- data/lib/generators/ooor/install_generator.rb +18 -0
- data/lib/generators/ooor/ooor.yml +49 -0
- data/lib/ooor.rb +230 -0
- data/lib/ooor/associations.rb +78 -0
- data/lib/ooor/autosave_association.rb +197 -0
- data/lib/ooor/base.rb +130 -0
- data/lib/ooor/base64.rb +20 -0
- data/lib/ooor/callbacks.rb +18 -0
- data/lib/ooor/errors.rb +120 -0
- data/lib/ooor/field_methods.rb +213 -0
- data/lib/ooor/helpers/core_helpers.rb +83 -0
- data/lib/ooor/locale.rb +11 -0
- data/lib/ooor/mini_active_resource.rb +86 -0
- data/lib/ooor/model_registry.rb +24 -0
- data/lib/ooor/model_schema.rb +25 -0
- data/lib/ooor/naming.rb +92 -0
- data/lib/ooor/nested_attributes.rb +57 -0
- data/lib/ooor/persistence.rb +353 -0
- data/lib/ooor/rack.rb +137 -0
- data/lib/ooor/railtie.rb +27 -0
- data/lib/ooor/reflection.rb +151 -0
- data/lib/ooor/reflection_ooor.rb +121 -0
- data/lib/ooor/relation.rb +204 -0
- data/lib/ooor/relation/finder_methods.rb +153 -0
- data/lib/ooor/report.rb +53 -0
- data/lib/ooor/serialization.rb +49 -0
- data/lib/ooor/services.rb +134 -0
- data/lib/ooor/session.rb +250 -0
- data/lib/ooor/session_handler.rb +66 -0
- data/lib/ooor/transport.rb +34 -0
- data/lib/ooor/transport/json_client.rb +65 -0
- data/lib/ooor/transport/xml_rpc_client.rb +15 -0
- data/lib/ooor/type_casting.rb +223 -0
- data/lib/ooor/version.rb +8 -0
- data/spec/cli_spec.rb +129 -0
- data/spec/helpers/test_helper.rb +11 -0
- data/spec/ooor_spec.rb +867 -0
- metadata +118 -0
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module Ooor
|
4
|
+
module FinderMethods
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def find(*arguments)
|
9
|
+
if arguments.size == 1 &&
|
10
|
+
arguments[0].is_a?(Hash) ||
|
11
|
+
(arguments[0].is_a?(Array) && !([arguments[0][1]] & Ooor::TypeCasting::OPERATORS).empty?)
|
12
|
+
find_single(nil, {domain: arguments[0]})
|
13
|
+
else
|
14
|
+
find_dispatch(*arguments)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def find_dispatch(*arguments)
|
20
|
+
scope = arguments.slice!(0)
|
21
|
+
options = arguments.slice!(0) || {}
|
22
|
+
if (!scope.is_a?(Array) && !options.is_a?(Hash))
|
23
|
+
scope = [scope] + [options] + arguments
|
24
|
+
options = {}
|
25
|
+
end
|
26
|
+
case scope
|
27
|
+
when :all then find_single(nil, options)
|
28
|
+
when :first then find_first_or_last(options)
|
29
|
+
when :last then find_first_or_last(options, "DESC")
|
30
|
+
when :one then find_one(options)
|
31
|
+
else find_single(scope, options)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def find_first_or_last(options, ordering = "ASC")
|
36
|
+
options[:order] ||= "id #{ordering}"
|
37
|
+
options[:limit] = 1
|
38
|
+
find_single(nil, options)[0]
|
39
|
+
end
|
40
|
+
|
41
|
+
#actually finds many resources specified with scope = ids_array
|
42
|
+
def find_single(scope, options)
|
43
|
+
context = options[:context] || {}
|
44
|
+
reload_fields_definition(false)
|
45
|
+
fields = options[:fields] || options[:only] || fast_fields(options)
|
46
|
+
fields += options[:include] if options[:include]
|
47
|
+
|
48
|
+
if scope
|
49
|
+
is_collection, records = read_scope(context, fields, scope)
|
50
|
+
else
|
51
|
+
is_collection, records = read_domain(context, fields, options)
|
52
|
+
end
|
53
|
+
active_resources = []
|
54
|
+
records.each { |record| active_resources << new(record, [], true)}
|
55
|
+
if is_collection
|
56
|
+
inject_includes!(active_resources, options)
|
57
|
+
active_resources
|
58
|
+
else
|
59
|
+
active_resources[0]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def inject_includes!(active_resources, options)
|
64
|
+
(options[:includes] || []).each do |key|
|
65
|
+
if key.is_a?(Hash) # recursive includes
|
66
|
+
sub_options = key.values.first
|
67
|
+
if sub_options.is_a?(Array) # like Rails User.includes(:address, friends: [:address, :followers])
|
68
|
+
sub_options = {includes: sub_options}
|
69
|
+
end
|
70
|
+
key = key.keys.first.to_s
|
71
|
+
else
|
72
|
+
key = key.to_s
|
73
|
+
end
|
74
|
+
|
75
|
+
if many2one_associations.keys.include?(key)
|
76
|
+
filtered_ids = active_resources.map do |i|
|
77
|
+
val = i.associations[key]
|
78
|
+
val.is_a?(Array) ? val.first : val
|
79
|
+
end.select {|i| i} # filter out nil ids
|
80
|
+
elsif one2many_associations.keys.include?(key) || many2many_associations.keys.include?(key)
|
81
|
+
filtered_ids = active_resources.map { |i| i.associations[key]}.flatten
|
82
|
+
end
|
83
|
+
|
84
|
+
relation = all_fields[key]['relation']
|
85
|
+
records = const_get(relation).find(filtered_ids, sub_options)
|
86
|
+
records_hash = Hash[ *records.collect { |r| [ r.id, r ] }.flatten ]
|
87
|
+
if many2one_associations.keys.include?(key)
|
88
|
+
active_resources.each_with_index do |res|
|
89
|
+
val = res.associations[key]
|
90
|
+
rel_id = val.is_a?(Array) ? val.first : val
|
91
|
+
res.loaded_associations[key] = rel_id ? records_hash[rel_id] : nil
|
92
|
+
end
|
93
|
+
else # one2many and many2many
|
94
|
+
active_resources.each_with_index do |res|
|
95
|
+
rel_ids = res.associations[key]
|
96
|
+
res.loaded_associations[key] = rel_ids.map { |i| records_hash[i] }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def read_scope(context, fields, scope)
|
103
|
+
if scope.is_a? Array
|
104
|
+
is_collection = true
|
105
|
+
else
|
106
|
+
scope = [scope]
|
107
|
+
is_collection = false
|
108
|
+
end
|
109
|
+
scope.map! { |item| item_to_id(item, context) }.reject! {|item| !item}
|
110
|
+
records = rpc_execute('read', scope, fields, context.dup)
|
111
|
+
records.sort_by! {|r| scope.index(r["id"])} if @session.config[:force_xml_rpc]
|
112
|
+
return is_collection, records
|
113
|
+
end
|
114
|
+
|
115
|
+
def read_domain(context, fields, options)
|
116
|
+
if @session.config[:force_xml_rpc]
|
117
|
+
domain = to_openerp_domain(options[:domain] || options[:conditions] || [])
|
118
|
+
ids = rpc_execute('search', domain, options[:offset] || 0, options[:limit] || false, options[:order] || false, context.dup)
|
119
|
+
records = rpc_execute('read', ids, fields, context.dup)
|
120
|
+
else
|
121
|
+
domain = to_openerp_domain(options[:domain] || options[:conditions] || [])
|
122
|
+
response = object_service(:search_read, openerp_model, 'search_read', {
|
123
|
+
fields: fields,
|
124
|
+
offset: options[:offset] || 0,
|
125
|
+
limit: options[:limit] || false,
|
126
|
+
domain: domain,
|
127
|
+
sort: options[:order] || false,
|
128
|
+
context: context
|
129
|
+
})
|
130
|
+
records = response["records"]
|
131
|
+
end
|
132
|
+
return true, records
|
133
|
+
end
|
134
|
+
|
135
|
+
def item_to_id(item, context)
|
136
|
+
if item.is_a?(String)
|
137
|
+
if item.to_i == 0#triggers ir_model_data absolute reference lookup
|
138
|
+
tab = item.split(".")
|
139
|
+
domain = [['name', '=', tab[-1]]]
|
140
|
+
domain << ['module', '=', tab[-2]] if tab[-2]
|
141
|
+
ir_model_data = const_get('ir.model.data').find(:first, domain: domain, context: context)
|
142
|
+
ir_model_data && ir_model_data.res_id && search([['id', '=', ir_model_data.res_id]], 0, false, false, context)[0]
|
143
|
+
else
|
144
|
+
item.to_i
|
145
|
+
end
|
146
|
+
else
|
147
|
+
item
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/lib/ooor/report.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module Ooor
|
4
|
+
module Report
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
#Added methods to obtain report data for a model
|
9
|
+
def report(report_name, ids, report_type='pdf', context={}) #TODO move to ReportService
|
10
|
+
context = session.object.inject_session_context(:report, false, context)[0]
|
11
|
+
uid = @session.config[:user_id]
|
12
|
+
pass = @session.config[:password]
|
13
|
+
db = @session.config[:database]
|
14
|
+
params = {model: openerp_model, id: ids[0], report_type: report_type}
|
15
|
+
session.report.report(db, uid, pass, report_name, ids, params, context)
|
16
|
+
end
|
17
|
+
|
18
|
+
def report_get(report_id)
|
19
|
+
uid = @session.config[:user_id]
|
20
|
+
pass = @session.config[:password]
|
21
|
+
db = @session.config[:database]
|
22
|
+
session.report.report_get(db, uid, pass, report_id)
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_report_data(report_name, ids, report_type='pdf', context={})
|
26
|
+
report_id = report(report_name, ids, report_type, context)
|
27
|
+
if report_id
|
28
|
+
state = false
|
29
|
+
attempt = 0
|
30
|
+
while not state
|
31
|
+
report = self.report_get(report_id)
|
32
|
+
state = report["state"]
|
33
|
+
attempt = 1
|
34
|
+
if not state
|
35
|
+
sleep(0.1)
|
36
|
+
attempt += 1
|
37
|
+
else
|
38
|
+
return [report["result"],report["format"]]
|
39
|
+
end
|
40
|
+
if attempt > 100
|
41
|
+
logger.debug "OOOR RPC: 'Printing Aborted!'"
|
42
|
+
break
|
43
|
+
end
|
44
|
+
end
|
45
|
+
else
|
46
|
+
logger.debug "OOOR RPC: 'report not found'"
|
47
|
+
end
|
48
|
+
return nil
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# OOOR: OpenObject On Ruby
|
2
|
+
# Copyright (C) 2009-TODAY Akretion LTDA (<http://www.akretion.com>).
|
3
|
+
# Author: Raphaël Valyi
|
4
|
+
# Licensed under the MIT license, see MIT-LICENSE file
|
5
|
+
|
6
|
+
module Ooor
|
7
|
+
module Serialization
|
8
|
+
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
included do
|
12
|
+
self.include_root_in_json = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def serializable_hash(options = nil)
|
16
|
+
options ||= {}
|
17
|
+
hash = super(options)
|
18
|
+
|
19
|
+
attribute_names = attributes.keys.sort
|
20
|
+
included_associations = {}
|
21
|
+
serialize_many2one(included_associations)
|
22
|
+
serialize_x_to_many(included_associations)
|
23
|
+
|
24
|
+
method_names = Array.wrap(options[:methods]).map { |n| n if respond_to?(n.to_s) }.compact
|
25
|
+
Hash[(attribute_names + method_names).map { |n| [n, send(n)] }].merge(included_associations)
|
26
|
+
end
|
27
|
+
|
28
|
+
def serialize_many2one(included_associations)
|
29
|
+
self.class.many2one_associations.keys.each do |k|
|
30
|
+
if loaded_associations[k].is_a? Base
|
31
|
+
included_associations[k] = loaded_associations[k].as_json[loaded_associations[k].class.openerp_model.gsub('.', '_')]
|
32
|
+
elsif associations[k].is_a? Array
|
33
|
+
included_associations[k] = {"id" => associations[k][0], "name" => associations[k][1]}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def serialize_x_to_many(included_associations)
|
39
|
+
(self.class.one2many_associations.keys + self.class.many2many_associations.keys).each do |k|
|
40
|
+
if loaded_associations[k].is_a? Array
|
41
|
+
included_associations[k] = loaded_associations[k].map {|item| item.as_json[item.class.openerp_model.gsub('.', '_')]}
|
42
|
+
else
|
43
|
+
included_associations[k] = associations[k].map {|id| {"id" => id}} if associations[k]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# OOOR: OpenObject On Ruby
|
2
|
+
# Copyright (C) 2009-2013 Akretion LTDA (<http://www.akretion.com>).
|
3
|
+
# Author: Raphaël Valyi
|
4
|
+
# Licensed under the MIT license, see MIT-LICENSE file
|
5
|
+
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
|
9
|
+
module Ooor
|
10
|
+
autoload :InvalidSessionError, 'ooor/errors'
|
11
|
+
|
12
|
+
class Service
|
13
|
+
def initialize(session)
|
14
|
+
@session = session
|
15
|
+
end
|
16
|
+
|
17
|
+
# using define_method provides handy autocompletion
|
18
|
+
def self.define_service(service, methods)
|
19
|
+
methods.each do |meth|
|
20
|
+
self.instance_eval do
|
21
|
+
define_method meth do |*args|
|
22
|
+
if @session.odoo_serie > 7
|
23
|
+
json_conn = @session.get_client(:json, "#{@session.base_jsonrpc2_url}")
|
24
|
+
json_conn.oe_service(@session.web_session, service, nil, meth, *args)
|
25
|
+
else # via XMLRPC on v7:
|
26
|
+
endpoint = @session.get_client(:xml, "#{@session.base_url}/#{service.to_s.gsub('ooor_alias_', '')}")
|
27
|
+
endpoint.call(meth.gsub('ooor_alias_', ''), *args)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
class CommonService < Service
|
37
|
+
define_service(:common, %w[ir_get ir_set ir_del about logout timezone_get get_available_updates get_migration_scripts get_server_environment login_message check_connectivity about get_stats list_http_services version authenticate get_available_updates set_loglevel get_os_time get_sqlcount])
|
38
|
+
|
39
|
+
def csrf_token()
|
40
|
+
unless defined?(Nokogiri)
|
41
|
+
raise "You need to install the nokogiri gem for this feature"
|
42
|
+
end
|
43
|
+
require 'nokogiri'
|
44
|
+
@session.logger.debug "OOOR csrf_token"
|
45
|
+
conn = @session.get_client(:json, "#{@session.base_jsonrpc2_url}")
|
46
|
+
login_page = conn.get('/web/login') do |req|
|
47
|
+
req.headers['Cookie'] = "session_id=#{@session.web_session[:session_id]}"
|
48
|
+
end.body # TODO implement some caching
|
49
|
+
Nokogiri::HTML(login_page).css("input[name='csrf_token']")[0]['value']
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
# Function to validate json response with useful messages
|
54
|
+
# Eg: For Database database "<DB NAME>" does not exist errors from open erb.
|
55
|
+
def validate_response(json_response)
|
56
|
+
error = json_response["error"]
|
57
|
+
|
58
|
+
if error && (error["data"]["type"] == "server_exception" || error['message'] == "Odoo Server Error")
|
59
|
+
raise "#{error["message"]} ------- #{error["data"]["debug"]}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
class DbService < Service
|
66
|
+
define_service(:db, %w[get_progress drop dump restore rename db_exist list change_admin_password list_lang server_version migrate_databases create_database duplicate_database])
|
67
|
+
|
68
|
+
def create(password=@session.config[:db_password], db_name='ooor_test', demo=true, lang='en_US', user_password=@session.config[:password] || 'admin')
|
69
|
+
if @session.odoo_serie > 7
|
70
|
+
json_conn = @session.get_client(:json, "#{@session.base_jsonrpc2_url}")
|
71
|
+
x = json_conn.oe_service(@session.web_session, :db, nil, 'create_database', password, db_name, demo, lang, user_password)
|
72
|
+
else # via XMLRPC on v7:
|
73
|
+
@session.logger.info "creating database #{db_name} this may take a while..."
|
74
|
+
process_id = @session.get_client(:xml, @session.base_url + "/db").call("create_database", password, db_name, demo, lang, user_password)
|
75
|
+
sleep(2)
|
76
|
+
while process_id.is_a?(Integer) && get_progress(password, process_id)[0] != 1
|
77
|
+
@session.logger.info "..."
|
78
|
+
sleep(0.5)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
@session.global_login(username: 'admin', password: user_password, database: db_name)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
class ObjectService < Service
|
87
|
+
define_service(:object, %w[execute exec_workflow])
|
88
|
+
|
89
|
+
def object_service(service, obj, method, *args)
|
90
|
+
@session.login_if_required()
|
91
|
+
args = inject_session_context(service, method, *args)
|
92
|
+
uid = @session.config[:user_id]
|
93
|
+
db = @session.config[:database]
|
94
|
+
@session.logger.debug "OOOR object service: rpc_method: #{service}, db: #{db}, uid: #{uid}, pass: #, obj: #{obj}, method: #{method}, *args: #{args.inspect}"
|
95
|
+
if @session.config[:force_xml_rpc]
|
96
|
+
pass = @session.config[:password]
|
97
|
+
send(service, db, uid, pass, obj, method, *args)
|
98
|
+
else
|
99
|
+
json_conn = @session.get_client(:json, "#{@session.base_jsonrpc2_url}")
|
100
|
+
json_conn.oe_service(@session.web_session, service, obj, method, *args)
|
101
|
+
end
|
102
|
+
rescue InvalidSessionError
|
103
|
+
@session.config[:force_xml_rpc] = true #TODO set v6 version too
|
104
|
+
retry
|
105
|
+
rescue SessionExpiredError
|
106
|
+
@session.logger.debug "session for uid: #{uid} has expired, trying to login again"
|
107
|
+
@session.login(@session.config[:database], @session.config[:username], @session.config[:password])
|
108
|
+
retry # TODO put a retry limit to avoid infinite login attempts
|
109
|
+
end
|
110
|
+
|
111
|
+
def inject_session_context(service, method, *args)
|
112
|
+
if service == :object && (i = Ooor.irregular_context_position(method)) && args.size >= i
|
113
|
+
c = HashWithIndifferentAccess.new(args[i])
|
114
|
+
args[i] = @session.session_context(c)
|
115
|
+
elsif args[-1].is_a? Hash #context
|
116
|
+
if args[-1][:context]
|
117
|
+
c = HashWithIndifferentAccess.new(args[-1][:context])
|
118
|
+
args[-1][:context] = @session.session_context(c)
|
119
|
+
else
|
120
|
+
c = HashWithIndifferentAccess.new(args[-1])
|
121
|
+
args[-1] = @session.session_context(c)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
args
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
class ReportService < Service
|
131
|
+
define_service(:report, %w[report report_get render_report]) #TODO make use json rpc transport too
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
data/lib/ooor/session.rb
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
require 'ooor/services'
|
2
|
+
require 'active_support/configurable'
|
3
|
+
require 'active_support/core_ext/hash/slice'
|
4
|
+
|
5
|
+
|
6
|
+
module Ooor
|
7
|
+
class Session
|
8
|
+
include ActiveSupport::Configurable
|
9
|
+
include Transport
|
10
|
+
|
11
|
+
attr_accessor :web_session, :id, :models
|
12
|
+
|
13
|
+
def common(); @common_service ||= CommonService.new(self); end
|
14
|
+
def db(); @db_service ||= DbService.new(self); end
|
15
|
+
def object(); @object_service ||= ObjectService.new(self); end
|
16
|
+
def report(); @report_service ||= ReportService.new(self); end
|
17
|
+
|
18
|
+
|
19
|
+
def public_controller_method(path, query_values={})
|
20
|
+
unless defined?(Addressable)
|
21
|
+
raise "You need to install the addressable gem for this feature"
|
22
|
+
end
|
23
|
+
require 'addressable/uri'
|
24
|
+
login_if_required()
|
25
|
+
conn = get_client(:json, "#{base_jsonrpc2_url}")
|
26
|
+
conn.post do |req|
|
27
|
+
req.url path
|
28
|
+
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
|
29
|
+
req.headers['Cookie'] = "session_id=#{web_session[:session_id]}"
|
30
|
+
uri = Addressable::URI.new
|
31
|
+
uri.query_values = query_values
|
32
|
+
req.body = uri.query
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(config, web_session, id)
|
37
|
+
set_config(HashWithIndifferentAccess.new(config))
|
38
|
+
Object.const_set(config[:scope_prefix], Module.new) if config[:scope_prefix]
|
39
|
+
@models = {}
|
40
|
+
@local_context = {}
|
41
|
+
@web_session = web_session || {}
|
42
|
+
@id = id || web_session[:session_id]
|
43
|
+
Ooor.session_handler.register_session(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
def login_if_required
|
47
|
+
if !config[:user_id] || !web_session[:session_id]
|
48
|
+
login(config[:database], config[:username], config[:password], config[:params])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def login(db, username, password, kw={})
|
53
|
+
logger.debug "OOOR login - db: #{db}, username: #{username}"
|
54
|
+
raise "Cannot login without specifying a database" unless db
|
55
|
+
raise "Cannot login without specifying a username" unless username
|
56
|
+
raise "Cannot login without specifying a password" unless password
|
57
|
+
if config[:force_xml_rpc]
|
58
|
+
send("ooor_alias_login", db, username, password)
|
59
|
+
else
|
60
|
+
conn = get_client(:json, "#{self.base_jsonrpc2_url}")
|
61
|
+
response = conn.post do |req|
|
62
|
+
req.url '/web/session/authenticate'
|
63
|
+
req.headers['Content-Type'] = 'application/json'
|
64
|
+
req.body = {method: 'call', params: {db: db, login: username, password: password, base_location: kw}}.to_json
|
65
|
+
end
|
66
|
+
web_session[:cookie] = response.headers["set-cookie"]
|
67
|
+
json_response = JSON.parse(response.body)
|
68
|
+
error = json_response["error"]
|
69
|
+
if error && (error["data"]["type"] == "server_exception" || error['message'] == "Odoo Server Error")
|
70
|
+
raise "#{error['message']} ------- #{error['data']['debug']}"
|
71
|
+
elsif response.status == 200
|
72
|
+
if sid_part1 = web_session[:cookie].split("sid=")[1]
|
73
|
+
# required on v7 but not on v8+, this enables us to sniff if we are on v7
|
74
|
+
web_session[:sid] = web_session[:cookie].split("sid=")[1].split(";")[0]
|
75
|
+
end
|
76
|
+
|
77
|
+
web_session[:session_id] = json_response['result']['session_id']
|
78
|
+
|
79
|
+
user_id = json_response['result'].delete('uid')
|
80
|
+
config[:user_id] = user_id
|
81
|
+
web_session.merge!(json_response['result'].delete('user_context'))
|
82
|
+
set_config(json_response['result'])
|
83
|
+
Ooor.session_handler.register_session(self)
|
84
|
+
user_id
|
85
|
+
else
|
86
|
+
raise Faraday::Error::ClientError.new(response.status, response)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def set_config(configuration)
|
92
|
+
configuration.each do |k, v|
|
93
|
+
config.send "#{k}=", v
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# a part of the config that will be mixed in the context of each session
|
98
|
+
def connection_session
|
99
|
+
HashWithIndifferentAccess.new(config[:connection_session] || {})
|
100
|
+
end
|
101
|
+
|
102
|
+
def [](key)
|
103
|
+
self[key]
|
104
|
+
end
|
105
|
+
|
106
|
+
def []=(key, value)
|
107
|
+
self[key] = value
|
108
|
+
end
|
109
|
+
|
110
|
+
def global_login(options={})
|
111
|
+
set_config(options)
|
112
|
+
load_models(config[:models], config[:reload])
|
113
|
+
end
|
114
|
+
|
115
|
+
def with_context(context)
|
116
|
+
@local_context = context
|
117
|
+
yield
|
118
|
+
@local_context = {}
|
119
|
+
end
|
120
|
+
|
121
|
+
def session_context(context={})
|
122
|
+
connection_session.merge(web_session.slice('lang', 'tz')).merge(@local_context).merge(context) # not just lang and tz?
|
123
|
+
end
|
124
|
+
|
125
|
+
def const_get(model_key, lang=nil);
|
126
|
+
if config[:aliases]
|
127
|
+
if lang && alias_data = config[:aliases][lang]
|
128
|
+
openerp_model = alias_data[model_key] || model_key
|
129
|
+
elsif alias_data = config[:aliases][connection_session['lang'] || :en_US]
|
130
|
+
openerp_model = alias_data[model_key] || model_key
|
131
|
+
else
|
132
|
+
openerp_model = model_key
|
133
|
+
end
|
134
|
+
else
|
135
|
+
openerp_model = model_key
|
136
|
+
end
|
137
|
+
define_openerp_model(model: openerp_model, scope_prefix: config[:scope_prefix], generate_constants: config[:generate_constants])
|
138
|
+
end
|
139
|
+
|
140
|
+
def[](model_key) #TODO invert: define method here and use []
|
141
|
+
const_get(model_key)
|
142
|
+
end
|
143
|
+
|
144
|
+
def load_models(model_names=config[:models], reload=config[:reload])
|
145
|
+
helper_paths.each do |dir|
|
146
|
+
::Dir[dir].each { |file| require file }
|
147
|
+
end
|
148
|
+
search_domain = model_names ? [['model', 'in', model_names]] : []
|
149
|
+
models_records = read_model_data(search_domain)
|
150
|
+
models_records.reject {|opts| opts['model'] == '_unknown' }.each do |opts|
|
151
|
+
options = HashWithIndifferentAccess.new(opts.merge(scope_prefix: config[:scope_prefix],
|
152
|
+
reload: reload,
|
153
|
+
generate_constants: config[:generate_constants]))
|
154
|
+
define_openerp_model(options)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def read_model_data(search_domain)
|
159
|
+
if config[:force_xml_rpc]
|
160
|
+
model_ids = object.object_service(:execute, "ir.model", :search, search_domain, 0, false, false, {}, false)
|
161
|
+
models_records = object.object_service(:execute, "ir.model", :read, model_ids, ['model', 'name'])
|
162
|
+
else
|
163
|
+
response = object.object_service(:search_read, "ir.model", 'search_read',
|
164
|
+
fields: ['model', 'name'],
|
165
|
+
offset: 0,
|
166
|
+
limit: false,
|
167
|
+
domain: search_domain,
|
168
|
+
sort: false,
|
169
|
+
context: {})
|
170
|
+
models_records = response["records"]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def set_model_template!(klass, options)
|
175
|
+
template = Ooor.model_registry.get_template(config, options[:model])
|
176
|
+
if template
|
177
|
+
klass.t = template
|
178
|
+
else
|
179
|
+
template = Ooor::ModelSchema.new
|
180
|
+
template.openerp_model = options[:model]
|
181
|
+
template.openerp_id = options[:id]
|
182
|
+
template.description = options[:name]
|
183
|
+
template.state = options[:state]
|
184
|
+
template.many2one_associations = {}
|
185
|
+
template.one2many_associations = {}
|
186
|
+
template.many2many_associations = {}
|
187
|
+
template.polymorphic_m2o_associations = {}
|
188
|
+
template.associations_keys = []
|
189
|
+
klass.t = template
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def define_openerp_model(options) #TODO param to tell if we define constants or not
|
194
|
+
if !models[options[:model]] || options[:reload]# || !scope.const_defined?(model_class_name)
|
195
|
+
scope_prefix = options[:scope_prefix]
|
196
|
+
scope = scope_prefix ? Object.const_get(scope_prefix) : Object
|
197
|
+
model_class_name = class_name_from_model_key(options[:model])
|
198
|
+
logger.debug "registering #{model_class_name}"
|
199
|
+
klass = Class.new(Base)
|
200
|
+
set_model_template!(klass, options)
|
201
|
+
klass.name = model_class_name
|
202
|
+
klass.scope_prefix = scope_prefix
|
203
|
+
klass.session = self
|
204
|
+
if options[:generate_constants] && (options[:reload] || !scope.const_defined?(model_class_name))
|
205
|
+
scope.const_set(model_class_name, klass)
|
206
|
+
end
|
207
|
+
(Ooor.extensions[options[:model]] || []).each do |block|
|
208
|
+
klass.class_eval(&block)
|
209
|
+
end
|
210
|
+
models[options[:model]] = klass
|
211
|
+
end
|
212
|
+
models[options[:model]]
|
213
|
+
end
|
214
|
+
|
215
|
+
# def models; @models ||= {}; end
|
216
|
+
|
217
|
+
def logger; Ooor.logger; end
|
218
|
+
|
219
|
+
def helper_paths
|
220
|
+
[File.dirname(__FILE__) + '/helpers/*', *config[:helper_paths]]
|
221
|
+
end
|
222
|
+
|
223
|
+
def class_name_from_model_key(model_key)
|
224
|
+
model_key.split('.').collect {|name_part| name_part.capitalize}.join
|
225
|
+
end
|
226
|
+
|
227
|
+
def odoo_serie
|
228
|
+
if config.user_id # authenticated session
|
229
|
+
if config[:server_version_info] # v10 and onward
|
230
|
+
config[:server_version_info][0]
|
231
|
+
elsif config['partner_id']
|
232
|
+
9
|
233
|
+
elsif web_session[:sid]
|
234
|
+
7
|
235
|
+
else
|
236
|
+
8
|
237
|
+
end
|
238
|
+
else
|
239
|
+
json_conn = get_client(:json, base_jsonrpc2_url)
|
240
|
+
begin
|
241
|
+
@version_info ||= json_conn.oe_service(web_session, "/web/webclient/version_info", nil, nil, [])
|
242
|
+
@version_info['server_serie'].to_i
|
243
|
+
rescue # Odoo v7 doesn't have this version info service
|
244
|
+
7
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
250
|
+
end
|