add manually sorting feature

This commit is contained in:
邱博亞 2022-03-29 14:25:10 +08:00
parent a571a8430d
commit d8da3dd1c9
13 changed files with 274 additions and 30 deletions

View File

@ -1,6 +1,6 @@
# encoding: UTF-8 # encoding: UTF-8
$:.push File.expand_path("../lib", __FILE__) $:.push File.expand_path("../lib", __FILE__)
$:.push File.expand_path("../app/models", __FILE__)
# Maintain your gem's version: # Maintain your gem's version:
require "announcement/version" require "announcement/version"
require 'json' require 'json'
@ -293,7 +293,10 @@ Gem::Specification.new do |s|
s.summary = "Announcements for Orbit" s.summary = "Announcements for Orbit"
s.description = "Announcements for Orbit" s.description = "Announcements for Orbit"
s.license = "MIT" s.license = "MIT"
s.metadata = {
"_require" => "announcement_setting",
"global_hash" => "{enable_manually_sort: (AnnouncementSetting.first.enable_manually_sort rescue false)}"
}
s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"] s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"]
s.test_files = Dir["test/**/*"] s.test_files = Dir["test/**/*"]

View File

@ -13,6 +13,32 @@ class Admin::AnnouncementsController < OrbitAdminController
super super
@app_title = "announcement" @app_title = "announcement"
end end
def update_sort
ids = params[:ids]
ids.each_with_index do |id,i|
Bulletin.where(id: id).update(sort_number: i)
end
edit_sort
render 'update_sort',layout: false
end
def update_sort_setting
setting = @announcement_setting
setting.update_attributes(settings_params)
if defined?(OrbitHelper::SharedHash)
OrbitHelper::SharedHash['announcement'][:enable_manually_sort] = setting.enable_manually_sort
end
redirect_to edit_sort_admin_announcement_path
end
def edit_sort
@setting = AnnouncementSetting.first
@announcements = Bulletin.where(is_top: true).order_by({is_top: -1,sort_number: 1,postdate: -1, _id: -1})
@table_fields = ['announcement.table.sort_number','announcement.table.title','announcement.default_widget.postdate']
end
def comment_hidden def comment_hidden
b = BulletinComment.find(params[:id]) rescue nil b = BulletinComment.find(params[:id]) rescue nil
if !b.nil? if !b.nil?

View File

@ -1,5 +1,20 @@
class AnnouncementsController < ApplicationController class AnnouncementsController < ApplicationController
include AnnouncementsHelper include AnnouncementsHelper
def initialize
super
@app_title = 'announcement'
@manually_sort = manually_sort
Bulletin.instance_variable_set('@manually_sort',@manually_sort)
#self.request = OrbitHelper.request
end
def manually_sort
if defined?(OrbitHelper::SharedHash)
OrbitHelper::SharedHash['announcement'][:enable_manually_sort]
else
AnnouncementSetting.first.enable_manually_sort rescue false
end
end
def comment def comment
@bulletin = Bulletin.where(:uid=>params[:uid]).first @bulletin = Bulletin.where(:uid=>params[:uid]).first
comment_val = params['comment'] comment_val = params['comment']
@ -207,8 +222,12 @@ class AnnouncementsController < ApplicationController
else else
cats = ["all"] + cats cats = ["all"] + cats
end end
if @manually_sort
anns = anns.sort_by { |a| [ (a["is_top"] ? 1 : 0) , -a['sort_number'].to_i, a["postdate"].blank? ? nil : a["postdate"].to_time] }.reverse
else
anns = anns.sort_by { |a| [ (a["is_top"] ? 1 : 0) , a["postdate"].blank? ? nil : a["postdate"].to_time] }.reverse anns = anns.sort_by { |a| [ (a["is_top"] ? 1 : 0) , a["postdate"].blank? ? nil : a["postdate"].to_time] }.reverse
end end
end
cats = cats.uniq cats = cats.uniq
tags = tags.uniq tags = tags.uniq
tags_translations = (subpart.get_tags_translations(false,false) rescue nil) tags_translations = (subpart.get_tags_translations(false,false) rescue nil)
@ -559,7 +578,11 @@ class AnnouncementsController < ApplicationController
if devide_flag if devide_flag
feeds_anns = get_feed_announcements("widget",nil,cats,widget_data_count - top_anns.count) feeds_anns = get_feed_announcements("widget",nil,cats,widget_data_count - top_anns.count)
top_anns = top_anns + feeds_anns.select{|v| v['is_top'] == true} top_anns = top_anns + feeds_anns.select{|v| v['is_top'] == true}
if @manually_sort
top_anns = top_anns.sort_by { |a| tmp=a["postdate"].blank?;[-a['sort_number'].to_i,tmp ? 0 : 1, tmp ? nil : a["postdate"].to_time] }.reverse
else
top_anns = top_anns.sort_by { |a| tmp=a["postdate"].blank?;[tmp ? 0 : 1, tmp ? nil : a["postdate"].to_time] }.reverse top_anns = top_anns.sort_by { |a| tmp=a["postdate"].blank?;[tmp ? 0 : 1, tmp ? nil : a["postdate"].to_time] }.reverse
end
rest_count = widget_data_count - top_anns.count rest_count = widget_data_count - top_anns.count
if rest_count <= 0 if rest_count <= 0
anns = top_anns.take(widget_data_count) anns = top_anns.take(widget_data_count)

