tr8n 3.1.7 → 3.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tr8n (3.1.7)
4
+ tr8n (3.1.8)
5
5
  coffee-script
6
6
  kaminari
7
7
  rails (>= 3.1.0)
@@ -0,0 +1,129 @@
1
+ #--
2
+ # Copyright (c) 2010-2012 Michael Berkovich, tr8n.net
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ ###########################################################################
25
+ ## API for getting translations from the main server
26
+ ###########################################################################
27
+
28
+ class Tr8n::Api::V1::ApplicationController < ActionController::Base
29
+ # for ssl access to the translator - using ssl_requirement plugin
30
+ ssl_allowed :sync if respond_to?(:ssl_allowed)
31
+
32
+ def sync
33
+ ensure_push_enabled
34
+
35
+ sync_log = Tr8n::SyncLog.create(:started_at => Time.now,
36
+ :keys_received => 0, :translations_received => 0,
37
+ :keys_sent => 0, :translations_sent => 0)
38
+
39
+ method = params[:method] || "push"
40
+
41
+ payload = []
42
+
43
+ if method == "push"
44
+ payload = push_translations(sync_log)
45
+
46
+ elsif method == "pull"
47
+ payload = pull_translations(sync_log)
48
+
49
+ end
50
+
51
+ sync_log.finished_at = Time.now
52
+ sync_log.save
53
+
54
+ sanitize_api_response(:translation_keys => payload)
55
+ rescue Tr8n::Exception => ex
56
+ sanitize_api_response("error" => ex.message)
57
+ end
58
+
59
+ private
60
+
61
+ def ensure_push_enabled
62
+ raise Tr8n::Exception.new("Push is disabled") unless Tr8n::Config.synchronization_push_enabled?
63
+ raise Tr8n::Exception.new("Unauthorized server push attempt") unless Tr8n::Config.synchronization_push_servers.include?(request.env['REMOTE_HOST'])
64
+ end
65
+
66
+ def translator
67
+ @translator ||= Tr8n::Config.system_translator
68
+ end
69
+
70
+ def languages
71
+ @languages ||= Tr8n::Language.enabled_languages
72
+ end
73
+
74
+ def batch_size
75
+ @batch_size ||= Tr8n::Config.synchronization_batch_size
76
+ end
77
+
78
+ def push_translations(sync_log, opts = {})
79
+ payload = []
80
+
81
+ # already parsed by Rails
82
+ # keys = JSON.parse(params[:translation_keys])
83
+ keys = params[:translation_keys]
84
+ return payload unless keys
85
+
86
+ sync_log.keys_received += keys.size
87
+
88
+ keys.each do |tkey_hash|
89
+ # pp tkey_hash
90
+ tkey, remaining_translations = Tr8n::TranslationKey.create_from_sync_hash(tkey_hash, translator)
91
+ next unless tkey
92
+
93
+ sync_log.translations_received += tkey_hash[:translations].size if tkey_hash[:translations]
94
+ sync_log.translations_sent += remaining_translations.size
95
+
96
+ tkey.mark_as_synced!
97
+
98
+ payload << tkey.to_sync_hash(:translations => remaining_translations, :languages => languages)
99
+ end
100
+
101
+ payload
102
+ end
103
+
104
+ def pull_translations(sync_log, opts = {})
105
+ payload = []
106
+
107
+ # find all keys that have changed since the last sync
108
+ changed_keys = Tr8n::TranslationKey.where("synced_at is null or updated_at > synced_at").limit(batch_size)
109
+ sync_log.keys_sent += changed_keys.size
110
+
111
+ changed_keys.each do |tkey|
112
+ tkey_hash = tkey.to_sync_hash(:languages => languages)
113
+ payload << tkey_hash
114
+ sync_log.translations_sent += tkey_hash["translations"].size if tkey_hash["translations"]
115
+ tkey.mark_as_synced!
116
+ end
117
+
118
+ payload
119
+ end
120
+
121
+ def sanitize_api_response(response)
122
+ if Tr8n::Config.api[:response_encoding] == "xml"
123
+ render(:text => response.to_xml)
124
+ else
125
+ render(:json => response.to_json)
126
+ end
127
+ end
128
+
129
+ end
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2010-2011 Michael Berkovich
2
+ # Copyright (c) 2010-2012 Michael Berkovich, tr8n.net
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2010-2011 Michael Berkovich
2
+ # Copyright (c) 2010-2012 Michael Berkovich, tr8n.net
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2010-2011 Michael Berkovich
2
+ # Copyright (c) 2010-2012 Michael Berkovich, tr8n.net
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -123,7 +123,7 @@ class Tr8n::LanguageRule < ActiveRecord::Base
123
123
  ###############################################################
124
124
  ## Synchronization Methods
125
125
  ###############################################################
