ext_ooor 2.3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|