View File

@ -48,6 +48,7 @@ module AnnouncementsHelper
"is_top" => a.is_top, "is_top" => a.is_top,
"bulletin_links" => links, "bulletin_links" => links,
"bulletin_files" => files, "bulletin_files" => files,
"sort_number" => a.sort_number,
"title" => a.title, "title" => a.title,
"source-site" => "", "source-site" => "",
"source-site-title" => "", "source-site-title" => "",
@ -194,6 +195,7 @@ module AnnouncementsHelper
.can_display_and_sorted.is_approved .can_display_and_sorted.is_approved
.filter_by_categories(categories,false).filter_by_tags(tags).to_a .filter_by_categories(categories,false).filter_by_tags(tags).to_a
end end
print('announcements',announcements.collect(&:title))
if !(defined? SiteFeed).nil? if !(defined? SiteFeed).nil?
if @type != "show_widget" if @type != "show_widget"
feeds_anns = get_feed_announcements("index",nil,categories,page_number*page_data_count) feeds_anns = get_feed_announcements("index",nil,categories,page_number*page_data_count)
@ -214,7 +216,17 @@ module AnnouncementsHelper
top_anns = announcements.select{|v| v.is_top} + feeds_anns.select{|v| v['is_top']} top_anns = announcements.select{|v| v.is_top} + feeds_anns.select{|v| v['is_top']}
rest_all_anns = feeds_anns.select{|v| v['is_top'] != true} + announcements.select{|v| !v.is_top} rest_all_anns = feeds_anns.select{|v| v['is_top'] != true} + announcements.select{|v| !v.is_top}
rest_anns = rest_all_anns.sort_by { |a| tmp=a["postdate"].blank?;[tmp ? 0 : 1, tmp ? nil : a["postdate"].to_time] }.reverse rest_anns = rest_all_anns.sort_by { |a| tmp=a["postdate"].blank?;[tmp ? 0 : 1, tmp ? nil : a["postdate"].to_time] }.reverse
all_sorted = top_anns.sort_by { |a| tmp=a["postdate"].blank?;[tmp ? 0 : 1, tmp ? nil : a["postdate"].to_time] }.reverse + rest_anns if @manually_sort
all_sorted = top_anns.sort_by do |a|
tmp = a["postdate"].blank?
[-a['sort_number'].to_i,tmp ? 0 : 1, tmp ? nil : a["postdate"].to_time]
end.reverse + rest_anns
else
all_sorted = top_anns.sort_by do |a|
tmp = a["postdate"].blank?
[tmp ? 0 : 1, tmp ? nil : a["postdate"].to_time]
end.reverse + rest_anns
end
else else
all_sorted = feeds_anns.select{|v| v['is_top']}.sort_by { |a| tmp=a["postdate"].blank?;[tmp ? 0 : 1, tmp ? nil : a["postdate"].to_time] }.reverse + feeds_anns.select{|v| v['is_top'] != true}.sort_by { |a| tmp=a["postdate"].blank?;[tmp ? 0 : 1, tmp ? nil : a["postdate"].to_time] }.reverse all_sorted = feeds_anns.select{|v| v['is_top']}.sort_by { |a| tmp=a["postdate"].blank?;[tmp ? 0 : 1, tmp ? nil : a["postdate"].to_time] }.reverse + feeds_anns.select{|v| v['is_top'] != true}.sort_by { |a| tmp=a["postdate"].blank?;[tmp ? 0 : 1, tmp ? nil : a["postdate"].to_time] }.reverse
end end