126
- def to_sync_hash(token)
126
+ def to_sync_hash(token, opts = {})
127
127
  {
128
128
  "token" => token,
129
129
  "type" => self.class.keyword,
@@ -22,27 +22,39 @@
22
22
  #++
23
23
 
24
24
  class Tr8n::SyncLog < ActiveRecord::Base
25
-
25
+ set_table_name :tr8n_sync_logs
26
+
26
27
  def self.sync(opts = {})
27
- sync_log = Tr8n::SyncLog.create(:started_at => Time.now)
28
+ sync_log = Tr8n::SyncLog.create(:started_at => Time.now,
29
+ :keys_received => 0, :translations_received => 0,
30
+ :keys_sent => 0, :translations_sent => 0)
31
+ sync_log.sync(opts)
32
+ sync_log
33
+ end
34
+
35
+ def sync(opts = {})
36
+ access_token
28
37
 
29
- translation_count = 0
30
- payload = []
38
+ translation_keys = []
31
39
  batch_count = 0
32
- total_key_count = 0
33
40
 
34
41
  log("Begin synchronization process...")
35
- log("Registering keys...")
42
+ log("Pushing keys...")
36
43
 
37
44
  conditions = "synced_at is null or updated_at > synced_at"
38
45
  conditions = nil if opts[:force]
39
46
 
40
47
  # STDOUT.sync = true
41
48
 
42
- Tr8n::TranslationKey.find_each(:conditions => conditions, :batch_size => Tr8n::Config.synchronization_batch_size) do |key|
43
- total_key_count += 1
44
- sync_hash = key.to_sync_hash
45
- payload << sync_hash
49
+ languages = Tr8n::Language.enabled_languages
50
+ total_key_count = Tr8n::TranslationKey.count(:conditions => conditions)
51
+ log("#{total_key_count} translation keys will be synchronized with the remote server in chunks of #{batch_size} keys...")
52
+
53
+ Tr8n::TranslationKey.find_each(:conditions => conditions, :batch_size => batch_size) do |key|
54
+ self.keys_sent += 1
55
+ tkey_hash = key.to_sync_hash(:languages => languages)
56
+ self.translations_sent += tkey_hash["translations"].size if tkey_hash["translations"]
57
+ translation_keys << tkey_hash
46
58
 
47
59
  key.mark_as_synced!
48
60
 
@@ -50,49 +62,43 @@ class Tr8n::SyncLog < ActiveRecord::Base
50
62
  # payload << sync_hash
51
63
  # end
52
64
 
53
- if payload.size == Tr8n::Config.synchronization_batch_size
54
- # pp "Sending #{batch_count+1} batch of #{Tr8n::Config.synchronization_batch_size} keys..."
65
+ if translation_keys.size >= Tr8n::Config.synchronization_batch_size
55
66
  batch_count += 1
56
- exchange(payload)
57
- payload = []
67
+ push_translations(translation_keys, opts)
68
+ translation_keys = []
69
+ log("Sent #{self.keys_sent} keys and #{self.translations_sent} translations.")
58
70
  end
59
71
  end
60
72
 
61
- if payload.size > 0
62
- # pp "Sending final batch..."
73
+ if translation_keys.size > 0
63
74
  batch_count += 1
64
- exchange(payload, opts)
75
+ push_translations(translation_keys, opts)
76
+ log("Sent #{self.keys_sent} keys with #{self.translations_sent} translations.")
65
77
  end
66
-
67
- sync_log.keys_sent = total_key_count
68
78
 
69
- log("Sent #{total_key_count} keys in #{batch_count} batches.")
79
+ log("Done. Sent #{total_key_count} keys with #{self.translations_sent} translations in #{batch_count} calls.")
70
80
 
71
81
  batch_count = 0
72
- total_key_count = 0
73
82
 
74
83
  unless opts[:force]
75
- log("Downloading translations...")
84
+ log("Pulling translations...")
76
85
 
77
- key_count = download
86
+ key_count = pull_translations(opts)
78
87
  while key_count > 0
79
88
  batch_count += 1
80
- total_key_count += key_count
81
- key_count = download(opts)
89
+ key_count = pull_translations(opts)
82
90
  end
83
- log("Downloaded #{total_key_count} keys in #{batch_count} batches.")
91
+ log("Done. Downloaded #{self.translations_received} keys with #{self.translations_received} translations.")
84
92
  end
85
93
 
86
- sync_log.keys_received = total_key_count
87
- sync_log.finished_at = Time.now
88
- sync_log.save
89
-
94
+ self.finished_at = Time.now
95
+ save
90
96
  rescue Exception => ex
91
- pp ex.message
92
- pp ex.backtrace
97
+ log_error(ex)
98
+ # pp ex.backtrace
93
99
  end
94
100
 
95
- def self.access_token
101
+ def access_token
96
102
  @access_token ||= begin
97
103
  uri = URI.parse("#{Tr8n::Config.synchronization_server}/platform/oauth/request_token?client_id=#{Tr8n::Config.synchronization_key}&client_secret=#{Tr8n::Config.synchronization_secret}&grant_type=client_credentials")
98
104
  response = Net::HTTP.get_response(uri)
@@ -102,9 +108,17 @@ class Tr8n::SyncLog < ActiveRecord::Base
102
108
  end
103
109
  end
104
110
 
105
- def self.exchange(payload, opts = {})
111
+ def batch_size
112
+ @batch_size ||= Tr8n::Config.synchronization_batch_size
113
+ end
114
+
115
+ def translator
116
+ @translator ||= Tr8n::Config.system_translator
117
+ end
118
+
119
+ def push_translations(payload, opts = {})
106
120
  uri = URI.parse("#{Tr8n::Config.synchronization_server}/api/application/sync")
107
- params = {:method => "register", :batch_size => Tr8n::Config.synchronization_batch_size, :translation_keys => payload}
121
+ params = {:method => "push", :batch_size => batch_size, :translation_keys => payload}
108
122
 
109
123
  req = Net::HTTP::Post.new(uri.path)
110
124
  req["Content-Type"] = "application/json"
@@ -124,17 +138,21 @@ class Tr8n::SyncLog < ActiveRecord::Base
124
138
  raise Tr8n::Exception.new("Synchronization failed") unless response.is_a?(Net::HTTPOK)
125
139
 
126
140
  data = HashWithIndifferentAccess.new(JSON.parse(response.body))
141
+ return unless data[:translation_keys]
142
+
127
143
  # pp data
144
+ self.keys_received += data[:translation_keys].size
128
145
 
129
146
  data[:translation_keys].each do |tkey_hash|
130
147
  # pp tkey_hash
131
- Tr8n::TranslationKey.create_from_sync_hash(tkey_hash, Tr8n::Config.system_translator)
148
+ self.translations_received += tkey_hash["translations"].size if tkey_hash["translations"]
149
+ Tr8n::TranslationKey.create_from_sync_hash(tkey_hash, translator)
132
150
  end
133
151
  end
134
152
 
135
- def self.download(opts = {})
153
+ def pull_translations(opts = {})
136
154
  uri = URI.parse("#{Tr8n::Config.synchronization_server}/api/application/sync")
137
- params = {:method=>"download", :batch_size => Tr8n::Config.synchronization_batch_size}
155
+ params = {:method=>"pull", :batch_size => batch_size}
138
156
  params[:force] = true if opts[:force]
139
157
 
140
158
  req = Net::HTTP::Post.new(uri.path)
@@ -153,19 +171,25 @@ class Tr8n::SyncLog < ActiveRecord::Base
153
171
  raise Tr8n::Exception.new("Synchronization failed") unless response.is_a?(Net::HTTPOK)
154
172
 
155
173
  data = HashWithIndifferentAccess.new(JSON.parse(response.body))
156
- # pp data
174
+ return 0 unless data[:translation_keys]
175
+
176
+ self.keys_received += data[:translation_keys].size
157
177
 
158
178
  data[:translation_keys].each do |tkey_hash|
159
- # pp tkey_hash
160
- tkey, translations = Tr8n::TranslationKey.create_from_sync_hash(tkey_hash, Tr8n::Config.system_translator)
179
+ self.translations_received += tkey_hash["translations"].size if tkey_hash["translations"]
180
+ tkey, translations = Tr8n::TranslationKey.create_from_sync_hash(tkey_hash, translator)
161
181
  tkey.mark_as_synced!
162
182
  end
163
183
 
164
184
  data[:translation_keys].size
165
185
  end
166
186
 
167
- def self.log(msg)
187
+ def log(msg)
168
188
  pp "#{Time.now}: #{msg}"
169
189
  end
190
+
191
+ def log_error(msg)
192
+ pp "Error: #{msg}"
193
+ end
170
194
 
171
- end
195
+ end
@@ -223,41 +223,48 @@ class Tr8n::Translation < ActiveRecord::Base
223
223
  ## Synchronization Methods
224
224
  ###############################################################
225
225
  # generates the hash without rule ids, but with full definitions
226
- def rules_sync_hash
227
- @rules_sync_hash ||= (rules || []).collect{|rule| rule[:rule].to_sync_hash(rule[:token])}
226
+ def mark_as_synced!
227
+ update_attributes(:synced_at => Time.now + 2.seconds)
228
+ end
229
+
230
+ def rules_sync_hash(opts = {})
231
+ @rules_sync_hash ||= (rules || []).collect{|rule| rule[:rule].to_sync_hash(rule[:token], opts)}
228
232
  end
229
233
 
230
234
  # serilaize translation to API hash to be used for synchronization
231
- def to_sync_hash(include_translator = true)
232
- hash = {"locale" => language.locale, "label" => label, "rank" => rank, "rules" => rules_sync_hash}
233
- hash["translator_id"] = translator.remote_id if include_translator and translator and translator.remote_id
235
+ def to_sync_hash(opts = {})
236
+ return {"locale" => language.locale, "label" => label, "rules" => rules_sync_hash(opts)} if opts[:comparible]
237
+
238
+ hash = {"locale" => language.locale, "label" => label, "rank" => rank, "rules" => rules_sync_hash(opts)}
239
+ if translator
240
+ if opts[:include_translator] # tr8n.net => local = include full translator info
241
+ hash["translator"] = translator.to_sync_hash(opts)
242
+ elsif translator.remote_id # local => tr8n.net = include only the remote id of the translator if the translator is linked
243
+ hash["translator_id"] = translator.remote_id
244
+ end
245
+ end
234
246
  hash
235
247
  end
236
248
 
237
249
  # create translation from API hash for a specific key
238
- def self.create_from_sync_hash(tkey, translator, hash, opts = {})
239
- # don't add empty translations
240
- return if hash["label"].blank?
250
+ def self.create_from_sync_hash(tkey, translator, thash, opts = {})
251
+ return if thash["label"].blank? # don't add empty translations
241
252
 
242
- lang = Tr8n::Language.for(hash["locale"])
243
- # don't add translations for an unsupported language
244
- return unless lang
253
+ lang = Tr8n::Language.for(thash["locale"])
254
+ return unless lang # don't add translations for an unsupported language
245
255
 
246
256
  # generate rules for the translation
247
- rules = []
248
-
249
- if hash["rules"] and hash["rules"].any?
250
- hash["rules"].each do |rule_hash|
251
- rule = Tr8n::LanguageRule.create_from_sync_hash(lang, translator, rule_hash, opts)
252
- pp rule
253
-
257
+ rules = []
258
+ if thash["rules"] and thash["rules"].any?
259
+ thash["rules"].each do |rhash|
260
+ rule = Tr8n::LanguageRule.create_from_sync_hash(lang, translator, rhash, opts)
254
261
  return unless rule # if the rule has not been created, we should not even add the translation
255
- rules << {:token => rule_hash["token"], :rule_id => rule.id}
262
+ rules << {:token => rhash["token"], :rule_id => rule.id}
256
263
  end
257
264
  end
258
-
259
265
  rules = nil if rules.empty?
260
- tkey.add_translation(hash["label"], rules, lang, translator)
266
+
267
+ tkey.add_translation(thash["label"], rules, lang, translator)
261
268
  end
262
269
 
263
270
  ###############################################################
@@ -219,10 +219,7 @@ class Tr8n::TranslationKey < ActiveRecord::Base
219
219
 
220
220
  def add_translation(label, rules = nil, language = Tr8n::Config.current_language, translator = Tr8n::Config.current_translator)
221
221
  raise Tr8n::Exception.new("The sentence contains dirty words") unless language.clean_sentence?(label)
222
- pp rules
223
222
  translation = Tr8n::Translation.create(:translation_key => self, :language => language, :translator => translator, :label => label, :rules => rules)
224
- pp translation
225
-
226
223
  translation.vote!(translator, 1)
227
224
  translation
228
225
  end
@@ -230,7 +227,9 @@ class Tr8n::TranslationKey < ActiveRecord::Base
230
227
  # returns all translations for the key, language and minimal rank
231
228
  def translations_for(language = nil, rank = nil)
232
229
  translations = Tr8n::Translation.where("translation_key_id = ?", self.id)
233
- translations = translations.where("language_id = ?", language.id) if language
230
+ if language
231
+ translations = translations.where("language_id in (?)", [language].flatten.collect{|lang| lang.id})
232
+ end
234
233
  translations = translations.where("rank >= ?", rank) if rank
235
234
  translations.order("rank desc").all
236
235
  end
@@ -531,49 +530,59 @@ class Tr8n::TranslationKey < ActiveRecord::Base
531
530
  ## Synchronization Methods
532
531
  ###############################################################
533
532
  def mark_as_synced!
534
- update_attributes(:synced_at => Time.now + 5.seconds)
533
+ update_attributes(:synced_at => Time.now + 2.seconds)
535
534
  end
536
535
 
537
- def to_sync_hash(default_translation_hashes = nil)
536
+ def to_sync_hash(opts = {})
538
537
  {
539
538
  "key" => self.key,
540
539
  "label" => self.label,
541
540
  "description" => self.description,
542
541
  "locale" => (locale || Tr8n::Config.default_locale),
543
- "translations" => default_translation_hashes || translations_for(nil, Tr8n::Config.translation_threshold).collect{|t| t.to_sync_hash}
542
+ "translations" => opts[:translations] || translations_for(opts[:languages], opts[:threshold] || Tr8n::Config.translation_threshold).collect{|t| t.to_sync_hash(opts)}
544
543
  }
545
544
  end
546
545
 
547
- def transations_sync_hashes
548
- @transations_sync_hashes ||= translations.collect{|t| t.to_sync_hash(false)}
546
+ def transations_sync_hashes(opts = {})
547
+ @transations_sync_hashes ||= begin
548
+ translations.collect{|t| t.to_sync_hash(:comparible => true)}
549
+ end
549
550
  end
550
551
 
552
+ def self.can_create_from_sync_hash?(tkey_hash, translator, opts = {})
553
+ return false if tkey_hash["key"].blank? or tkey_hash["label"].blank? or tkey_hash["locale"].blank?
554
+ true
555
+ end
556
+
551
557
  # create translation key from API hash
552
- def self.create_from_sync_hash(tkey_hash, translator, opts = {})
553
- return if tkey_hash["key"].blank? or tkey_hash["label"].blank? or tkey_hash["locale"].blank?
554
-
558
+ def self.create_from_sync_hash(tkey_hash, default_translator, opts = {})
559
+ return unless can_create_from_sync_hash?(tkey_hash, default_translator, opts)
560
+
561
+ # find or create translation key
555
562
  tkey = Tr8n::TranslationKey.find_or_create(tkey_hash["label"], tkey_hash["description"])
556
563
 
557
- # return unless tkey.key==tkey_hash[:key] # need to warn the user that the key methods don't match
564
+ # we will keep the translations that need to be sent back
565
+ remaining_translations = tkey.transations_sync_hashes(opts).dup
558
566
 
559
- # opts[:force_create] = Tr8n::Config.synchronization_create_rules? if opts[:force_create].nil?
560
-
561
- remaining_translations = tkey.transations_sync_hashes.dup
562
- # pp :before, remaining_sync_hashes
563
-
564
567
  added_trans = []
565
- (tkey_hash["translations"] || []).each do |t_hash|
568
+ (tkey_hash["translations"] || []).each do |thash|
569
+ remaining_translations.delete(thash)
570
+
566
571
  # if the translation came from a linked translator, use the translator
567
- translation_translator = translator
568
- if t_hash["translator_id"]
569
- translation_translator = Tr8n::Translator.find_by_id(t_hash["translator_id"])
570
- t_hash.delete("translator_id")
571
- translation_translator ||= translator
572
+ translator = default_translator
573
+ if thash["translator_id"] # incoming translations from the remote server
574
+ translator = Tr8n::Translator.find_by_id(thash["translator_id"]) || default_translator
575
+ elsif thash["translator"]
576
+ translator = Tr8n::Translator.create_from_sync_hash(thash["translator"], opts) || default_translator
572
577
  end
573
-
574
- remaining_translations.delete(t_hash)
575
- next if tkey.transations_sync_hashes.include?(t_hash)
576
- trans = Tr8n::Translation.create_from_sync_hash(tkey, translation_translator, t_hash, opts)
578
+
579
+ # don't insert duplicate translations
580
+ comparible_hash = thash.slice("locale", "label", "rules")
581
+ next if tkey.transations_sync_hashes.include?(comparible_hash)
582
+
583
+ translation = Tr8n::Translation.create_from_sync_hash(tkey, translator, thash, opts)
584
+
585
+ translation.mark_as_synced! if translation
577
586
  end
578
587
 
579
588
  # need to send back translations that have not been added, but exist in the system
@@ -190,6 +190,12 @@ class Tr8n::Translator < ActiveRecord::Base
190
190
  false
191
191
  end
192
192
 
193
+ # for translators registered on the exchange server
194
+ def remote?
195
+ return false if user
196
+ not remote_id.nil?
197
+ end
198
+
193
199
  def system?
194
200
  level == Tr8n::Config.system_level
195
201
  end
@@ -204,13 +210,14 @@ class Tr8n::Translator < ActiveRecord::Base
204
210
 
205
211
  def name
206
212
  return "Tr8n Network" if system?
213
+ return super if remote?
207
214
 
208
215
  unless Tr8n::Config.site_user_info_enabled?
209
216
  translator_name = super
210
217
  return translator_name unless translator_name.blank?
211
218
  return "No Name"
212
219
  end
213
-
220
+
214
221
  return "Deleted User" unless user
215
222
  user_name = Tr8n::Config.user_name(user)
216
223
  return "No Name" if user_name.blank?
@@ -220,6 +227,7 @@ class Tr8n::Translator < ActiveRecord::Base
220
227
 
221
228
  def gender
222
229
  return "unknown" if system?
230
+ return super if remote?
223
231
 
224
232
  unless Tr8n::Config.site_user_info_enabled?
225
233
  translator_gender = super
@@ -230,9 +238,10 @@ class Tr8n::Translator < ActiveRecord::Base
230
238
  Tr8n::Config.user_gender(user)
231
239
  end
232
240
 
241
+ # TODO: change db to mugshot_url
233
242
  def mugshot
234
243
  return Tr8n::Config.system_image if system?
235
-
244
+ return super if remote?
236
245
  return super unless Tr8n::Config.site_user_info_enabled?
237
246
  return Tr8n::Config.silhouette_image unless user
238
247
  img_url = Tr8n::Config.user_mugshot(user)
@@ -240,7 +249,9 @@ class Tr8n::Translator < ActiveRecord::Base
240
249
  img_url
241
250
  end
242
251
 
252
+ # TODO: change db to link_url
243
253
  def link
254
+ # return super if remote?
244
255
  return super unless Tr8n::Config.site_user_info_enabled?
245
256
  return Tr8n::Config.default_url unless user
246
257
  Tr8n::Config.user_link(user)
@@ -263,12 +274,14 @@ class Tr8n::Translator < ActiveRecord::Base
263
274
 
264
275
  def level
265
276
  return Tr8n::Config.admin_level if admin?
277
+ return super if remote?
266
278
  return 0 if super.nil?
267
279
  super
268
280
  end
269
281
 
270
282
  def title
271
283
  return 'admin' if admin?
284
+ return super if remote?
272
285
  Tr8n::Config.translator_levels[level.to_s] || 'unknown'
273
286
  end
274
287
 
@@ -303,4 +316,29 @@ class Tr8n::Translator < ActiveRecord::Base
303
316
  def to_s
304
317
  name
305
318
  end
319
+
320
+ ###############################################################
321
+ ## Synchronization Methods
322
+ ###############################################################
323
+ def to_sync_hash(opts = {})
324
+ {
325
+ "id" => opts[:remote] ? self.remote_id : self.id,
326
+ "name" => self.name,
327
+ "gender" => self.gender,
328
+ "mugshot" => self.mugshot,
329
+ "link" => self.link
330
+ }
331
+ end
332
+
333
+ def self.create_from_sync_hash(thash, opts = {})
334
+ Tr8n::Translator.find_by_remote_id(thash[:id]) || Tr8n::Translator.create(
335
+ :user_id => 0,
336
+ :remote_id => thash[:id],
337
+ :name => thash[:name],
338
+ :gender => thash[:gender],
339
+ :mugshot => thash[:mugshot],
340
+ :link => thash[:link]
341
+ )
342
+ end
343
+
306
344
  end
@@ -32,7 +32,7 @@ Tr8n::Engine.routes.draw do
32
32
  match "admin/#{ctrl}(/:action)", :controller => "admin/#{ctrl}"
33
33
  end
34
34
 
35
- [:language, :translation, :translator].each do |ctrl|
35
+ [:application, :language, :translation, :translator].each do |ctrl|
36
36
  match "api/v1/#{ctrl}(/:action)", :controller => "api/v1/#{ctrl}"
37
37
  end
38
38
 
@@ -34,7 +34,9 @@ class CreateTr8nSyncTables < ActiveRecord::Migration
34
34
  end
35
35
 
36
36
  add_column :tr8n_translation_keys, :synced_at, :timestamp
37
+ add_index :tr8n_translation_keys, :synced_at
37
38
  add_column :tr8n_translations, :synced_at, :timestamp
39
+ add_index :tr8n_translations, :synced_at
38
40
  end
39
41
 
40
42
  def down
@@ -264,13 +264,14 @@ defaults:
264
264
  # You can run this command as often as your account on tr8n.net permits you.
265
265
  #############################################################################
266
266
  synchronization:
267
- server: "http://tr8n.net" # alternative, regional locations will be available in the future
268
- key: "YOUR APP KEY" # replace this with your key
269
- secret: "YOUR APP SECRET" # replace this with your secret
270
- batch_size: 50 # how many kesy to send to the server at a time
271
- create_rules: true # force rules creation, or skip translations for rules that don't exist
272
- all_languages: false # use only enabled languages, or all languages
273
-
267
+ server: "http://tr8n.net" # visit this url to register your application
268
+ key: "YOUR APP KEY" # replace with your application key
269
+ secret: "YOUR APP SECRET" # replace with your application secret
270
+ batch_size: 50 # how many keys to send to the server at a time - if your site supports many language, decrease the batch size
271
+ all_languages: false # use only enabled languages, or all languages
272
+ enable_push: false # allows translations to be pushed from the main server using the sync API
273
+ push_servers: ["tr8n.net"] # only allows those servers to push translations
274
+
274
275
  #############################################################################
275
276
  # Environment Settings
276
277
  # You can overload any feature defined in the defaults for any environment
@@ -104,7 +104,7 @@ class CreateTr8nTables < ActiveRecord::Migration
104
104
  add_index :tr8n_language_metrics, [:created_at], :name => :tr8n_lm_c
105
105
 
106
106
  create_table :tr8n_translators do |t|
107
- t.integer :user_id, :null => false
107
+ t.integer :user_id
108
108
  t.boolean :inline_mode, :default => false
109
109
  t.boolean :blocked, :default => false
110
110
  t.boolean :reported, :default => false
@@ -100,7 +100,7 @@ module Tr8n
100
100
  Tr8n::Translator, Tr8n::TranslatorLog, Tr8n::TranslatorMetric,
101
101
  Tr8n::TranslatorFollowing, Tr8n::TranslatorReport,
102
102
  Tr8n::LanguageForumTopic, Tr8n::LanguageForumMessage, Tr8n::LanguageForumAbuseReport,
103
- Tr8n::Glossary, Tr8n::IpLocation
103
+ Tr8n::Glossary, Tr8n::IpLocation, Tr8n::SyncLog
104
104
  ]
