tr8n 3.1.7 → 3.1.8

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.
@@ -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