View File

@ -13,6 +13,7 @@ class AnnouncementSetting
field :top_text , type: String , localize: true field :top_text , type: String , localize: true
field :hot_text , type: String , localize: true field :hot_text , type: String , localize: true
field :hidden_text , type: String , localize: true field :hidden_text , type: String , localize: true
field :enable_manually_sort, type: Boolean, default: false
has_many :anns_status_settings, :autosave => true, :dependent => :destroy has_many :anns_status_settings, :autosave => true, :dependent => :destroy
accepts_nested_attributes_for :anns_status_settings, :allow_destroy => true accepts_nested_attributes_for :anns_status_settings, :allow_destroy => true
def self.check_limit_for_user(user_id, b_id = nil) def self.check_limit_for_user(user_id, b_id = nil)
@ -31,4 +32,12 @@ class AnnouncementSetting
self.sub_annc_title_trans.blank? ? I18n.t("announcement.table.title") : self.sub_annc_title_trans self.sub_annc_title_trans.blank? ? I18n.t("announcement.table.title") : self.sub_annc_title_trans
end end
end end
before_save do
if self.enable_manually_sort_changed? && self.enable_manually_sort
AnnsCache.all.delete
Bulletin.index({approved: -1,is_hidden: 1,is_preview: 1, is_top: -1,sort_number: 1,postdate: -1,_id: -1,deadline: -1}, { unique: false, background: true })
Bulletin.create_indexes
end
end
end end

View File

@ -100,7 +100,7 @@ class Bulletin
scope :open_in_future, ->{where(:is_hidden.ne=>true,:is_preview.ne => true,:postdate.gt=>Time.now).order(postdate: :asc)} scope :open_in_future, ->{where(:is_hidden.ne=>true,:is_preview.ne => true,:postdate.gt=>Time.now).order(postdate: :asc)}
scope :can_display_and_sorted, ->{where(:approved => true,:is_hidden.ne=>true,:is_preview.ne => true).valid_time_range} scope :can_display_and_sorted, ->{where(:approved => true,:is_hidden.ne=>true,:is_preview.ne => true).valid_time_range}
scope :valid_time_range, ->{any_of({:postdate.lte=>Time.now, :deadline.gte=>Time.now},{:postdate.lte=>Time.now, :deadline=>nil},{:postdate=>nil,:deadline.gte=>Time.now},{:postdate=>nil,:deadline=>nil}).order(is_top: :desc,postdate: :desc,id: :desc)} scope :valid_time_range, ->{any_of({:postdate.lte=>Time.now, :deadline.gte=>Time.now},{:postdate.lte=>Time.now, :deadline=>nil},{:postdate=>nil,:deadline.gte=>Time.now},{:postdate=>nil,:deadline=>nil}).order((@manually_sort ? {is_top: :desc,sort_number: :asc,postdate: :desc,id: :desc} : {is_top: :desc,postdate: :desc,id: :desc}))}
scope :is_approved, ->{where(:approved => true)} scope :is_approved, ->{where(:approved => true)}
scope :is_approved_and_show, ->{where(:approved => true,:is_hidden.ne=>true,:is_preview.ne => true)} scope :is_approved_and_show, ->{where(:approved => true,:is_hidden.ne=>true,:is_preview.ne => true)}
scope :filter_cats_and_tags, ->(cats,tags) {filter_by_widget_categories(cats,false).filter_by_tags(tags)} scope :filter_cats_and_tags, ->(cats,tags) {filter_by_widget_categories(cats,false).filter_by_tags(tags)}
@ -109,6 +109,7 @@ class Bulletin
index({postdate: 1}, { unique: false, background: true }) index({postdate: 1}, { unique: false, background: true })
index({is_top: -1,postdate: -1, _id: -1}, { unique: false, background: true }) index({is_top: -1,postdate: -1, _id: -1}, { unique: false, background: true })
index({approved: -1,is_hidden: 1,is_preview: 1, is_top: -1,postdate: -1,_id: -1,deadline: -1}, { unique: false, background: true }) index({approved: -1,is_hidden: 1,is_preview: 1, is_top: -1,postdate: -1,_id: -1,deadline: -1}, { unique: false, background: true })
field :sort_number, type: Integer
def get_org_model def get_org_model
if self.is_preview if self.is_preview
org_model = nil org_model = nil