105
105
  end
106
106
 
@@ -763,12 +763,17 @@ module Tr8n
763
763
  synchronization[:secret]
764
764
  end
765
765
 
766
- def self.synchronization_create_rules?
767
- synchronization[:create_rules]
768
- end
769
-
770
766
  def self.synchronization_all_languages?
771
767
  synchronization[:all_languages]
772
768
  end
769
+
770
+ def self.synchronization_push_enabled?
771
+ synchronization[:enable_push]
772
+ end
773
+
774
+ def self.synchronization_push_servers
775
+ synchronization[:push_servers]
776
+ end
777
+
773
778
  end
774
779
  end
@@ -1,3 +1,3 @@
1
1
  module Tr8n
2
- VERSION = "3.1.7"
2
+ VERSION = "3.1.8"
3
3
  end
@@ -258,18 +258,27 @@ defaults:
258
258
  # You can run this command as often as your account on tr8n.net permits you.
259
259
  #############################################################################
260
260
  synchronization:
261
- server: "http://tr8n.net" # alternative, regional locations will be available in the future
262
- key: "iKjOrZjor68a4UeCD3Cq2r9t9ibBC4HnYoMNMrN7" # replace this with your key
263
- secret: "iKjOrZjor68a4UeCD3Cq2r9t9ibBC4HnYoMNMrN7" # replace this with your secret
264
- batch_size: 50 # how many kesy to send to the server at a time
265
- create_rules: true # force rules creation, or skip translations for rules that don't exist
266
- all_languages: false # use only enabled languages, or all languages
261
+ server: "http://tr8n.net" # visit this url to register your application
262
+ key: "YOUR APP KEY" # replace with your application key
263
+ secret: "YOUR APP SECRET" # replace with your application secret
264
+ batch_size: 50 # how many keys to send to the server at a time - if your site supports many language, decrease the batch size
265
+ all_languages: false # use only enabled languages, or all languages
266
+ enable_push: false # allows translations to be pushed from the main server using the sync API
267
+ push_servers: ["tr8n.net"] # only allows those servers to push translations
267
268
 
