tr8n 3.1.6 → 3.1.7
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.
- data/Gemfile.lock +1 -1
- data/{local/tr8n_server/app/assets/stylesheets/admin.css → app/assets/stylesheets/tr8n/admin.css.scss} +0 -0
- data/app/assets/stylesheets/tr8n/components.css.scss +211 -0
- data/app/assets/stylesheets/tr8n/layout.css.scss +143 -0
- data/app/models/tr8n/language_rule.rb +31 -29
- data/app/models/tr8n/sync_log.rb +114 -19
- data/app/models/tr8n/translation.rb +43 -40
- data/app/models/tr8n/translation_key.rb +48 -18
- data/config/routes.rb +1 -1
- data/lib/generators/tr8n/templates/config/tr8n/config.yml +2 -2
- data/lib/generators/tr8n/templates/layouts/tr8n.html.erb +4 -1
- data/lib/generators/tr8n/templates/layouts/tr8n_admin.html.erb +5 -3
- data/lib/generators/tr8n/tr8n_generator.rb +1 -1
- data/lib/tasks/tr8n.rake +4 -2
- data/lib/tr8n/config.rb +1 -1
- data/lib/tr8n/version.rb +1 -1
- data/local/tr8n_server/app/assets/stylesheets/admin.css.scss +200 -0
- data/local/tr8n_server/app/assets/stylesheets/{application.css → application.css.scss} +0 -0
- metadata +43 -42
- data/app/controllers/tr8n/api/v1/sync_controller.rb +0 -30
- data/app/controllers/tr8n/api/v1/translator_controller.rb +0 -34
@@ -90,7 +90,7 @@ class Tr8n::Translation < ActiveRecord::Base
|
|
90
90
|
return nil if super_rules == nil
|
91
91
|
return nil unless super_rules.class.name == 'Array'
|
92
92
|
return nil if super_rules.size == 0
|
93
|
-
|
93
|
+
|
94
94
|
@loaded_rules ||= begin
|
95
95
|
rulz = []
|
96
96
|
super_rules.each do |rule|
|
@@ -118,43 +118,6 @@ class Tr8n::Translation < ActiveRecord::Base
|
|
118
118
|
end
|
119
119
|
end
|
120
120
|
|
121
|
-
# generates the hash without rule ids, but with full definitions
|
122
|
-
def rules_api_hash
|
123
|
-
@rules_api_hash ||= (rules || []).collect{|rule_hash| rule_hash[:rule].to_api_hash.merge(:token => rule_hash[:token])}
|
124
|
-
end
|
125
|
-
|
126
|
-
# serilaize translation to API hash to be used for synchronization
|
127
|
-
def to_api_hash
|
128
|
-
{:locale => language.locale, :label => label, :rank => rank, :rules => rules_api_hash}
|
129
|
-
end
|
130
|
-
|
131
|
-
# create translation from API hash for a specific key
|
132
|
-
def self.create_from_api_hash(tkey, translator, hash, opts = {})
|
133
|
-
return if hash[:label].blank? # don't add empty translations
|
134
|
-
lang = Tr8n::Language.for(hash[:locale])
|
135
|
-
return unless lang # don't add translations for an unsupported language
|
136
|
-
|
137
|
-
tkey.translations.each do |trn|
|
138
|
-
# if an identical translation exists, don't add it
|
139
|
-
return if trn.to_api_hash == hash
|
140
|
-
end
|
141
|
-
|
142
|
-
# generate rules for the translation
|
143
|
-
rules = nil
|
144
|
-
|
145
|
-
if hash[:rules].any?
|
146
|
-
hash[:rules].each do |rule_hash|
|
147
|
-
return unless rule_hash[:token] and rule_hash[:type] and rule_hash[:definition]
|
148
|
-
|
149
|
-
rule = Tr8n::LanguageRule.for_definition(lang, translator, rule_hash[:type], rule_hash[:definition], opts)
|
150
|
-
return unless rule # if the rule has not been created, we should not even add the translation
|
151
|
-
rules << {:token => rule_hash[:token], :rule_id => rule.id}
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
tkey.add_translation(hash[:label], rules, lang, translator)
|
156
|
-
end
|
157
|
-
|
158
121
|
# deprecated - api_hash should be used instead
|
159
122
|
def rules_definitions
|
160
123
|
return nil if rules.nil? or rules.empty?
|
@@ -257,9 +220,49 @@ class Tr8n::Translation < ActiveRecord::Base
|
|
257
220
|
end
|
258
221
|
|
259
222
|
###############################################################
|
260
|
-
##
|
223
|
+
## Synchronization Methods
|
224
|
+
###############################################################
|
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])}
|
228
|
+
end
|
229
|
+
|
230
|
+
# 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
|
234
|
+
hash
|
235
|
+
end
|
236
|
+
|
237
|
+
# 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?
|
241
|
+
|
242
|
+
lang = Tr8n::Language.for(hash["locale"])
|
243
|
+
# don't add translations for an unsupported language
|
244
|
+
return unless lang
|
245
|
+
|
246
|
+
# 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
|
+
|
254
|
+
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}
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
rules = nil if rules.empty?
|
260
|
+
tkey.add_translation(hash["label"], rules, lang, translator)
|
261
|
+
end
|
262
|
+
|
263
|
+
###############################################################
|
264
|
+
## Search Methods
|
261
265
|
###############################################################
|
262
|
-
|
263
266
|
def self.filter_status_options
|
264
267
|
[["all translations", "all"],
|
265
268
|
["accepted translations", "accepted"],
|
@@ -68,6 +68,7 @@ class Tr8n::TranslationKey < ActiveRecord::Base
|
|
68
68
|
:locale => locale,
|
69
69
|
:level => level,
|
70
70
|
:admin => Tr8n::Config.block_options[:admin])
|
71
|
+
|
71
72
|
unless options[:source].blank?
|
72
73
|
# at the time of creation - mark the first source of the key
|
73
74
|
Tr8n::TranslationKeySource.find_or_create(new_tkey, Tr8n::TranslationSource.find_or_create(options[:source], options[:url]))
|
@@ -218,8 +219,10 @@ class Tr8n::TranslationKey < ActiveRecord::Base
|
|
218
219
|
|
219
220
|
def add_translation(label, rules = nil, language = Tr8n::Config.current_language, translator = Tr8n::Config.current_translator)
|
220
221
|
raise Tr8n::Exception.new("The sentence contains dirty words") unless language.clean_sentence?(label)
|
221
|
-
|
222
|
-
|
222
|
+
pp rules
|
223
|
+
translation = Tr8n::Translation.create(:translation_key => self, :language => language, :translator => translator, :label => label, :rules => rules)
|
224
|
+
pp translation
|
225
|
+
|
223
226
|
translation.vote!(translator, 1)
|
224
227
|
translation
|
225
228
|
end
|
@@ -524,35 +527,62 @@ class Tr8n::TranslationKey < ActiveRecord::Base
|
|
524
527
|
Tr8n::Cache.delete("translation_key_#{key}")
|
525
528
|
end
|
526
529
|
|
527
|
-
|
530
|
+
###############################################################
|
531
|
+
## Synchronization Methods
|
532
|
+
###############################################################
|
533
|
+
def mark_as_synced!
|
534
|
+
update_attributes(:synced_at => Time.now + 5.seconds)
|
535
|
+
end
|
536
|
+
|
537
|
+
def to_sync_hash(default_translation_hashes = nil)
|
528
538
|
{
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
539
|
+
"key" => self.key,
|
540
|
+
"label" => self.label,
|
541
|
+
"description" => self.description,
|
542
|
+
"locale" => (locale || Tr8n::Config.default_locale),
|
543
|
+
"translations" => default_translation_hashes || translations_for(nil, Tr8n::Config.translation_threshold).collect{|t| t.to_sync_hash}
|
534
544
|
}
|
535
545
|
end
|
536
546
|
|
547
|
+
def transations_sync_hashes
|
548
|
+
@transations_sync_hashes ||= translations.collect{|t| t.to_sync_hash(false)}
|
549
|
+
end
|
550
|
+
|
537
551
|
# create translation key from API hash
|
538
|
-
def self.
|
539
|
-
return if tkey_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?
|
540
554
|
|
541
|
-
tkey = Tr8n::TranslationKey.find_or_create(tkey_hash[
|
555
|
+
tkey = Tr8n::TranslationKey.find_or_create(tkey_hash["label"], tkey_hash["description"])
|
542
556
|
|
543
557
|
# return unless tkey.key==tkey_hash[:key] # need to warn the user that the key methods don't match
|
544
558
|
|
545
|
-
opts[:force_create] = Tr8n::Config.synchronization_create_rules? if opts[:force_create].nil?
|
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
|
546
563
|
|
547
|
-
|
548
|
-
|
564
|
+
added_trans = []
|
565
|
+
(tkey_hash["translations"] || []).each do |t_hash|
|
566
|
+
# 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
|
+
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)
|
549
577
|
end
|
550
578
|
|
551
|
-
|
579
|
+
# need to send back translations that have not been added, but exist in the system
|
580
|
+
# pp :after, remaining_sync_hashes
|
581
|
+
[tkey, remaining_translations]
|
552
582
|
end
|
553
583
|
|
554
584
|
###############################################################
|
555
|
-
## Feature
|
585
|
+
## Feature Methods
|
556
586
|
###############################################################
|
557
587
|
|
558
588
|
def self.title
|
@@ -580,7 +610,7 @@ class Tr8n::TranslationKey < ActiveRecord::Base
|
|
580
610
|
end
|
581
611
|
|
582
612
|
###############################################################
|
583
|
-
## Search
|
613
|
+
## Search Methods
|
584
614
|
###############################################################
|
585
615
|
|
586
616
|
def self.filter_phrase_type_options
|
@@ -641,6 +671,6 @@ class Tr8n::TranslationKey < ActiveRecord::Base
|
|
641
671
|
results = results.where("tr8n_translation_keys.id not in (select tr8n_translation_key_locks.translation_key_id from tr8n_translation_key_locks where tr8n_translation_key_locks.language_id = ? and tr8n_translation_key_locks.locked = ?)", Tr8n::Config.current_language.id, true)
|
642
672
|
end
|
643
673
|
|
644
|
-
results
|
674
|
+
results.order("created_at desc")
|
645
675
|
end
|
646
676
|
end
|
data/config/routes.rb
CHANGED
@@ -265,8 +265,8 @@ defaults:
|
|
265
265
|
#############################################################################
|
266
266
|
synchronization:
|
267
267
|
server: "http://tr8n.net" # alternative, regional locations will be available in the future
|
268
|
-
key: "
|
269
|
-
secret: "
|
268
|
+
key: "YOUR APP KEY" # replace this with your key
|
269
|
+
secret: "YOUR APP SECRET" # replace this with your secret
|
270
270
|
batch_size: 50 # how many kesy to send to the server at a time
|
271
271
|
create_rules: true # force rules creation, or skip translations for rules that don't exist
|
272
272
|
all_languages: false # use only enabled languages, or all languages
|
@@ -11,6 +11,9 @@
|
|
11
11
|
<div class="page_head">
|
12
12
|
<div class="page_fixed">
|
13
13
|
<div class="mrgn_h">
|
14
|
+
<ul class="flt_r horiz_list top_nav">
|
15
|
+
<li><%= tr8n_language_selector_tag(:lightbox=>false) %></li>
|
16
|
+
</ul>
|
14
17
|
<h1 class="logo"><%=tr("Tr8n Translation Engine", "Application title")%></h1>
|
15
18
|
</div>
|
16
19
|
</div>
|
@@ -37,7 +40,7 @@
|
|
37
40
|
<li><%=link_to(tr("Credits"), "/tr8n/help/credits", :class => "quiet")%></li>
|
38
41
|
<li><%=link_to(tr("License"), "/tr8n/help/license", :class => "quiet")%></li>
|
39
42
|
</ul>
|
40
|
-
© Copyright 2010 -
|
43
|
+
© Copyright 2010 - 2012 tr8n.net
|
41
44
|
</div>
|
42
45
|
|
43
46
|
<div style="padding:15px;">
|
@@ -23,8 +23,10 @@
|
|
23
23
|
<div class="page_head">
|
24
24
|
<div class="page_fixed">
|
25
25
|
<div class="mrgn_h">
|
26
|
-
|
27
|
-
|
26
|
+
<ul class="flt_r horiz_list top_nav">
|
27
|
+
<li><%= tr8n_language_selector_tag(:lightbox=>false) %></li>
|
28
|
+
</ul>
|
29
|
+
<h1 class="logo"><%=link_to(tr("Tr8n Translation Engine", "Application title"), "/", :style=>"text-decorations:none;")%></h1>
|
28
30
|
</div>
|
29
31
|
</div>
|
30
32
|
</div>
|
@@ -49,7 +51,7 @@
|
|
49
51
|
<li><%=link_to(tr("Credits"), "/tr8n/help/credits", :class => "quiet")%></li>
|
50
52
|
<li><%=link_to(tr("License"), "/tr8n/help/license", :class => "quiet")%></li>
|
51
53
|
</ul>
|
52
|
-
© Copyright 2010 -
|
54
|
+
© Copyright 2010 - 2012 tr8n.net
|
53
55
|
</div>
|
54
56
|
|
55
57
|
<div style="padding:15px;">
|
@@ -45,7 +45,7 @@ class Tr8nGenerator < Rails::Generators::Base
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def copy_configuration
|
48
|
-
config_source = File.expand_path("#{self.class.source_root}/config
|
48
|
+
config_source = File.expand_path("#{self.class.source_root}/config/tr8n", __FILE__)
|
49
49
|
system "rsync -ruv #{config_source} #{Rails.root}/config"
|
50
50
|
end
|
51
51
|
|
data/lib/tasks/tr8n.rake
CHANGED
@@ -85,8 +85,10 @@ namespace :tr8n do
|
|
85
85
|
Tr8n::IpLocation.import_from_file('config/tr8n/data/ip_locations.csv', :verbose => true)
|
86
86
|
end
|
87
87
|
|
88
|
-
desc "
|
88
|
+
desc "Synchronize translations with tr8n.net"
|
89
89
|
task :sync => :environment do
|
90
|
-
|
90
|
+
opts = {}
|
91
|
+
opts[:force] = true if ENV["force"] == "true"
|
92
|
+
Tr8n::SyncLog.sync(opts)
|
91
93
|
end
|
92
94
|
end
|
data/lib/tr8n/config.rb
CHANGED
@@ -532,7 +532,7 @@ module Tr8n
|
|
532
532
|
|
533
533
|
def self.language_rule_dependencies
|
534
534
|
@language_rule_dependencies ||= begin
|
535
|
-
depts =
|
535
|
+
depts = HashWithIndifferentAccess.new
|
536
536
|
language_rule_classes.each do |cls|
|
537
537
|
if depts[cls.dependency]
|
538
538
|
raise Tr8n::Exception.new("The same dependency key #{cls.dependency} has been registered for multiple rules. This is not allowed.")
|
data/lib/tr8n/version.rb
CHANGED
@@ -0,0 +1,200 @@
|
|
1
|
+
/****************************************/
|
2
|
+
/*** Classes for the admin user page
|
3
|
+
/****************************************/
|
4
|
+
@mixin rounded-corners($radius) {
|
5
|
+
border-radius: $radius;
|
6
|
+
-moz-border-radius: $radius;
|
7
|
+
-webkit-border-radius: $radius;
|
8
|
+
}
|
9
|
+
|
10
|
+
@mixin shadow($info) {
|
11
|
+
box-shadow:$info;
|
12
|
+
-moz-box-shadow:$info;
|
13
|
+
-webkit-box-shadow:$info;
|
14
|
+
}
|
15
|
+
|
16
|
+
@import "layout.css.scss";
|
17
|
+
@import "components.css.scss";
|
18
|
+
|
19
|
+
.pagination {
|
20
|
+
text-align:left !important;
|
21
|
+
}
|
22
|
+
|
23
|
+
.admin_table {
|
24
|
+
width: 100%;
|
25
|
+
font-size: 12px;
|
26
|
+
border: 1px #e5e5e5 solid;
|
27
|
+
border-collapse: collapse;
|
28
|
+
background-color: white;
|
29
|
+
margin-bottom: 10px;
|
30
|
+
}
|
31
|
+
|
32
|
+
.admin_table th {
|
33
|
+
background-color: #efefef;
|
34
|
+
border: none;
|
35
|
+
border: 1px #e5e5e5 solid;
|
36
|
+
font-weight:bold;
|
37
|
+
}
|
38
|
+
|
39
|
+
.admin_table tr:hover {
|
40
|
+
background-color: #eee;
|
41
|
+
}
|
42
|
+
|
43
|
+
.admin_table td {
|
44
|
+
padding: 5px;
|
45
|
+
border: 1px solid #e5e5e5;
|
46
|
+
}
|
47
|
+
|
48
|
+
.admin_table td.numeric,
|
49
|
+
.admin_table th.numeric {
|
50
|
+
text-align: right;
|
51
|
+
}
|
52
|
+
|
53
|
+
.admin_table span.description {
|
54
|
+
color: #aaaaaa;
|
55
|
+
font-weight: normal;
|
56
|
+
font-size: 11px;
|
57
|
+
}
|
58
|
+
|
59
|
+
.admin_table td.key {
|
60
|
+
vertical-align:top;
|
61
|
+
width: 33%;
|
62
|
+
color: #666666;
|
63
|
+
font-weight: bold;
|
64
|
+
text-align: right;
|
65
|
+
padding-left: 10px;
|
66
|
+
}
|
67
|
+
|
68
|
+
.admin_table td.value {
|
69
|
+
vertical-align:top;
|
70
|
+
}
|
71
|
+
|
72
|
+
.admin_table td.value ul {
|
73
|
+
margin-top:0px;
|
74
|
+
margin-left:1.5em;
|
75
|
+
}
|
76
|
+
|
77
|
+
.admin_table td.value li {
|
78
|
+
line-height:1em;
|
79
|
+
margin-top:8px;
|
80
|
+
margin-left:0;
|
81
|
+
text-indent:-0.25em;
|
82
|
+
}
|
83
|
+
|
84
|
+
.admin_table td.value .detail {
|
85
|
+
font-size:11px;
|
86
|
+
color:#666;
|
87
|
+
}
|
88
|
+
|
89
|
+
.admin_table td .action_link {
|
90
|
+
float:right;
|
91
|
+
font-size:11px;
|
92
|
+
}
|
93
|
+
|
94
|
+
.admin_table p.status {
|
95
|
+
font-weight:bold;
|
96
|
+
}
|
97
|
+
|
98
|
+
.admin_table p.closed {
|
99
|
+
color:Red;
|
100
|
+
}
|
101
|
+
|
102
|
+
.admin_table p.claimed {
|
103
|
+
color:Green;
|
104
|
+
}
|
105
|
+
|
106
|
+
.admin_table .obscured {
|
107
|
+
background-color:Black;
|
108
|
+
padding:2px 6px;
|
109
|
+
}
|
110
|
+
|
111
|
+
.admin_table .obscured:hover {
|
112
|
+
background-color:#eee;
|
113
|
+
}
|
114
|
+
|
115
|
+
.admin_actions {
|
116
|
+
width: 100%;
|
117
|
+
background-color: #fffeee;
|
118
|
+
border: 1px #e5e5e5 solid;
|
119
|
+
margin-bottom: 10px;
|
120
|
+
}
|
121
|
+
|
122
|
+
.admin_actions th {
|
123
|
+
border: 1px #e5e5e5 solid;
|
124
|
+
font-weight:bold;
|
125
|
+
}
|
126
|
+
|
127
|
+
.admin_actions td {
|
128
|
+
padding: 5px;
|
129
|
+
}
|
130
|
+
|
131
|
+
.admin_actions td a {
|
132
|
+
text-decoration: none;
|
133
|
+
display:block;
|
134
|
+
display:inline-block;
|
135
|
+
width: 100%;
|
136
|
+
}
|
137
|
+
|
138
|
+
.admin_actions td a:hover {
|
139
|
+
text-decoration: underline;
|
140
|
+
}
|
141
|
+
|
142
|
+
.admin_table .expand_link {
|
143
|
+
float:right;
|
144
|
+
}
|
145
|
+
|
146
|
+
.admin_footer_edit {font-size:11px;margin-left:10px;margin-bottom:10px;font-weight:normal;height:20px;padding:5px;background-color:#feffef;border:solid 1px #e4e3c9;}
|
147
|
+
|
148
|
+
#subtabs {
|
149
|
+
margin-bottom: 1em;
|
150
|
+
}
|
151
|
+
|
152
|
+
#date_container {
|
153
|
+
margin-bottom: 1em;
|
154
|
+
font-weight: bold;
|
155
|
+
}
|
156
|
+
|
157
|
+
#totals {
|
158
|
+
border: 2px solid #ccc;
|
159
|
+
font-size: 24px;
|
160
|
+
font-weight: bold;
|
161
|
+
}
|
162
|
+
|
163
|
+
#totals td, th {
|
164
|
+
padding: 5px;
|
165
|
+
border: 1px solid #999;
|
166
|
+
}
|
167
|
+
|
168
|
+
.hourly.breakdown th {
|
169
|
+
text-align: center;
|
170
|
+
}
|
171
|
+
|
172
|
+
a {
|
173
|
+
text-decoration: none;
|
174
|
+
}
|
175
|
+
|
176
|
+
a:hover {
|
177
|
+
text-decoration: underline;
|
178
|
+
}
|
179
|
+
|
180
|
+
.admin_menu_item {
|
181
|
+
padding-left:15px;
|
182
|
+
padding-bottom:3px;
|
183
|
+
text-align:left;
|
184
|
+
width:90%;
|
185
|
+
border-right:1px solid #ccc;
|
186
|
+
}
|
187
|
+
|
188
|
+
.admin_menu_item_selected {
|
189
|
+
padding-left:15px;
|
190
|
+
text-align:left;
|
191
|
+
width:90%;
|
192
|
+
border-top:1px solid #ccc;
|
193
|
+
border-bottom:1px solid #ccc;
|
194
|
+
border-left:1px solid #ccc;
|
195
|
+
border-right:0px;
|
196
|
+
}
|
197
|
+
|
198
|
+
.admin_menu_item_selected a {
|
199
|
+
color:black;
|
200
|
+
}
|