View File

@ -0,0 +1,35 @@
<div id="data-table" class="ut-table">
<table class="table main-list">
<thead>
<tr class="sort-header">
<% @table_fields.each do |field| %>
<%
field_text = field.to_s.include?('.') ? t(field.to_s) : field.to_s
sort = field.to_s.split('.')[-1]
active = params[:sort].eql? sort
order = active ? (["asc", "desc"]-[params[:order]]).first : "asc"
arrow = (order.eql? "desc") ? "<b class='icons-arrow-up-3'></b>" : "<b class='icons-arrow-down-4'></b>"
klass = sort.eql?(:title) ? "span5" : "span2"
th_data = "<a href='?sort=#{sort}&order=#{order}'>#{field_text} #{active ? arrow : ""}</a>"
%>
<th class='<%= klass %> <%= active ? "active" : "" %>'><%= th_data.html_safe %></th>
<% end %>
</tr>
</thead>
<tbody id="sortable">
<% @announcements.each do |annc| %>
<tr data-id="<%= annc.id %>">
<td>
<%= number_field_tag nil,annc.sort_number,class: 'sort_number',step: 1 %>
</td>
<td>
<%= annc.title %>
</td>
<td>
<%= format_value annc.postdate %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>

View File

@ -0,0 +1,117 @@
<% content_for :page_specific_css do %>
<%= stylesheet_link_tag "lib/main-forms" %>
<%= stylesheet_link_tag "lib/fileupload" %>
<%= stylesheet_link_tag "lib/main-list" %>
<% end %>
<%= form_for @setting, url: update_sort_setting_admin_announcement_path, html: {class: "form-horizontal main-forms"}, method: 'post' do |f| %>
<div class="input-area">
<div class="control-group">
<%= f.label :maull, t("announcement.enable_manually_sort"), :class => "control-label muted" %>
<div class="controls">
<%= f.check_box :enable_manually_sort,:id=>'enable_manually_sort' %>
</div>
</div>
</div>
<% end %>
<div style="margin-bottom: 1em;">
<button type="button" class="btn btn-primary" id="update_sort_button"><%= t('announcement.manual_update_sort') %></button>
</div>
<%= render partial: 'edit_sort' %>
<script type="text/javascript">
function update_sort(){
var ids = $.map($('#sortable>tr'),function(v){return $(v).data('id')});
$.ajax({
url: "<%= update_sort_admin_announcement_path %>",
type: 'POST',
dataType: 'text',
data: {ids: ids},
success: function(data){
$('#data-table').replaceWith(data);
sortable();
}
});
}
function sortable(){
$( "#sortable" ).sortable({
update: function( event, ui ) {
update_sort();
}
});
$('.sort_number').change(function(){
var new_sort_number = parseFloat($(this).val());
var min_number = $('.sort_number').length;
var max_number = 0;
var pool = $('.sort_number').not(this);
var same_order = pool.filter(function(){
var tmp_sort = parseFloat($(this).val());
if (tmp_sort<min_number){
min_number = tmp_sort;
}
if (tmp_sort>max_number){
max_number = tmp_sort;
}
return tmp_sort==new_sort_number
});
var tmp_same_order = null;
if (same_order.length>0){
tmp_same_order = same_order.eq(0);
tmp_same_order.parents('tr').eq(0).before($(this).parents('tr').eq(0));
}else{
//ex. 1 2 3 5,insert 4
if (parseInt(pool.eq(0).val())<parseInt(pool.eq(-1).val())){ //asc
$.each(pool,function(){
var tmp_sort = parseFloat($(this).val());
if (new_sort_number>max_number){
if (tmp_sort<=new_sort_number){
tmp_same_order = $(this);
}
}else{
if (tmp_sort>=new_sort_number){
tmp_same_order = $(this);
return false;
}
}
});
if (new_sort_number>max_number){
tmp_same_order.parents('tr').eq(0).after($(this).parents('tr').eq(0));
}else{
tmp_same_order.parents('tr').eq(0).before($(this).parents('tr').eq(0));
}
}else{ //desc
//ex. 5 3 2 1,insert 4
$.each(pool,function(){
var tmp_sort = parseFloat($(this).val());
if (new_sort_number<min_number){
if (tmp_sort>=new_sort_number){
tmp_same_order = $(this);
}
}else if (new_sort_number>max_number){
if (tmp_sort<=new_sort_number){
tmp_same_order = $(this);
return false;
}
}else{
if (tmp_sort<=new_sort_number){
tmp_same_order = $(this);
return false;
}
}
});
if (new_sort_number<min_number){
tmp_same_order.parents('tr').eq(0).after($(this).parents('tr').eq(0));
}else{
tmp_same_order.parents('tr').eq(0).before($(this).parents('tr').eq(0));
}
}
}
update_sort();
});
}
$(document).ready(function(){
$('#update_sort_button').click(update_sort);
$('#enable_manually_sort').change(function(){
$(this).parents('form').submit();
})
sortable();
});
</script>