268
269
  #############################################################################
269
270
  # Environment Settings
270
271
  # You can overload any feature defined in the defaults for any environment
271
272
  #############################################################################
272
273
  development:
274
+ # synchronization:
275
+ # server: "http://localhost:3000" # alternative, regional locations will be available in the future
276
+ # key: "14ngS3YadRF54PArKR8mre3MJfPzJA0jxW2j2RJu" # replace this with your key
277
+ # secret: "ycFdAAQOEmxtpb8gE9M4gqQrgfU4SR1LetkXnLDZ" # replace this with your secret
278
+ # batch_size: 50 # how many kesy to send to the server at a time
279
+ # all_languages: false # use only enabled languages, or all languages
280
+ # enable_push: true # allows translations to be pushed from the main server using the sync API
281
+ # push_servers: ["localhost", "tr8n.net"] # only allows those servers to push translations
273
282
 
274
283
  test:
275
284
 
@@ -104,7 +104,7 @@ class CreateTr8nTables < ActiveRecord::Migration
104
104
  add_index :tr8n_language_metrics, [:created_at], :name => :tr8n_lm_c
105
105
 
106
106
  create_table :tr8n_translators do |t|
107
- t.integer :user_id, :null => false
107
+ t.integer :user_id
108
108
  t.boolean :inline_mode, :default => false
