phrasing 3.2.10 → 4.0.0rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +8 -11
- data/CHANGELOG.md +0 -20
- data/Gemfile +4 -1
- data/README-3.md +134 -0
- data/README.md +10 -14
- data/Release_notes_version_4.md +26 -0
- data/app/assets/javascripts/phrasing.js.erb +37 -17
- data/app/assets/stylesheets/phrasing_edit_mode_bubble.css.scss +3 -3
- data/app/controllers/phrasing_phrase_versions_controller.rb +4 -4
- data/app/controllers/phrasing_phrases_controller.rb +42 -75
- data/app/helpers/inline_helper.rb +24 -31
- data/app/models/phrasing_phrase.rb +29 -23
- data/app/views/phrasing/_production_warning.html.haml +0 -2
- data/app/views/phrasing_phrases/edit.html.haml +2 -2
- data/app/views/phrasing_phrases/import_export.html.haml +0 -5
- data/config/routes.rb +11 -9
- data/lib/generators/phrasing/phrasing_generator.rb +26 -0
- data/lib/generators/phrasing/templates/app/helpers/phrasing_helper.rb +12 -0
- data/lib/generators/phrasing/templates/config/initializers/phrasing.rb +11 -0
- data/{db/migrate/20131010101010_create_phrasing_phrase_versions.rb → lib/generators/phrasing/templates/db/migrate/create_phrasing_phrase_versions.rb} +2 -2
- data/{db/migrate/20120313191745_create_phrasing_phrases.rb → lib/generators/phrasing/templates/db/migrate/create_phrasing_phrases.rb} +2 -2
- data/lib/phrasing.rb +11 -37
- data/lib/phrasing/version.rb +2 -2
- data/phrasing.gemspec +1 -3
- data/spec/features/dummy_spec.rb +27 -24
- data/spec/features/phrasing_spec.rb +128 -84
- data/spec/lib/phrasing_spec.rb +50 -50
- metadata +14 -26
- data/4.0.0_changes.md +0 -1
- data/lib/phrasing/implementation.rb +0 -21
- data/lib/phrasing/simple.rb +0 -3
- data/lib/tasks/phrasing_tasks.rake +0 -69
@@ -1,9 +1,9 @@
|
|
1
|
-
class PhrasingPhraseVersionsController <
|
1
|
+
class PhrasingPhraseVersionsController < ActionController::Base
|
2
2
|
|
3
3
|
def destroy
|
4
|
-
|
5
|
-
|
6
|
-
redirect_to edit_phrasing_phrase_path(
|
4
|
+
phrase_version = PhrasingPhraseVersion.find(params[:id])
|
5
|
+
phrase_version.destroy
|
6
|
+
redirect_to edit_phrasing_phrase_path(phrase_version.phrasing_phrase.id)
|
7
7
|
end
|
8
8
|
|
9
9
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class PhrasingPhrasesController <
|
1
|
+
class PhrasingPhrasesController < ActionController::Base
|
2
2
|
|
3
3
|
layout 'phrasing'
|
4
4
|
|
@@ -8,18 +8,21 @@ class PhrasingPhrasesController < Phrasing.parent_controller.constantize
|
|
8
8
|
|
9
9
|
before_filter :authorize_editor
|
10
10
|
|
11
|
+
def import_export; end
|
12
|
+
def help; end
|
13
|
+
|
11
14
|
def index
|
12
15
|
params[:locale] ||= I18n.default_locale
|
13
|
-
query = PhrasingPhrase
|
14
|
-
query = query.
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@phrasing_phrases = query.where(key_like.or(value_like))
|
16
|
+
query = PhrasingPhrase.order("phrasing_phrases.key")
|
17
|
+
query = query.where(locale: params[:locale]) if params[:locale].present?
|
18
|
+
|
19
|
+
@phrasing_phrases = if params[:search].present?
|
20
|
+
key_like = PhrasingPhrase.arel_table[:key].matches("%#{params[:search]}%")
|
21
|
+
value_like = PhrasingPhrase.arel_table[:value].matches("%#{params[:search]}%")
|
22
|
+
query.where(key_like.or(value_like))
|
21
23
|
else
|
22
|
-
|
24
|
+
# because we want to have non nil values first.
|
25
|
+
query.where("value is not null") + query.where("value is null")
|
23
26
|
end
|
24
27
|
|
25
28
|
@locale_names = PhrasingPhrase.uniq.pluck(:locale)
|
@@ -30,24 +33,13 @@ class PhrasingPhrasesController < Phrasing.parent_controller.constantize
|
|
30
33
|
end
|
31
34
|
|
32
35
|
def update
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
respond_to do |format|
|
38
|
-
format.html do
|
39
|
-
redirect_to phrasing_phrases_path, notice: "#{@phrasing_phrase.key} updated!"
|
40
|
-
end
|
41
|
-
|
42
|
-
format.js do
|
43
|
-
render json: @phrasing_phrase
|
44
|
-
end
|
36
|
+
if request.xhr?
|
37
|
+
xhr_phrase_update
|
38
|
+
else
|
39
|
+
phrase_update
|
45
40
|
end
|
46
41
|
end
|
47
42
|
|
48
|
-
def import_export
|
49
|
-
end
|
50
|
-
|
51
43
|
def download
|
52
44
|
app_name = Rails.application.class.to_s.split("::").first
|
53
45
|
app_env = Rails.env
|
@@ -58,7 +50,7 @@ class PhrasingPhrasesController < Phrasing.parent_controller.constantize
|
|
58
50
|
def upload
|
59
51
|
number_of_changes = Phrasing::Serializer.import_yaml(params["file"].tempfile)
|
60
52
|
redirect_to phrasing_phrases_path, notice: "YAML file uploaded successfully! Number of phrases changed: #{number_of_changes}."
|
61
|
-
rescue
|
53
|
+
rescue => e
|
62
54
|
logger.info "\n#{e.class}\n#{e.message}"
|
63
55
|
message = if params[:file].nil?
|
64
56
|
"Please choose a file."
|
@@ -71,65 +63,40 @@ class PhrasingPhrasesController < Phrasing.parent_controller.constantize
|
|
71
63
|
end
|
72
64
|
|
73
65
|
def destroy
|
74
|
-
|
75
|
-
|
76
|
-
redirect_to phrasing_phrases_path, notice: "#{
|
66
|
+
phrasing_phrase = PhrasingPhrase.find(params[:id])
|
67
|
+
phrasing_phrase.destroy
|
68
|
+
redirect_to phrasing_phrases_path, notice: "#{phrasing_phrase.key} deleted!"
|
77
69
|
end
|
78
70
|
|
79
|
-
|
80
|
-
end
|
71
|
+
private
|
81
72
|
|
82
|
-
|
83
|
-
|
84
|
-
redirect_to :back, alert: "You didn't set your source server"
|
85
|
-
else
|
86
|
-
yaml = read_remote_yaml(Phrasing.staging_server_endpoint)
|
87
|
-
|
88
|
-
if yaml
|
89
|
-
Phrasing::Serializer.import_yaml(yaml)
|
90
|
-
redirect_to :back, notice: "Translations synced from source server"
|
91
|
-
else
|
92
|
-
redirect_to :back
|
93
|
-
end
|
94
|
-
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def remote_update_phrase
|
99
|
-
klass, attribute = params[:klass], params[:attribute]
|
100
|
-
|
101
|
-
if Phrasing.is_whitelisted?(klass, attribute)
|
102
|
-
class_object = klass.classify.constantize
|
103
|
-
@object = class_object.where(id: params[:id]).first
|
104
|
-
@object.send("#{attribute}=",params[:new_value])
|
105
|
-
@object.save!
|
106
|
-
render json: @object
|
107
|
-
else
|
108
|
-
render status: 403, text: "Attribute not whitelisted!"
|
73
|
+
def authorize_editor
|
74
|
+
redirect_to root_path unless can_edit_phrases?
|
109
75
|
end
|
110
76
|
|
111
|
-
|
112
|
-
|
113
|
-
end
|
77
|
+
def xhr_phrase_update
|
78
|
+
klass, attribute = params[:klass], params[:attribute]
|
114
79
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
rescue Exception => e
|
124
|
-
logger.fatal e
|
125
|
-
flash[:alert] = "Syncing failed: #{e}"
|
80
|
+
if Phrasing.is_whitelisted?(klass, attribute)
|
81
|
+
class_object = klass.classify.constantize
|
82
|
+
@object = class_object.where(id: params[:id]).first
|
83
|
+
@object.send("#{attribute}=",params[:new_value])
|
84
|
+
@object.save!
|
85
|
+
render json: @object
|
86
|
+
else
|
87
|
+
render status: 403, text: "Attribute not whitelisted!"
|
126
88
|
end
|
127
|
-
|
89
|
+
|
90
|
+
rescue ActiveRecord::RecordInvalid => e
|
91
|
+
render status: 403, text: e
|
128
92
|
end
|
129
93
|
|
94
|
+
def phrase_update
|
95
|
+
phrase = PhrasingPhrase.find(params[:id])
|
96
|
+
phrase.value = params[:phrasing_phrase][:value]
|
97
|
+
phrase.save!
|
130
98
|
|
131
|
-
|
132
|
-
redirect_to root_path unless can_edit_phrases?
|
99
|
+
redirect_to phrasing_phrases_path, notice: "#{phrase.key} updated!"
|
133
100
|
end
|
134
101
|
|
135
102
|
end
|
@@ -3,58 +3,51 @@ module InlineHelper
|
|
3
3
|
# phrase("headline", url: www.infinum.co/yabadaba, inverse: true, interpolation: {min: 15, max: 20}, scope: "models.errors")
|
4
4
|
|
5
5
|
# Data model phrase
|
6
|
-
# phrase(
|
6
|
+
# phrase(record, :title, inverse: true, class: phrase-record-title)
|
7
7
|
|
8
8
|
def phrase(*args)
|
9
9
|
if args[0].class == String or args[0].class == Symbol
|
10
10
|
key, options = args[0].to_s, args[1]
|
11
11
|
phrasing_phrase(key,options || {})
|
12
12
|
else
|
13
|
-
record,
|
14
|
-
inline(record,
|
13
|
+
record, attribute, options = args[0], args[1], args[2]
|
14
|
+
inline(record, attribute, options || {})
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
return record.send(field_name).to_s.html_safe unless can_edit_phrases?
|
18
|
+
private
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
klass += ' inverse' if options[:inverse]
|
24
|
-
klass += options[:class] if options[:class]
|
20
|
+
def inline(record, attribute, options={})
|
21
|
+
return uneditable_phrase(record, attribute, options) unless can_edit_phrases?
|
25
22
|
|
26
|
-
|
23
|
+
klass = 'phrasable phrasable_on'
|
24
|
+
klass += ' inverse' if options[:inverse]
|
25
|
+
klass += options[:class] if options[:class]
|
27
26
|
|
28
|
-
|
29
|
-
(record.send(field_name) || record.try(:key)).to_s.html_safe
|
30
|
-
end
|
31
|
-
end
|
27
|
+
url = phrasing_polymorphic_url(record, attribute)
|
32
28
|
|
33
|
-
|
34
|
-
|
35
|
-
|
29
|
+
content_tag(:span, { class: klass, contenteditable: true, spellcheck: false, "data-url" => url }) do
|
30
|
+
(record.send(attribute) || record.try(:key)).to_s.html_safe
|
31
|
+
end
|
32
|
+
end
|
36
33
|
|
37
34
|
def phrasing_phrase(key, options = {})
|
38
|
-
key = options[:scope] ? "#{options[:scope]}.#{key}" : key
|
39
|
-
|
40
|
-
|
41
|
-
inline(@record, :value, options)
|
42
|
-
else
|
43
|
-
options.try(:[], :interpolation) ? t(key, options[:interpolation]).html_safe : t(key).html_safe
|
44
|
-
end
|
35
|
+
key = options[:scope] ? "#{options[:scope]}.#{key}" : key
|
36
|
+
record = PhrasingPhrase.find_phrase(key)
|
37
|
+
inline(record, :value, options)
|
45
38
|
end
|
46
39
|
|
47
|
-
def
|
48
|
-
if
|
49
|
-
|
50
|
-
true
|
40
|
+
def uneditable_phrase(record, attribute, options={})
|
41
|
+
record_value = if options[:interpolation]
|
42
|
+
I18n.interpolate(record.send(attribute), options[:interpolation])
|
51
43
|
else
|
52
|
-
|
44
|
+
record.send(attribute)
|
53
45
|
end
|
46
|
+
record_value.to_s.html_safe
|
54
47
|
end
|
55
48
|
|
56
49
|
def phrasing_polymorphic_url(record, attribute)
|
57
|
-
|
50
|
+
phrasing_phrase_path(klass: record.class.to_s, id: record.id, attribute: attribute)
|
58
51
|
end
|
59
52
|
|
60
|
-
end
|
53
|
+
end
|
@@ -1,39 +1,45 @@
|
|
1
1
|
class PhrasingPhrase < ActiveRecord::Base
|
2
2
|
|
3
3
|
validates_presence_of :key, :locale
|
4
|
+
# validate :uniqueness_of_key_on_locale_scope, on: :create
|
5
|
+
validates_uniqueness_of :key, scope: [:locale]
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
has_many :versions, dependent: :destroy, class_name: "PhrasingPhraseVersion"
|
7
|
+
has_many :versions, dependent: :destroy, class_name: PhrasingPhraseVersion
|
8
8
|
|
9
9
|
after_update :version_it
|
10
10
|
|
11
|
-
def self.
|
12
|
-
|
13
|
-
value = I18n.t key, raise: true
|
14
|
-
PhrasingPhrase.where(key: key, locale: I18n.locale).first
|
15
|
-
rescue I18n::MissingTranslationData
|
16
|
-
create_phrase(key)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.create_phrase key, value = nil
|
21
|
-
phrasing_phrase = PhrasingPhrase.new
|
22
|
-
phrasing_phrase.locale = I18n.locale.to_s
|
23
|
-
phrasing_phrase.key = key.to_s
|
24
|
-
phrasing_phrase.value = value || key.to_s
|
25
|
-
phrasing_phrase.save
|
26
|
-
phrasing_phrase
|
11
|
+
def self.find_phrase(key)
|
12
|
+
where(key: key, locale: I18n.locale.to_s).first || search_i18n_and_create_phrase(key)
|
27
13
|
end
|
28
14
|
|
29
15
|
private
|
30
16
|
|
17
|
+
def self.search_i18n_and_create_phrase(key)
|
18
|
+
begin
|
19
|
+
value = I18n.t(key, raise: true)
|
20
|
+
create_phrase(key, value)
|
21
|
+
rescue I18n::MissingTranslationData
|
22
|
+
create_phrase(key)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.create_phrase(key, value=nil)
|
27
|
+
phrasing_phrase = PhrasingPhrase.new
|
28
|
+
phrasing_phrase.locale = I18n.locale.to_s
|
29
|
+
phrasing_phrase.key = key.to_s
|
30
|
+
phrasing_phrase.value = value || key.to_s
|
31
|
+
phrasing_phrase.save
|
32
|
+
phrasing_phrase
|
33
|
+
end
|
34
|
+
|
31
35
|
def uniqueness_of_key_on_locale_scope
|
32
|
-
|
36
|
+
if PhrasingPhrase.where(key: key, locale: locale).any?
|
37
|
+
errors.add(:key, "Duplicate entry #{key} for locale #{locale}")
|
38
|
+
end
|
33
39
|
end
|
34
40
|
|
35
41
|
def version_it
|
36
|
-
PhrasingPhraseVersion.create_version(id,
|
42
|
+
PhrasingPhraseVersion.create_version(id, value_was) if value_was != value
|
37
43
|
end
|
38
|
-
|
39
|
-
end
|
44
|
+
|
45
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
.edit
|
2
2
|
%h2= @phrasing_phrase.key
|
3
|
-
= link_to "Delete Phrase", phrasing_phrase_path(@phrasing_phrase), class: "btn btn-danger delete-phrase", method: :delete,
|
3
|
+
= link_to "Delete Phrase", phrasing_phrase_path(@phrasing_phrase), class: "btn btn-danger delete-phrase", method: :delete, onclick: "return confirm('Are you sure?');"
|
4
4
|
= form_for @phrasing_phrase, url: { action: "update" } do |f|
|
5
5
|
= f.text_area :value, rows: 12
|
6
6
|
= f.submit "Update", class: "btn btn-success submit-edit-phrase"
|
@@ -20,4 +20,4 @@
|
|
20
20
|
%td.phrasing-version-value= version.value.html_safe
|
21
21
|
%td.phrasing-version-created_at= version.created_at.strftime("%d-%m-%Y %H:%M:%S")
|
22
22
|
%td= link_to "Revert", phrasing_phrase_path(@phrasing_phrase.id, phrasing_phrase: {value: version.value}), class: "btn btn-success", method: :put
|
23
|
-
%td= link_to "Delete", phrasing_phrase_version_path(version.id), class: "btn btn-danger", method: :delete,
|
23
|
+
%td= link_to "Delete", phrasing_phrase_version_path(version.id), class: "btn btn-danger", method: :delete, onclick: "return confirm('Are you sure?');"
|
@@ -8,11 +8,6 @@
|
|
8
8
|
= file_field_tag "file"
|
9
9
|
= submit_tag "Upload"
|
10
10
|
|
11
|
-
- unless Phrasing.staging_server_endpoint.nil? || Rails.env.staging?
|
12
|
-
%h2 Staging syncing
|
13
|
-
%div
|
14
|
-
= link_to "Sync from staging server", sync_phrasing_phrases_path
|
15
|
-
|
16
11
|
%h2 Export
|
17
12
|
%div
|
18
13
|
= link_to "Download as YAML", download_phrasing_phrases_path
|
data/config/routes.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
Rails.application.routes.draw do
|
2
|
-
resources Phrasing.route,
|
2
|
+
resources Phrasing.route,
|
3
|
+
as: :phrasing_phrases,
|
4
|
+
controller: :phrasing_phrases,
|
5
|
+
only: [:index, :edit, :update, :destroy] do
|
3
6
|
collection do
|
4
|
-
get
|
5
|
-
get
|
6
|
-
get
|
7
|
-
|
8
|
-
post 'upload'
|
9
|
-
put 'remote_update_phrase'
|
7
|
+
get :help
|
8
|
+
get :import_export
|
9
|
+
get :download
|
10
|
+
post :upload
|
10
11
|
end
|
11
12
|
end
|
12
|
-
|
13
|
-
|
13
|
+
|
14
|
+
resources :phrasing_phrase_versions, only: :destroy
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class PhrasingGenerator < Rails::Generators::Base
|
2
|
+
include Rails::Generators::Migration
|
3
|
+
source_root File.expand_path('../templates', __FILE__)
|
4
|
+
|
5
|
+
def create_initializer_file
|
6
|
+
initializer_location = "config/initializers/phrasing.rb"
|
7
|
+
copy_file initializer_location, initializer_location
|
8
|
+
end
|
9
|
+
|
10
|
+
def create_helper_file
|
11
|
+
helper_location = "app/helpers/phrasing_helper.rb"
|
12
|
+
copy_file helper_location, helper_location
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_migrations
|
16
|
+
phrasing_phrase_migration = "db/migrate/create_phrasing_phrases.rb"
|
17
|
+
migration_template phrasing_phrase_migration, phrasing_phrase_migration
|
18
|
+
phrase_versions_migration = "db/migrate/create_phrasing_phrase_versions.rb"
|
19
|
+
migration_template phrase_versions_migration, phrase_versions_migration
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.next_migration_number(path)
|
23
|
+
sleep 1 # migration numbers should differentiate
|
24
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module PhrasingHelper
|
2
|
+
# You must implement the can_edit_phrases? method.
|
3
|
+
# Example:
|
4
|
+
#
|
5
|
+
# def can_edit_phrases?
|
6
|
+
# current_user.is_admin?
|
7
|
+
# end
|
8
|
+
|
9
|
+
def can_edit_phrases?
|
10
|
+
raise NotImplementedError.new("You must implement the can_edit_phrases? method")
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Phrasing.setup do |config|
|
2
|
+
config.route = 'phrasing'
|
3
|
+
|
4
|
+
# List all the model attributes you wish to edit with Phrasing, example:
|
5
|
+
# config.whitelist = ["Post.title", "Post.description"]
|
6
|
+
config.whitelist = []
|
7
|
+
|
8
|
+
# You can whitelist all models, but it's not recommended.
|
9
|
+
# Read here: https://github.com/infinum/phrasing#security
|
10
|
+
config.allow_update_on_all_models_and_attributes = false
|
11
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
class CreatePhrasingPhraseVersions < ActiveRecord::Migration
|
2
2
|
def change
|
3
|
-
create_table :phrasing_phrase_versions do |t|
|
3
|
+
create_table :phrasing_phrase_versions do |t|
|
4
4
|
t.integer :phrasing_phrase_id
|
5
5
|
t.text :value
|
6
6
|
t.timestamps
|
7
7
|
end
|
8
8
|
add_index :phrasing_phrase_versions, :phrasing_phrase_id
|
9
9
|
end
|
10
|
-
end
|
10
|
+
end
|