diff --git a/app/models/archive_file.rb b/app/models/archive_file.rb index 601654f..afbd5f0 100644 --- a/app/models/archive_file.rb +++ b/app/models/archive_file.rb @@ -85,6 +85,10 @@ class ArchiveFile field :uid, type: String field :sort_number, type: Integer field :tmp_sort_number, type: Integer + field :enable_file_link_sort, type: Boolean, default: false + field :sort_order_list, type: Hash, default: {} + field :files_links_migrated, type: Boolean, default: false + attr_accessor :order_source field :rss2_sn field :feed_uid field :site_feed_id @@ -96,15 +100,15 @@ class ArchiveFile # belongs_to :archive_file_category has_many :archive_file_multiples, :autosave => true, :dependent => :destroy - + has_many :archive_file_links, :autosave => true, :dependent => :destroy accepts_nested_attributes_for :archive_file_multiples, :allow_destroy => true - + accepts_nested_attributes_for :archive_file_links, :allow_destroy => true # validates :title, :at_least_one => true - after_save :save_archive_file_multiples, :update_tmp_sort_number - + after_save :save_archive_file_multiples, :save_archive_file_links, :sync_links_back_to_urls_array, :sync_file_order_between_ui, :update_tmp_sort_number + before_save :add_http - + before_save :migrate_urls_to_links_if_needed after_initialize do unless self.new_record? if self.urls.nil? && self.url.present? @@ -139,6 +143,67 @@ class ArchiveFile self.class.recalc_sort_number end end + + def migrate_urls_to_links_if_needed + return unless self.enable_file_link_sort + return if self.files_links_migrated + in_use_locales = (Site.first.in_use_locales rescue I18n.available_locales).map(&:to_s) + in_use_locales.each do |locale| + urls = self.urls_translations[locale] || [] + url_texts = self.url_texts_translations[locale] || [] + urls.each_with_index do |url, i| + next if url.blank? + self.archive_file_links.build(locale: locale, url: url, url_text: url_texts[i]) + end + end + new_sort_order_list = {} + in_use_locales.each do |locale| + file_tokens = self.archive_file_multiples.select { |f| f.choose_lang.include?(locale) } + .sort_by { |f| -(f.sort_number.to_i) } + .map { |f| "file:#{f.id}" } + link_tokens = self.archive_file_links.select { |l| l.locale == locale } + .map { |l| "link:#{l.id}" } + new_sort_order_list[locale] = file_tokens + link_tokens + end + self.sort_order_list = new_sort_order_list + self.files_links_migrated = true + end + + def ordered_tokens_for_locale(locale) + locale = locale.to_s + order_tokens = (self.sort_order_list[locale] || []).dup + existing_file_tokens = self.archive_file_multiples.select { |f| f.choose_lang.include?(locale) }.map { |f| "file:#{f.id}" } + existing_link_tokens = self.archive_file_links.select { |l| l.locale == locale }.map { |l| "link:#{l.id}" } + valid_tokens = existing_file_tokens + existing_link_tokens + tokens = order_tokens & valid_tokens + missing = valid_tokens - tokens + tokens + missing + end + + def merged_items_for_locale(locale) + locale = locale.to_s + file_map = self.archive_file_multiples.each_with_object({}) { |f, h| h[f.id.to_s] = f } + link_map = self.archive_file_links.select { |l| l.locale == locale }.each_with_object({}) { |l, h| h[l.id.to_s] = l } + self.ordered_tokens_for_locale(locale).map do |token| + type, id = token.split(":", 2) + if type == "file" + f = file_map[id] + next nil if f.nil? + { type: "file", token: token, label: (f.file_title.presence || (File.basename(f.file.path) rescue "file")) } + else + l = link_map[id] + next nil if l.nil? + { type: "link", token: token, label: (l.url_text.presence || l.url) } + end + end.compact + end + + def save_archive_file_links + return if @skip_callback + self.archive_file_links.each do |t| + t.destroy if t.should_destroy + end + end def get_url_text(idx=0,org=false) url_text = self.url_texts[idx] rescue nil if org @@ -272,6 +337,7 @@ class ArchiveFile end ArchiveCache.destroy_all end + def get_files(locale=nil, serial_number=0) if locale.nil? locale = I18n.locale.to_s @@ -286,6 +352,45 @@ class ArchiveFile h["serial_number"] = serial_number h end + elsif self.enable_file_link_sort + file_map = self.archive_file_multiples.each_with_object({}) { |f, h| h[f.id.to_s] = f } + link_map = self.archive_file_links.each_with_object({}) { |l, h| h[l.id.to_s] = l } + self.ordered_tokens_for_locale(locale).each do |token| + type, id = token.split(":", 2) + if type == "file" + file = file_map[id] + next if file.nil? + title = (file.file_title.blank? ? File.basename(file.file.path) : file.file_title) rescue "" + extension = file.file.file.extension.downcase rescue "" + serial_number += 1 + real_file_time = "" + if file.file.present? && file.file.path && File.exist?(file.file.path) + real_file_time = File.mtime(file.file.path).localtime.strftime("%Y-%m-%d %H:%M") + else + real_file_time = file.created_at.localtime.strftime("%Y-%m-%d %H:%M") rescue "" + end + files << { + "file-name" => title, + "file-type" => extension, + "file-url" => (file.file.present? ? "/xhr/archive/download?file=#{file.id}" : 'javascript:void(0)'), + "target" => "_blank", + "serial_number" => serial_number, + "file-date" => real_file_time + } + elsif type == "link" + link = link_map[id] + next if link.nil? + serial_number += 1 + target = (link.url.to_s.match(/\/[^\/]/) ? '_self' : '_blank') + files << { + "file-name" => (link.url_text.presence || link.url), + "file-type" => 'link', + "file-url" => link.url.to_s + "\" data-target=\"#{target}", + "target" => target, + "serial_number" => serial_number + } + end + end else self.archive_file_multiples.order_by(:sort_number=>'desc').each do |file| if file.choose_lang.include?(locale) @@ -296,7 +401,6 @@ class ArchiveFile if file.file.present? && file.file.path && File.exist?(file.file.path) real_file_time = File.mtime(file.file.path).localtime.strftime("%Y-%m-%d %H:%M") else - # 如果找不到實體檔案,才回退到資料庫時間(或留空) real_file_time = file.created_at.localtime.strftime("%Y-%m-%d %H:%M") rescue "" end files << { @@ -325,6 +429,7 @@ class ArchiveFile end [files, serial_number] end + def get_widget_data(locale=nil, serial_number=0, idx=0, show_tags=false, more_url=nil) created_at_int = self.created_at.strftime('%Y%m%d').to_i statuses = self.statuses_with_classname.collect do |status| @@ -546,6 +651,70 @@ class ArchiveFile end end end + + def sync_links_back_to_urls_array + return unless self.enable_file_link_sort + in_use_locales = (Site.first.in_use_locales rescue I18n.available_locales).map(&:to_s) + new_urls = self.urls_translations.dup + new_url_texts = self.url_texts_translations.dup + in_use_locales.each do |locale| + ordered_links = self.ordered_tokens_for_locale(locale) + .select { |token| token.start_with?("link:") } + .map { |token| token.split(":", 2)[1] } + link_map = self.archive_file_links.select { |l| l.locale == locale }.each_with_object({}) { |l, h| h[l.id.to_s] = l } + urls_for_locale = [] + url_texts_for_locale = [] + ordered_links.each do |id| + link = link_map[id] + next if link.nil? + urls_for_locale << link.url + url_texts_for_locale << link.url_text + end + new_urls[locale] = urls_for_locale + new_url_texts[locale] = url_texts_for_locale + end + @skip_callback = true + in_use_locales.each do |locale| + I18n.with_locale(locale) do + self.class.where(:id => self.id).update_all(:urls => new_urls[locale], :url_texts => new_url_texts[locale]) + end + end + @skip_callback = false + end + + def sync_file_order_between_ui + return unless self.enable_file_link_sort + case self.order_source + when "merged_list" + reference_locale = (Site.first.in_use_locales rescue I18n.available_locales).first.to_s + file_ids_in_order = self.ordered_tokens_for_locale(reference_locale) + .select { |token| token.start_with?("file:") } + .map { |token| token.split(":", 2)[1] } + total = file_ids_in_order.count + file_ids_in_order.each_with_index do |id, idx| + ArchiveFileMultiple.where(:id => id).update_all(:sort_number => (total - idx)) + end + self.class.recalc_sort_number + when "file_sort" + in_use_locales = (Site.first.in_use_locales rescue I18n.available_locales).map(&:to_s) + new_sort_order_list = self.sort_order_list.dup + in_use_locales.each do |locale| + tokens = self.ordered_tokens_for_locale(locale) + file_slot_indexes = tokens.each_index.select { |i| tokens[i].start_with?("file:") } + visible_files_sorted = self.archive_file_multiples.select { |f| f.choose_lang.include?(locale) } + .sort_by { |f| -(f.sort_number.to_i) } + new_tokens = tokens.dup + file_slot_indexes.each_with_index do |slot_index, i| + file = visible_files_sorted[i] + new_tokens[slot_index] = "file:#{file.id}" if file + end + new_sort_order_list[locale] = new_tokens + end + self.class.where(:id => self.id).update_all(:sort_order_list => new_sort_order_list) + else + end + end + def self.smart_convertor(text,url) doc = Nokogiri.HTML(text) doc.search('a[href]').each do |link| diff --git a/app/models/archive_file_link.rb b/app/models/archive_file_link.rb new file mode 100644 index 0000000..79b0920 --- /dev/null +++ b/app/models/archive_file_link.rb @@ -0,0 +1,14 @@ +class ArchiveFileLink + include Mongoid::Document + include Mongoid::Timestamps + + field :locale, type: String + field :url, type: String + field :url_text, type: String + field :sort_number, type: Integer + field :should_destroy, type: Boolean + + belongs_to :archive_file, index: true + + index({archive_file_id: 1, locale: 1}) +end diff --git a/app/views/admin/archive_files/_form.html.erb b/app/views/admin/archive_files/_form.html.erb index f1fa7bd..627a53f 100644 --- a/app/views/admin/archive_files/_form.html.erb +++ b/app/views/admin/archive_files/_form.html.erb @@ -103,7 +103,6 @@
<%= t('archive.min') %>: <%=min_sort_number%>, <%= t('archive.max') %>: <%=max_sort_number%>
- @@ -150,6 +149,20 @@ <% end %> +<%= t("archive.disable_file_link_sort_warning") %>
+ <% unless @archive_file.files_links_migrated %> +<%= t("archive.not_migrated_yet_hint") %>
+ <% end %> +