109
109
  t.boolean :blocked, :default => false
110
110
  t.boolean :reported, :default => false
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tr8n
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.7
4
+ version: 3.1.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-12 00:00:00.000000000Z
12
+ date: 2012-01-23 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &2161020140 !ruby/object:Gem::Requirement
16
+ requirement: &2161128960 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 3.1.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2161020140
24
+ version_requirements: *2161128960
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: will_filter
27
- requirement: &2161018480 !ruby/object:Gem::Requirement
27
+ requirement: &2161122860 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 3.1.2
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *2161018480
35
+ version_requirements: *2161122860
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: kaminari
38
- requirement: &2161017580 !ruby/object:Gem::Requirement
38
+ requirement: &2161121960 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *2161017580
46
+ version_requirements: *2161121960
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sass
49
- requirement: &2161016440 !ruby/object:Gem::Requirement
49
+ requirement: &2161120940 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *2161016440
57
+ version_requirements: *2161120940
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: coffee-script
60
- requirement: &2161015140 !ruby/object:Gem::Requirement
60
+ requirement: &2161120060 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *2161015140
68
+ version_requirements: *2161120060
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: pry
71
- requirement: &2161013980 !ruby/object:Gem::Requirement
71
+ requirement: &2161119400 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *2161013980
79
+ version_requirements: *2161119400
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: bundler
82
- requirement: &2161012960 !ruby/object:Gem::Requirement
82
+ requirement: &2161118620 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: 1.0.0
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *2161012960
90
+ version_requirements: *2161118620
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: sqlite3
93
- requirement: &2161011440 !ruby/object:Gem::Requirement
93
+ requirement: &2161117900 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *2161011440
101
+ version_requirements: *2161117900
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: rspec
104
- requirement: &2161009940 !ruby/object:Gem::Requirement
104
+ requirement: &2161117200 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: '0'
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *2161009940
112
+ version_requirements: *2161117200
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: rspec-rails
115
- requirement: &2161008580 !ruby/object:Gem::Requirement
115
+ requirement: &2161116420 !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
118
118
  - - ! '>='
