Add joint sorting of links and files
This commit is contained in:
parent
225066bbea
commit
42a6fe06d2
|
|
@ -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|
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -103,7 +103,6 @@
|
|||
<p class="muted"><%= t('archive.min') %>: <%=min_sort_number%>, <%= t('archive.max') %>: <%=max_sort_number%></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Status Module -->
|
||||
|
|
@ -150,6 +149,20 @@
|
|||
<% end %>
|
||||
</ul>
|
||||
|
||||
<div class="control-group" id="file_link_sort_control" data-migrated="<%= @archive_file.files_links_migrated ? 'true' : 'false' %>">
|
||||
<label class="control-label muted"><%= t("archive.enable_file_link_sort") %></label>
|
||||
<div class="controls">
|
||||
<label class="checkbox">
|
||||
<%= f.check_box :enable_file_link_sort, id: "enable_file_link_sort_checkbox" %>
|
||||
<%= t("archive.enable_file_link_sort_hint") %>
|
||||
</label>
|
||||
<p class="muted" style="color:#c0392b;"><%= t("archive.disable_file_link_sort_warning") %></p>
|
||||
<% unless @archive_file.files_links_migrated %>
|
||||
<p class="muted" style="color:#c0392b;"><%= t("archive.not_migrated_yet_hint") %></p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Language -->
|
||||
<div class="tab-content language-area">
|
||||
|
||||
|
|
@ -175,25 +188,52 @@
|
|||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- urls -->
|
||||
<div class="control-group input-title">
|
||||
<label class="control-label muted"><%= t(:link) %></label>
|
||||
<div class="controls">
|
||||
<div class="link_append_target">
|
||||
<!-- 開關「關閉」時使用:原本的陣列式連結輸入,完全不變 -->
|
||||
<div class="link_append_target old-style-links" style="<%= @archive_file.enable_file_link_sort ? 'display:none;' : '' %>">
|
||||
<% I18n.with_locale(locale) do %>
|
||||
<% f.object.urls.to_a.each_with_index do |url,i| %>
|
||||
<%= render :partial => "form_link", :locals=>{:f=>f,:locale=>locale,:url=>url,:url_text=>f.object.get_url_text(i,true)} %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<a class="trigger btn btn-small btn-primary add_link" data-locale="<%=locale%>"><i class="icons-plus"></i> <%= t(:add) %></a>
|
||||
<a class="trigger btn btn-small btn-primary add_link old-style-links" data-locale="<%=locale%>" style="<%= @archive_file.enable_file_link_sort ? 'display:none;' : '' %>"><i class="icons-plus"></i> <%= t(:add) %></a>
|
||||
|
||||
<div class="new-style-links" style="<%= @archive_file.enable_file_link_sort ? '' : 'display:none;' %>">
|
||||
<% f.object.archive_file_links.select{|l| l.locale == locale.to_s}.each do |archive_file_link| %>
|
||||
<%= f.fields_for :archive_file_links, archive_file_link do |lf| %>
|
||||
<%= render :partial => "form_file_link", :locals => {:f=>lf, :locale=>locale, :form_link=>archive_file_link} %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<a class="trigger btn btn-small btn-primary add_file_link new-style-links" data-locale="<%=locale%>" style="<%= @archive_file.enable_file_link_sort ? '' : 'display:none;' %>"><i class="icons-plus"></i> <%= t(:add) %></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group file-link-order-block" style="<%= @archive_file.enable_file_link_sort ? '' : 'display:none;' %>">
|
||||
<label class="control-label muted"><%= t("archive.file_link_order") %></label>
|
||||
<div class="controls">
|
||||
<ul class="file-link-sortable" data-locale="<%= locale %>">
|
||||
<% f.object.merged_items_for_locale(locale).each do |item| %>
|
||||
<li class="file-link-sortable-item" data-token="<%= item[:token] %>">
|
||||
<i class="icons-list-2 sort-order-icon"></i>
|
||||
<span class="label label-info"><%= item[:type] == 'file' ? t(:file_) : t(:link) %></span>
|
||||
<%= item[:label] %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<div class="file-link-sortable-hidden-fields"></div>
|
||||
<p class="muted"><%= t("archive.file_link_order_hint") %></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<% end %>
|
||||
|
||||
<!-- File -->
|
||||
<div class="control-group">
|
||||
<label class="control-label muted"><%= t(:file_) %></label>
|
||||
|
|
@ -232,6 +272,7 @@
|
|||
|
||||
<!-- Form Actions -->
|
||||
<div class="form-actions">
|
||||
<%= hidden_field_tag 'archive_file[order_source]', '', id: 'archive_file_order_source' %>
|
||||
<%= hidden_field_tag 'page', params[:page] if !params[:page].blank? %>
|
||||
<%= f.submit t('submit'), class: 'btn btn-primary' %>
|
||||
<a href="<%= admin_archive_files_path %>" class="btn"><%= t("archive.back_to_archives") %></a>
|
||||
|
|
@ -424,6 +465,7 @@
|
|||
existingfiles.each(function(i, file){
|
||||
$(file).find("input.file-sort-number-field").val(existingfiles.length - i);
|
||||
})
|
||||
$('#archive_file_order_source').val('file_sort');
|
||||
}
|
||||
});
|
||||
$('.main-forms .add-on').tooltip();
|
||||
|
|
@ -452,6 +494,63 @@
|
|||
}
|
||||
});
|
||||
|
||||
function toggle_file_link_sort_ui(){
|
||||
var migrated = $('#file_link_sort_control').data('migrated') === true || $('#file_link_sort_control').data('migrated') === 'true';
|
||||
if (!migrated){
|
||||
$('.old-style-links').show();
|
||||
$('.new-style-links').hide();
|
||||
$('.file-link-order-block').hide();
|
||||
return;
|
||||
}
|
||||
var checked = $('#enable_file_link_sort_checkbox').is(':checked');
|
||||
$('.old-style-links').toggle(!checked);
|
||||
$('.new-style-links').toggle(checked);
|
||||
$('.file-link-order-block').toggle(checked);
|
||||
}
|
||||
toggle_file_link_sort_ui();
|
||||
$(document).on('change', '#enable_file_link_sort_checkbox', toggle_file_link_sort_ui);
|
||||
|
||||
// 合併排序清單:拖曳後,把目前順序寫回 hidden fields 一起送出
|
||||
$(".file-link-sortable").each(function(){
|
||||
var $list = $(this);
|
||||
var locale = $list.data("locale");
|
||||
function write_hidden_fields(){
|
||||
var $hidden_container = $list.siblings(".file-link-sortable-hidden-fields");
|
||||
$hidden_container.empty();
|
||||
$list.children("li").each(function(){
|
||||
var token = $(this).data("token");
|
||||
$hidden_container.append(
|
||||
$("<input>", {type:"hidden", name:"archive_file[sort_order_list]["+locale+"][]", value: token})
|
||||
);
|
||||
});
|
||||
}
|
||||
$list.sortable({ update: function(){
|
||||
write_hidden_fields();
|
||||
$('#archive_file_order_source').val('merged_list');
|
||||
}});
|
||||
write_hidden_fields();
|
||||
});
|
||||
|
||||
// 新式連結:新增一筆
|
||||
$(document).on('click', '.add_file_link', function(){
|
||||
var target_locale = $(this).data('locale');
|
||||
var new_index = 'new_' + new Date().getTime();
|
||||
var html = "<%= escape_javascript(f.fields_for(:archive_file_links, ArchiveFileLink.new, child_index: 'NEW_INDEX') { |lf| render(partial: 'form_file_link', locals: {f: lf, locale: 'new_locale', form_link: ArchiveFileLink.new}) }) %>";
|
||||
html = html.replace(/NEW_INDEX/g, new_index).replace(/new_locale/g, target_locale);
|
||||
$(this).siblings('.new-style-links').append(html);
|
||||
});
|
||||
// 新式連結:刪除「這次新增、還沒存檔」的那筆
|
||||
$(document).on('click', '.delete_file_link', function(){
|
||||
$(this).parents('.input-prepend').remove();
|
||||
});
|
||||
// 新式連結:刪除「已存在」的那筆(標記 should_destroy,交給後端在儲存時真正刪除)
|
||||
$(document).on('click', '.remove_existing_file_link', function(){
|
||||
if(confirm("<%= I18n.t(:sure?)%>")){
|
||||
$(this).find('.should_destroy').val('1');
|
||||
$(this).hide();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
<div class="input-prepend input-append start-line" data-locale="<%=locale%>">
|
||||
<span class="add-on icons-link" title="<%= t(:url) %>"></span>
|
||||
<%= f.text_field :url, class: "input-large", placeholder: t(:url) %>
|
||||
<span class="add-on icons-pencil" title="<%= t("archive.url_text") %>"></span>
|
||||
<%= f.text_field :url_text, class: "input-large", placeholder: t("archive.url_text_hint") %>
|
||||
<%= f.hidden_field :locale, value: locale %>
|
||||
<% if form_link.new_record? %>
|
||||
<span class="delete_file_link add-on btn" title="<%= t(:delete_) %>">
|
||||
<a class="icon-trash"></a>
|
||||
</span>
|
||||
<% else %>
|
||||
<%= f.hidden_field :id %>
|
||||
<%= f.hidden_field :should_destroy, value: nil, class: 'should_destroy' %>
|
||||
<span class="remove_existing_file_link add-on btn" title="<%= t(:remove) %>">
|
||||
<a class="icon-remove"></a>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
@ -39,8 +39,14 @@ en:
|
|||
Title: Title
|
||||
Files: Files
|
||||
Category: Category
|
||||
enable_file_link_sort: "File & Link Sorting"
|
||||
enable_file_link_sort_hint: "When enabled, files and links can be freely mixed and reordered together using the list below"
|
||||
file_link_order: "Order (drag items to reorder)"
|
||||
file_link_order_hint: "Only saved files/links can be dragged here; please save once first for newly added items, then reopen this page to reorder them."
|
||||
not_migrated_yet_hint: "This record hasn't been migrated for sorting yet. The link display will stay as-is for now; please check the box and save once, then reopen this page to see and use the ordering list."
|
||||
disable_file_link_sort_warning: "Note: After disabling this option, the displayed order of files and links may differ from your sorted result. Please save and re-open this page to review the order again."
|
||||
back_to_archives: Back to Archives
|
||||
frontend:
|
||||
archive: Archive Front-end
|
||||
widget:
|
||||
index: Archive Widget
|
||||
index: Archive Widget
|
||||
|
|
|
|||
|
|
@ -36,6 +36,12 @@ zh_tw:
|
|||
downloaded_times: 下載次數
|
||||
Files: 檔案
|
||||
Category: 類別
|
||||
enable_file_link_sort: "檔案及連結排序"
|
||||
enable_file_link_sort_hint: "開啟後,「檔案」與「連結」可以合併、自由交錯排序,顯示順序將依照下方拖曳的順序呈現"
|
||||
file_link_order: "排序(拖曳項目調整順序)"
|
||||
file_link_order_hint: "只有已經儲存過的檔案/連結才能在這裡拖曳排序;新增的項目請先按「儲存」,再重新進入編輯頁面排序。"
|
||||
not_migrated_yet_hint: "此筆資料尚未套用過排序功能,畫面暫時維持原本的連結顯示方式;請先勾選並儲存一次,儲存後重新進入編輯頁面,才會看到排序清單並生效。"
|
||||
disable_file_link_sort_warning: "提醒:關閉此開關後,檔案及連結的顯示順序可能會與您排序的結果有差異,請送出(儲存)後再次進入編輯頁面確認排序。"
|
||||
back_to_archives: 返回檔案
|
||||
frontend:
|
||||
archive: 檔案室前台
|
||||
|
|
|
|||
Loading…
Reference in New Issue