View File

@ -0,0 +1 @@
<%= render partial: 'edit_sort' %>

View File

@ -3,6 +3,9 @@ en:
feed: Feed feed: Feed
import: Import import: Import
announcement: announcement:
manually_sort: Manually Sort
enable_manually_sort: Enable Manually Sort
manual_update_sort: Manually Update Sort
carousel_image_types: carousel_image_types:
default: Default default: Default
carousel: Carousel carousel: Carousel

View File

@ -4,6 +4,9 @@ zh_tw:
import: 匯入 import: 匯入
get_all_anncs_without_subannc: "選擇相關公告" get_all_anncs_without_subannc: "選擇相關公告"
announcement: announcement:
manually_sort: 手動排序
enable_manually_sort: 開啟手動排序
manual_update_sort: 手動更新排序
carousel_image_types: carousel_image_types:
default: 預設 default: 預設
carousel: 輪播 carousel: 輪播

View File

@ -35,6 +35,13 @@ Rails.application.routes.draw do
resources :announcements resources :announcements
get 'announcements/:id/comment'=> 'announcements#comment' get 'announcements/:id/comment'=> 'announcements#comment'
get 'annc-comment-hidden/:id' => 'announcements#comment_hidden' get 'annc-comment-hidden/:id' => 'announcements#comment_hidden'
resource :announcement,:only => [] do
collection do
get "edit_sort"
post "update_sort_setting", to: 'announcements#update_sort_setting'
post "update_sort", to: 'announcements#update_sort'
end
end
end end
resources :announcements do resources :announcements do
@ -51,5 +58,4 @@ Rails.application.routes.draw do
get '/xhr/announcements/:slug_title-:uid/comment', to: 'announcements#comment' get '/xhr/announcements/:slug_title-:uid/comment', to: 'announcements#comment'
get '/xhr/announcements/file/:id/*f_name', to: 'announcements#get_file', format: false get '/xhr/announcements/file/:id/*f_name', to: 'announcements#get_file', format: false
end end
end end