@@ -120,10 +120,10 @@ dependencies:
120
120
  version: '0'
121
121
  type: :development
122
122
  prerelease: false
123
- version_requirements: *2161008580
123
+ version_requirements: *2161116420
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: factory_girl
126
- requirement: &2161006680 !ruby/object:Gem::Requirement
126
+ requirement: &2161115780 !ruby/object:Gem::Requirement
127
127
  none: false
128
128
  requirements:
129
129
  - - ! '>='
@@ -131,10 +131,10 @@ dependencies:
131
131
  version: '0'
132
132
  type: :development
133
133
  prerelease: false
134
- version_requirements: *2161006680
134
+ version_requirements: *2161115780
135
135
  - !ruby/object:Gem::Dependency
136
136
  name: rr
137
- requirement: &2161005100 !ruby/object:Gem::Requirement
137
+ requirement: &2161115020 !ruby/object:Gem::Requirement
138
138
  none: false
139
139
  requirements:
140
140
  - - ! '>='
@@ -142,10 +142,10 @@ dependencies:
142
142
  version: '0'
143
143
  type: :development
144
144
  prerelease: false
145
- version_requirements: *2161005100
145
+ version_requirements: *2161115020
146
146
  - !ruby/object:Gem::Dependency
147
147
  name: steak
