diff --git a/Gemfile b/Gemfile index 7b12d5c03..138fa838b 100644 --- a/Gemfile +++ b/Gemfile @@ -33,6 +33,11 @@ gem 'resque-restriction' #gem 'rb-readline' gem 'ruby-debug19' gem 'rubyzip' + +gem 'sunspot_mongo' +gem 'sunspot_solr' +gem 'progress_bar' + gem 'sinatra' gem 'sprockets' gem 'tinymce-rails' diff --git a/Gemfile.lock b/Gemfile.lock index c7dcecaa2..31bdf08a4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -97,7 +97,9 @@ GEM factory_girl (~> 2.6.0) railties (>= 3.0.0) fastercsv (1.5.4) + fattr (2.2.1) haml (3.1.4) + highline (1.6.13) hike (1.2.1) hoe (2.16.1) rake (~> 0.8) @@ -136,12 +138,17 @@ GEM net-ldap (0.3.1) nokogiri (1.5.2) nokogiri (1.5.2-x86-mingw32) + options (2.3.0) + fattr orm_adapter (0.0.6) pdf-writer (1.1.8) color (>= 1.4.0) transaction-simple (~> 1.3) polyglot (0.3.3) pr_geohash (1.0.0) + progress_bar (0.4.0) + highline (~> 1.6.1) + options (~> 2.3.0) rack (1.3.6) rack-cache (1.2) rack (>= 0.4) @@ -256,9 +263,12 @@ GEM rsolr (~> 1.0.7) sunspot-rails-tester (0.0.4) sunspot_rails (~> 1.2) + sunspot_mongo (1.0.1) + sunspot_rails sunspot_rails (1.3.3) nokogiri sunspot (= 1.3.3) + sunspot_solr (1.3.3) thor (0.14.6) tilt (1.3.3) tinymce-rails (3.4.8) @@ -314,6 +324,7 @@ DEPENDENCIES mysql2 net-ldap (~> 0.3.1) nokogiri + progress_bar radius rails (>= 3.1.0, < 3.2.0) rake @@ -335,6 +346,8 @@ DEPENDENCIES spork sprockets sunspot-rails-tester + sunspot_mongo + sunspot_solr tinymce-rails uglifier watchr diff --git a/config/initializers/redis_search.rb b/config/initializers/redis_search.rb deleted file mode 100644 index 76971b97c..000000000 --- a/config/initializers/redis_search.rb +++ /dev/null @@ -1,18 +0,0 @@ -require "redis" -require "redis-namespace" -require "redis-search" -# don't forget change namespace -redis = Redis.new(:host => "127.0.0.1",:port => "6379") -# We suggest you use a special db in Redis, when you need to clear all data, you can use flushdb command to clear them. -redis.select(3) -# Give a special namespace as prefix for Redis key, when your have more than one project used redis-search, this config will make them work fine. -redis = Redis::Namespace.new("orbit_nccu:redis_search2", :redis => redis) -Redis::Search.configure do |config| - config.redis = redis - config.complete_max_length = 100 - #config.pinyin_match = true - # use rmmseg, true to disable it, it can save memroy - config.disable_rmmseg = false -end -# Bulletin.new -# NewsBulletin.new \ No newline at end of file diff --git a/config/sunspot.yml b/config/sunspot.yml index 79867b7a6..7bbd32c0f 100644 --- a/config/sunspot.yml +++ b/config/sunspot.yml @@ -7,11 +7,11 @@ production: development: solr: hostname: localhost - port: 8983 + port: 8982 log_level: INFO test: solr: hostname: localhost - port: 8983 + port: 8981 log_level: WARNING \ No newline at end of file diff --git a/lib/tasks/matt_dev.rake b/lib/tasks/matt_dev.rake index c82606c23..0a9f71040 100644 --- a/lib/tasks/matt_dev.rake +++ b/lib/tasks/matt_dev.rake @@ -6,9 +6,22 @@ namespace :matt_dev do p a a= Redis::Search.query("NewsBulletin",'社科院',:conditions =>{:is_checked=>true,:is_hidden=>false}) p a - # p Pinyin.t('台灣不是中國的一部分') end - + + task :searching_02 => :environment do + a= Bulletin.solr_search do #("Bulletin",'我',:conditions =>{:is_checked=>true,:is_hidden=>false}) + fulltext '我' + with(:frontend_search,true) + #with(:is_hidden,true) + end + p a.results + a= NewsBulletin.solr_search do #("NewsBulletin",'社科院',:conditions =>{:is_checked=>true,:is_hidden=>false}) + fulltext '社科院' + with(:frontend_search,true) + #with(:is_hidden,true) + end + p a.results + end task :testing_new_tag_cal do ranges = [[1],[1,2],[1,2,3],[1,2,3,4],[1,2,3,4,5],[1,2,3,4,5,6],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7,8],[1,2,3,4,5,6,7,8,9],[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8,9,10,11],[1,2,3,4,5,6,7,8,9,10,11,12],[-1,-2,-3,-4,5,6,7,8,9,10,11,12]] diff --git a/solr/conf/admin-extra.html b/solr/conf/admin-extra.html new file mode 100644 index 000000000..aa739da86 --- /dev/null +++ b/solr/conf/admin-extra.html @@ -0,0 +1,31 @@ + + + diff --git a/solr/conf/elevate.xml b/solr/conf/elevate.xml new file mode 100644 index 000000000..7630ebe20 --- /dev/null +++ b/solr/conf/elevate.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + diff --git a/solr/conf/mapping-ISOLatin1Accent.txt b/solr/conf/mapping-ISOLatin1Accent.txt new file mode 100644 index 000000000..ede774258 --- /dev/null +++ b/solr/conf/mapping-ISOLatin1Accent.txt @@ -0,0 +1,246 @@ +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Syntax: +# "source" => "target" +# "source".length() > 0 (source cannot be empty.) +# "target".length() >= 0 (target can be empty.) + +# example: +# "À" => "A" +# "\u00C0" => "A" +# "\u00C0" => "\u0041" +# "ß" => "ss" +# "\t" => " " +# "\n" => "" + +# À => A +"\u00C0" => "A" + +# Á => A +"\u00C1" => "A" + +#  => A +"\u00C2" => "A" + +# à => A +"\u00C3" => "A" + +# Ä => A +"\u00C4" => "A" + +# Å => A +"\u00C5" => "A" + +# Æ => AE +"\u00C6" => "AE" + +# Ç => C +"\u00C7" => "C" + +# È => E +"\u00C8" => "E" + +# É => E +"\u00C9" => "E" + +# Ê => E +"\u00CA" => "E" + +# Ë => E +"\u00CB" => "E" + +# Ì => I +"\u00CC" => "I" + +# Í => I +"\u00CD" => "I" + +# Î => I +"\u00CE" => "I" + +# Ï => I +"\u00CF" => "I" + +# IJ => IJ +"\u0132" => "IJ" + +# Ð => D +"\u00D0" => "D" + +# Ñ => N +"\u00D1" => "N" + +# Ò => O +"\u00D2" => "O" + +# Ó => O +"\u00D3" => "O" + +# Ô => O +"\u00D4" => "O" + +# Õ => O +"\u00D5" => "O" + +# Ö => O +"\u00D6" => "O" + +# Ø => O +"\u00D8" => "O" + +# Œ => OE +"\u0152" => "OE" + +# Þ +"\u00DE" => "TH" + +# Ù => U +"\u00D9" => "U" + +# Ú => U +"\u00DA" => "U" + +# Û => U +"\u00DB" => "U" + +# Ü => U +"\u00DC" => "U" + +# Ý => Y +"\u00DD" => "Y" + +# Ÿ => Y +"\u0178" => "Y" + +# à => a +"\u00E0" => "a" + +# á => a +"\u00E1" => "a" + +# â => a +"\u00E2" => "a" + +# ã => a +"\u00E3" => "a" + +# ä => a +"\u00E4" => "a" + +# å => a +"\u00E5" => "a" + +# æ => ae +"\u00E6" => "ae" + +# ç => c +"\u00E7" => "c" + +# è => e +"\u00E8" => "e" + +# é => e +"\u00E9" => "e" + +# ê => e +"\u00EA" => "e" + +# ë => e +"\u00EB" => "e" + +# ì => i +"\u00EC" => "i" + +# í => i +"\u00ED" => "i" + +# î => i +"\u00EE" => "i" + +# ï => i +"\u00EF" => "i" + +# ij => ij +"\u0133" => "ij" + +# ð => d +"\u00F0" => "d" + +# ñ => n +"\u00F1" => "n" + +# ò => o +"\u00F2" => "o" + +# ó => o +"\u00F3" => "o" + +# ô => o +"\u00F4" => "o" + +# õ => o +"\u00F5" => "o" + +# ö => o +"\u00F6" => "o" + +# ø => o +"\u00F8" => "o" + +# œ => oe +"\u0153" => "oe" + +# ß => ss +"\u00DF" => "ss" + +# þ => th +"\u00FE" => "th" + +# ù => u +"\u00F9" => "u" + +# ú => u +"\u00FA" => "u" + +# û => u +"\u00FB" => "u" + +# ü => u +"\u00FC" => "u" + +# ý => y +"\u00FD" => "y" + +# ÿ => y +"\u00FF" => "y" + +# ff => ff +"\uFB00" => "ff" + +# fi => fi +"\uFB01" => "fi" + +# fl => fl +"\uFB02" => "fl" + +# ffi => ffi +"\uFB03" => "ffi" + +# ffl => ffl +"\uFB04" => "ffl" + +# ſt => ft +"\uFB05" => "ft" + +# st => st +"\uFB06" => "st" diff --git a/solr/conf/protwords.txt b/solr/conf/protwords.txt new file mode 100644 index 000000000..1dfc0abec --- /dev/null +++ b/solr/conf/protwords.txt @@ -0,0 +1,21 @@ +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#----------------------------------------------------------------------- +# Use a protected word file to protect against the stemmer reducing two +# unrelated words to the same base word. + +# Some non-words that normally won't be encountered, +# just to test that they won't be stemmed. +dontstems +zwhacky + diff --git a/solr/conf/schema.xml b/solr/conf/schema.xml new file mode 100644 index 000000000..17a751ad0 --- /dev/null +++ b/solr/conf/schema.xml @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id + + text + + + + diff --git a/solr/conf/scripts.conf b/solr/conf/scripts.conf new file mode 100644 index 000000000..f58b262ae --- /dev/null +++ b/solr/conf/scripts.conf @@ -0,0 +1,24 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +user= +solr_hostname=localhost +solr_port=8983 +rsyncd_port=18983 +data_dir= +webapp_name=solr +master_host= +master_data_dir= +master_status_dir= diff --git a/solr/conf/solrconfig.xml b/solr/conf/solrconfig.xml new file mode 100644 index 000000000..65c196073 --- /dev/null +++ b/solr/conf/solrconfig.xml @@ -0,0 +1,934 @@ + + + + + + ${solr.abortOnConfigurationError:true} + + + + + + + + + + + + + ${solr.data.dir:./solr/data} + + + + false + 10 + + + + 32 + + 10000 + 1000 + 10000 + + + + + + + + native + + + + + + false + 32 + 10 + + + + + + false + + true + + + + + + 1 + + 0 + + + + false + + + + + + + + + + + + + + + + + 1024 + + + + + + + + + + true + + + + + 20 + + 200 + + + + + + + + + + + + solr rocks + 0 + 10 + + + static firstSearcher warming query from solrconfig.xml + + + + + false + + 2 + + + + + + + + + + + + + + + + + + explicit + + + + + + + + + + + dismax + explicit + 0.01 + + text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4 + + + text^0.2 features^1.1 name^1.5 manu^1.4 manu_exact^1.9 + + + popularity^0.5 recip(price,1,1000,1000)^0.3 + + + id,name,price,score + + + 2<-1 5<-2 6<90% + + 100 + *:* + + text features name + + 0 + + name + regex + + + + + + + dismax + explicit + text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 + 2<-1 5<-2 6<90% + + incubationdate_dt:[* TO NOW/DAY-1MONTH]^2.2 + + + + inStock:true + + + + cat + manu_exact + price:[* TO 500] + price:[500 TO *] + + + + + + textSpell + + default + name + ./spellchecker + + + + + + + + + false + + false + + 1 + + + spellcheck + + + + + + + true + + + tvComponent + + + + + + + + default + + org.carrot2.clustering.lingo.LingoClusteringAlgorithm + + 20 + + + stc + org.carrot2.clustering.stc.STCClusteringAlgorithm + + + + + true + default + true + + name + id + + features + + true + + + + false + + + clusteringComponent + + + + + + + text + true + ignored_ + + true + links + ignored_ + + + + + + + true + + + termsComponent + + + + + + string + elevate.xml + + + + + explicit + + + elevator + + + + + + + + + + + + + + + + + + standard + solrpingquery + all + + + + + + explicit + + true + + + + + + + + 100 + + + + + + + 70 + + 0.5 + + [-\w ,/\n\"']{20,200} + + + + + + ]]> + ]]> + + + + + + + + + 5 + + + + + + solr + + + + + 1 + 2 + + + diff --git a/solr/conf/spellings.txt b/solr/conf/spellings.txt new file mode 100644 index 000000000..d7ede6f56 --- /dev/null +++ b/solr/conf/spellings.txt @@ -0,0 +1,2 @@ +pizza +history \ No newline at end of file diff --git a/solr/conf/stopwords.txt b/solr/conf/stopwords.txt new file mode 100644 index 000000000..b5824da32 --- /dev/null +++ b/solr/conf/stopwords.txt @@ -0,0 +1,58 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#----------------------------------------------------------------------- +# a couple of test stopwords to test that the words are really being +# configured from this file: +stopworda +stopwordb + +#Standard english stop words taken from Lucene's StopAnalyzer +a +an +and +are +as +at +be +but +by +for +if +in +into +is +it +no +not +of +on +or +s +such +t +that +the +their +then +there +these +they +this +to +was +will +with + diff --git a/solr/conf/synonyms.txt b/solr/conf/synonyms.txt new file mode 100644 index 000000000..b0e31cb7e --- /dev/null +++ b/solr/conf/synonyms.txt @@ -0,0 +1,31 @@ +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#----------------------------------------------------------------------- +#some test synonym mappings unlikely to appear in real input text +aaa => aaaa +bbb => bbbb1 bbbb2 +ccc => cccc1,cccc2 +a\=>a => b\=>b +a\,a => b\,b +fooaaa,baraaa,bazaaa + +# Some synonym groups specific to this example +GB,gib,gigabyte,gigabytes +MB,mib,megabyte,megabytes +Television, Televisions, TV, TVs +#notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming +#after us won't split it into two words. + +# Synonym mappings can be used for spelling correction too +pixima => pixma + diff --git a/vendor/built_in_modules/announcement/app/controllers/panel/announcement/front_end/bulletins_controller.rb b/vendor/built_in_modules/announcement/app/controllers/panel/announcement/front_end/bulletins_controller.rb index 685eaa248..d46cbc9f2 100644 --- a/vendor/built_in_modules/announcement/app/controllers/panel/announcement/front_end/bulletins_controller.rb +++ b/vendor/built_in_modules/announcement/app/controllers/panel/announcement/front_end/bulletins_controller.rb @@ -13,8 +13,12 @@ class Panel::Announcement::FrontEnd::BulletinsController < OrbitWidgetController if !params[:search_query].blank? search_cond = {:is_checked=>true,:is_hidden=>false,:is_pending => false} search_cond.merge!({:bulletin_category_id => "#{params[:category_id]}" }) if !params[:category_id].blank? - search = Redis::Search.query("Bulletin", params[:search_query], :conditions =>search_cond,:limit=>Bulletin.all.count) - search_result = search.collect{|t| t["id"]} + + @search = Bulletin.solr_search do + fulltext params[:search_query] + #with(:frontend_search,true) + end + search_result = @search.results.collect{|result| result.id} @bulletins = Bulletin.all.available_for_lang(I18n.locale).can_display.any_in(_id:search_result).page( params[:page_main]).per(15) else diff --git a/vendor/built_in_modules/announcement/app/models/bulletin.rb b/vendor/built_in_modules/announcement/app/models/bulletin.rb index e70acf57b..12a725cb9 100644 --- a/vendor/built_in_modules/announcement/app/models/bulletin.rb +++ b/vendor/built_in_modules/announcement/app/models/bulletin.rb @@ -4,7 +4,7 @@ class Bulletin include Mongoid::Document include Mongoid::Timestamps include Mongoid::MultiParameterAttributes - include Redis::Search + include Sunspot::Mongo include Impressionist::Impressionable BelongsToCategory = :bulletin_category @@ -65,11 +65,29 @@ class Bulletin after_save :save_bulletin_links after_save :save_bulletin_files - redis_search_index(:title_field => :s_title, - :alias_field =>:s_title_en , - :score_field => :view_count, - :condition_fields => [:is_checked,:is_hidden,:bulletin_category_id,:is_pending], - :ext_fields => []) + searchable do + text :titles do + title_translations.to_a.collect{|t| t[1]} + end + text :texts do + text_translations.to_a.collect{|t| t[1]} + end + # text :text do + # a = (title_translations["zh_tw"] + title_translations["en"]) + # a + # end + boolean :frontend_search do + !is_hidden && !is_pending && is_checked + end + + integer :view_count + string :bulletin_category_id + end + # redis_search_index(:title_field => :s_title, + # :alias_field =>:s_title_en , + # :score_field => :view_count, + # :condition_fields => [:is_checked,:is_hidden,:bulletin_category_id,:is_pending], + # :ext_fields => []) # def gen_title_for_search @@ -78,21 +96,21 @@ class Bulletin #alias_method - def s_title - self.title_translations["zh_tw"] - end + # def s_title + # self.title_translations["zh_tw"] + # end - def s_title_was - self.title_was["zh_tw"] - end + # def s_title_was + # self.title_was["zh_tw"] + # end - def s_title_en - self.title_translations["en"] - end + # def s_title_en + # self.title_translations["en"] + # end - def s_title_en_was - self.title_was["en"] - end + # def s_title_en_was + # self.title_was["en"] + # end # def s_text_en # Nokogiri::HTML(self.text.en).text diff --git a/vendor/built_in_modules/news/app/controllers/panel/news/front_end/news_bulletins_controller.rb b/vendor/built_in_modules/news/app/controllers/panel/news/front_end/news_bulletins_controller.rb index 87826f021..bacb0a319 100644 --- a/vendor/built_in_modules/news/app/controllers/panel/news/front_end/news_bulletins_controller.rb +++ b/vendor/built_in_modules/news/app/controllers/panel/news/front_end/news_bulletins_controller.rb @@ -13,8 +13,12 @@ class Panel::News::FrontEnd::NewsBulletinsController < OrbitWidgetController if !params[:search_query].blank? search_cond = {:is_checked=>true,:is_hidden=>false,:is_pending=>false,:is_pending=>false} search_cond.merge!({:news_bulletin_category_id => "#{params[:category_id]}" }) if !params[:category_id].blank? - search = Redis::Search.query("NewsBulletin", params[:search_query], :conditions =>search_cond,:limit=>NewsBulletin.all.count) - search_result = search.collect{|t| t["id"]} + + @search = NewsBulletin.solr_search do + fulltext params[:search_query] + with(:frontend_search,true) + end + search_result = @search.results.collect{|result| result.id} @news_bulletins = NewsBulletin.all.available_for_lang(I18n.locale).can_display.any_in(_id:search_result).page( params[:page_main]).per(10) else date_now = Time.now diff --git a/vendor/built_in_modules/news/app/models/news_bulletin.rb b/vendor/built_in_modules/news/app/models/news_bulletin.rb index 3fdabd56d..8105917c5 100644 --- a/vendor/built_in_modules/news/app/models/news_bulletin.rb +++ b/vendor/built_in_modules/news/app/models/news_bulletin.rb @@ -4,7 +4,7 @@ class NewsBulletin include Mongoid::Document include Mongoid::Timestamps include Mongoid::MultiParameterAttributes - include Redis::Search + include Sunspot::Mongo include Impressionist::Impressionable BelongsToCategory = :news_bulletin_category @@ -91,31 +91,47 @@ class NewsBulletin "news_bulletin" end - redis_search_index(:title_field => :s_title, - :alias_field => :s_title_en, - :score_field => :view_count, - :condition_fields => [:is_checked,:is_hidden,:news_bulletin_category_id,:is_pending], - :ext_fields =>[]) + searchable do + text :titles do + title_translations.to_a.collect{|t| t[1]} + end + text :texts do + text_translations.to_a.collect{|t| t[1]} + end + integer :view_count + + boolean :frontend_search do + !is_hidden && !is_pending && is_checked + end + + + string :news_bulletin_category_id + end + # redis_search_index(:title_field => :s_title, + # :alias_field => :s_title_en, + # :score_field => :view_count, + # :condition_fields => [:is_checked,:is_hidden,:news_bulletin_category_id,:is_pending], + # :ext_fields =>[]) # def gen_title_for_search # # [s_title,s_title_en,s_text_en,s_text_zh_tw].join(' ') # end - def s_title - self.title_translations["zh_tw"] - end + # def s_title + # self. + # end - def s_title_was - self.title_was["zh_tw"] - end + # def s_title_was + # self.title_was["zh_tw"] + # end - def s_title_en - self.title_translations["en"] - end + # def s_title_en + # self.title_translations["en"] + # end - def s_title_en_was - self.title_was["en"] - end + # def s_title_en_was + # self.title_was["en"] + # end # def s_text_en # Nokogiri::HTML(self.text.en).text