View File

@ -75,7 +75,7 @@ module Announcement
if ENV['worker_num']=='0' && File.basename($0) != 'rake' && !Rails.const_defined?('Console') if ENV['worker_num']=='0' && File.basename($0) != 'rake' && !Rails.const_defined?('Console')
require File.expand_path('../../../app/models/anns_cache', __FILE__) require File.expand_path('../../../app/models/anns_cache', __FILE__)
if defined?(AnnsCache) if defined?(AnnsCache)
AnnsCache.destroy_all AnnsCache.all.delete
end end
end end
rescue => e rescue => e
@ -109,38 +109,43 @@ module Announcement
:priority=>1, :priority=>1,
:active_for_action=>{'admin/announcements'=>'index'}, :active_for_action=>{'admin/announcements'=>'index'},
:available_for => 'users' :available_for => 'users'
context_link 'announcement.manually_sort',
:link_path=>"edit_sort_admin_announcement_path" ,
:priority=>2,
:active_for_action=>{'admin/announcement'=>'edit_sort'},
:available_for => 'sub_managers'
context_link 'new_', context_link 'new_',
:link_path=>"new_admin_announcement_path" , :link_path=>"new_admin_announcement_path" ,
:priority=>2, :priority=>3,
:active_for_action=>{'admin/announcements'=>'new'}, :active_for_action=>{'admin/announcements'=>'new'},
:available_for => 'sub_managers' :available_for => 'sub_managers'
context_link 'categories', context_link 'categories',
:link_path=>"admin_module_app_categories_path" , :link_path=>"admin_module_app_categories_path" ,
:link_arg=>"{:module_app_id=>ModuleApp.find_by(:key=>'announcement').id}", :link_arg=>"{:module_app_id=>ModuleApp.find_by(:key=>'announcement').id}",
:priority=>3, :priority=>4,
:active_for_action=>{'admin/announcements'=>'categories'}, :active_for_action=>{'admin/announcements'=>'categories'},
:active_for_category => 'Announcement', :active_for_category => 'Announcement',
:available_for => 'managers' :available_for => 'managers'
context_link 'tags', context_link 'tags',
:link_path=>"admin_module_app_tags_path" , :link_path=>"admin_module_app_tags_path" ,
:link_arg=>"{:module_app_id=>ModuleApp.find_by(:key=>'announcement').id}", :link_arg=>"{:module_app_id=>ModuleApp.find_by(:key=>'announcement').id}",
:priority=>4, :priority=>5,
:active_for_action=>{'admin/announcements'=>'tags'}, :active_for_action=>{'admin/announcements'=>'tags'},
:active_for_tag => 'Announcement', :active_for_tag => 'Announcement',
:available_for => 'managers' :available_for => 'managers'
context_link 'announcement.feed_list', context_link 'announcement.feed_list',
:link_path=>"admin_announcement_feed_path" , :link_path=>"admin_announcement_feed_path" ,
:priority=>5, :priority=>6,
:active_for_action=>{'admin/announcements'=>'feed'}, :active_for_action=>{'admin/announcements'=>'feed'},
:available_for => 'managers' :available_for => 'managers'
context_link 'announcement.import', context_link 'announcement.import',
:link_path=>"admin_announcement_import_path" , :link_path=>"admin_announcement_import_path" ,
:priority=>6, :priority=>7,
:active_for_action=>{'admin/announcements'=>'import'}, :active_for_action=>{'admin/announcements'=>'import'},
:available_for => 'managers' :available_for => 'managers'
context_link 'announcement.settings', context_link 'announcement.settings',
:link_path=>"admin_announcement_settings_path" , :link_path=>"admin_announcement_settings_path" ,
:priority=>6, :priority=>8,
:active_for_action=>{'admin/announcements'=>'settings'}, :active_for_action=>{'admin/announcements'=>'settings'},
:available_for => 'managers' :available_for => 'managers'
end end