148
- requirement: &2161003840 !ruby/object:Gem::Requirement
148
+ requirement: &2161114300 !ruby/object:Gem::Requirement
149
149
  none: false
150
150
  requirements:
151
151
  - - ! '>='
@@ -153,10 +153,10 @@ dependencies:
153
153
  version: '0'
154
154
  type: :development
155
155
  prerelease: false
156
- version_requirements: *2161003840
156
+ version_requirements: *2161114300
157
157
  - !ruby/object:Gem::Dependency
158
158
  name: capybara
159
- requirement: &2161002700 !ruby/object:Gem::Requirement
159
+ requirement: &2161113340 !ruby/object:Gem::Requirement
160
160
  none: false
161
161
  requirements:
162
162
  - - ! '>='
@@ -164,10 +164,10 @@ dependencies:
164
164
  version: '0'
165
165
  type: :development
166
166
  prerelease: false
167
- version_requirements: *2161002700
167
+ version_requirements: *2161113340
168
168
  - !ruby/object:Gem::Dependency
169
169
  name: database_cleaner
170
- requirement: &2161001520 !ruby/object:Gem::Requirement
170
+ requirement: &2161112380 !ruby/object:Gem::Requirement
171
171
  none: false
172
172
  requirements:
173
173
  - - ! '>='
@@ -175,10 +175,10 @@ dependencies:
175
175
  version: '0'
176
176
  type: :development
177
177
  prerelease: false
178
- version_requirements: *2161001520
178
+ version_requirements: *2161112380
179
179
  - !ruby/object:Gem::Dependency
180
180
  name: memcache-client
181
- requirement: &2161000540 !ruby/object:Gem::Requirement
181
+ requirement: &2161111320 !ruby/object:Gem::Requirement
182
182
  none: false
183
183
  requirements:
184
184
  - - ! '>='
@@ -186,7 +186,7 @@ dependencies:
186
186
  version: 1.8.5
187
187
  type: :development
188
188
  prerelease: false
189
- version_requirements: *2161000540
189
+ version_requirements: *2161111320
190
190
  description: Crowd-sourced translation and localization engine for Rails.
191
191
  email:
192
192
  - michael@tr8n.net
@@ -588,6 +588,7 @@ files:
588
588
  - app/controllers/tr8n/admin/translation_controller.rb
589
589
  - app/controllers/tr8n/admin/translation_key_controller.rb
590
590
  - app/controllers/tr8n/admin/translator_controller.rb
591
+ - app/controllers/tr8n/api/v1/application_controller.rb
591
592
  - app/controllers/tr8n/api/v1/base_controller.rb
592
593
  - app/controllers/tr8n/api/v1/language_controller.rb
593
594
  - app/controllers/tr8n/api/v1/translation_controller.rb
@@ -1007,7 +1008,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
1007
1008
  version: '0'
1008
1009
  segments:
1009
1010
  - 0
1010
- hash: -3500293524521491792
1011
+ hash: -1543792512908225439
1011
1012
  required_rubygems_version: !ruby/object:Gem::Requirement
1012
1013
  none: false
1013
1014
  requirements:
@@ -1016,7 +1017,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1016
1017
  version: '0'
1017
1018
  segments:
1018
1019
  - 0
1019
- hash: -3500293524521491792
1020
+ hash: -1543792512908225439
1020
1021
  requirements: []
1021
1022
  rubyforge_project: tr8n
1022
1023
  rubygems_version: 1.8.11