Add export and import and batch delete subscribers feature.

This commit is contained in:
BoHung Chiu 2022-05-23 22:20:09 +08:00
parent bb3236feba
commit 4735b107d7
12 changed files with 790 additions and 2 deletions

View File

@ -25,4 +25,87 @@ class Admin::EPaperSubscribersController < OrbitAdminController
end
redirect_to admin_e_paper_subscribers_path
end
def export_excel
@epaper_subscribers = EPaperSubscriber.where(:email.nin=>[nil,""]).desc(:created_at)
@subscribers = @epaper_subscribers.where(:subscribed.ne=>false)
@unsubscribers = @epaper_subscribers.where(:subscribed=>false)
respond_to do |format|
format.xlsx {
response.headers['Content-Disposition'] = 'attachment; filename="'+Site.first.title+'-'+I18n.t('e_paper.e_paper')+'-'+I18n.t('e_paper.subscriber')+'.xlsx"'
}
end
end
def get_subscribers_modal
@epaper_subscribers = EPaperSubscriber.where(:email.nin=>[nil,""]).desc(:created_at)
@subscribers = @epaper_subscribers.where(:subscribed.ne=>false)
@unsubscribers = @epaper_subscribers.where(:subscribed=>false)
render :partial => 'modal_select', :layout => false
end
def import_from_excel
workbook = RubyXL::Parser.parse(params["import_file"].tempfile)
subscribe_sheet = workbook['Subscribe']
unsubscribe_sheet = workbook['Unsubscribe']
subscribe_sheet.each_with_index do |row, i|
next if i < 1
c0 = row.cells[0]
c1 = row.cells[1]
if c0
email = c0.value
if email.present?
subscriber = EPaperSubscriber.where(:email=>email).first
if subscriber.nil?
subscriber = EPaperSubscriber.new(:email=>email)
end
language = c1.value
if language.blank?
language = I18n.locale.to_s
end
subscriber.subscribed = true
subscriber.language = language
subscriber.save
end
end
end
unsubscribe_sheet.each_with_index do |row, i|
next if i < 1
c0 = row.cells[0]
c1 = row.cells[1]
if c0
email = c0.value
if email.present?
subscriber = EPaperSubscriber.where(:email=>email).first
if subscriber.nil?
subscriber = EPaperSubscriber.new(:email=>email)
end
language = c1.value
if language.blank?
language = I18n.locale.to_s
end
subscriber.subscribed = false
subscriber.language = language
subscriber.save
end
end
end
redirect_to admin_e_paper_subscribers_path
end
def download_excel_format
@subscribers = []
@unsubscribers = []
respond_to do |format|
format.xlsx {
response.headers['Content-Disposition'] = 'attachment; filename="'+Site.first.title+'-'+I18n.t('e_paper.e_paper')+'-'+I18n.t('e_paper.subscriber')+'excel_format.xlsx"'
}
end
end
def delete_subscribers
subscriber_ids = params['subscriber_ids']
if subscriber_ids
EPaperSubscriber.where(:id.in=>subscriber_ids).destroy
end
redirect_to admin_e_paper_subscribers_path
end
end

View File

@ -269,6 +269,8 @@ class EPapersController < ApplicationController
subscriber.subscribed = false
subscriber.save
data = {"success" => true, "msg" => "Successfully Unsubscribed!!!"}
else
data = {"success" => false, "msg" => "You are not a subscriber!!!"}
end
render :json => data.to_json
end

View File

@ -28,5 +28,24 @@
</tbody>
</table>
<div class="bottomnav clearfix" style="left: 81px;">
<%= content_tag :div, paginate(@subscribers), class: "pagination pagination-centered" %>
<%= content_tag(:div, paginate(@subscribers), class: "pagination pagination-centered") %>
<div class="pull-right">
<%=
content_tag(:div, link_to(t("e_paper.export"), admin_e_paper_subscribers_export_excel_path + '.xlsx', :class=>"btn btn-success"), class: "pull-right")
%>
<div class="dropup upload-button pull-right">
<button class="btn btn-info dropdown-toggle" type="button" data-toggle="dropdown">
<i class="icon-upload-alt icon-white"></i><%= t('personal_journal.upload') %>
<span class="caret"></span>
</button>
<div class="dropdown-menu upload-box">
<form action="<%=admin_e_paper_subscribers_import_from_excel_path%>" method="post" enctype="multipart/form-data">
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
<input type="file" name="import_file" >
<button class="btn btn-primary" type="submit"><%= t(:submit) %></button>
<a class="" href="<%=admin_e_paper_subscribers_download_excel_format_path%>.xlsx">Download excel format</a>
</form>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,202 @@
<style>
.checkbox-card .hide{
display: none;
}
</style>
<div id="member-filter" class="modal hide fade" tabindex="-1" role="dialog" data-backdrop="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3><%= t('e_paper.subscribers') %></h3>
</div>
<div class="modal-body">
<div class="tabbable tabs-left">
<div class="nano">
<div class="content">
<ul class="nav nav-pills nav-stacked">
<li class="active"><a href="#epaper_subscribed" data-toggle="tab">Subscribed</a></li>
<li><a href="#epaper_unsubscribed" data-toggle="tab">Unsubscribed</a></li>
</ul>
</div>
</div>
<div class="tab-content tab-content-1">
<div class="tab-pane fade in active" id="epaper_subscribed">
<div id="subscribers_form">
<fieldset class="clearfix">
<div class="member-filter-result nano">
<div class="content">
<i class="icon-search"></i>
<input class="search_emails" placeholder="<%=t('email')%>" title="<%=t('email')%>">
<div style="margin-bottom: 1em;"></div>
<ul class="checkbox-card clearfix">
<% @subscribers.each do |subscriber| %>
<li class="check-item">
<label class="checkbox inline">
<span class="subscriber_email"><%= subscriber.email %></span>
<%= check_box_tag 'subscriber_ids[]', subscriber.id, false , :id => "subscriber_ids_#{subscriber.id}" %>
<%= hidden_field_tag 'subscriber_ids[]', subscriber.id %>
</label>
</li>
<% end %>
</ul>
</div>
</div>
</fieldset>
<div class="form-actions">
<div class="btn-group dropup" style="text-align:left;">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
Select
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="#" class="select_all_modal"><i class="icon-ok"></i> Select All</a></li>
<li><a href="#" class="deselect_all_modal"><i class="icon-remove"></i> De-select All</a></li>
</ul>
</div>
<button type="button" class="btn" data-dismiss="modal"><%= t(:cancel) %></button>
<%= submit_tag t(:confirm), class: "btn btn-primary" %>
</div>
</div>
</div>
<div class="tab-pane fade" id="epaper_unsubscribed">
<div id="subscribers_form">
<fieldset class="clearfix">
<div class="member-filter-result nano">
<div class="content">
<i class="icon-search"></i>
<input class="search_emails" placeholder="<%=t('email')%>" title="<%=t('email')%>">
<div style="margin-bottom: 1em;"></div>
<ul class="checkbox-card clearfix">
<% @unsubscribers.each do |unsubscriber| %>
<li class="check-item">
<label class="checkbox inline">
<span class="subscriber_email"><%= unsubscriber.email %></span>
<%= check_box_tag 'subscriber_ids[]', unsubscriber.id, false , :id => "subscriber_ids_#{unsubscriber.id}" %>
<%= hidden_field_tag 'subscriber_ids[]', unsubscriber.id %>
</label>
</li>
<% end %>
</ul>
</div>
</div>
</fieldset>
<div class="form-actions">
<div class="btn-group dropup" style="text-align:left;">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
Select
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="#" class="select_all_modal"><i class="icon-ok"></i> Select All</a></li>
<li><a href="#" class="deselect_all_modal"><i class="icon-remove"></i> De-select All</a></li>
</ul>
</div>
<button type="button" class="btn" data-dismiss="modal"><%= t(:cancel) %></button>
<%= submit_tag t(:confirm), class: "btn btn-primary" %>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.checkbox-card li .member-pic {
float: left;
margin-right: 5px;
width: 40px;
height: 40px;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$('#member-filter').on('show.bs.modal', function() {
$('#member-filter').off('show.bs.modal');
$(this).find('.nano').nanoScroller({ scrollTop: 0, iOSNativeScrolling: true });
$(this).find('.checkbox-card').cardCheck();
});
});
</script>
<script type="text/javascript">
function encode_str(rawStr){
var encodedStr = rawStr.replace(/[\u00A0-\u9999<>\&]/gim, function(i) {
return '&#' + i.charCodeAt(0) + ';';
});
return encodedStr;
}
function generate_member_card(role_id,member_id,member_name,member_pic_url){
return ('<li class="filter-item selected_item selected_member check-item" id="'+member_id+'">'+
'<label>'+
'<img alt="Member pic" class="member-pic" src="'+member_pic_url+'">'+
'<span class="user-name" style="position: absolute;margin-left: 45px;">'+encode_str(member_name)+'</span>'+
'</label>'+
'<input name="<%=role_field_name rescue 'role_ids[]'%>" type="hidden" value='+role_id+'>'+
'<input name="<%=member_field_name rescue 'member_ids[]'%>" type="hidden" value='+member_id+'>'+
'<input type="checkbox">'+
'</li>')
};
function generate_role_card(role_id,role_name,member_infos){
if(member_infos.length == 0)
return "";
else{
var member_cards = "";
var fa = '<span style="top: 0.8em;position: absolute;right: 0.3em; font-family: FontAwesome;cursor: pointer;" class="fa fa-caret-down"></span>';
member_infos.forEach(function(member_info){
member_cards += generate_member_card(role_id,member_info.member_id,member_info.member_name,member_info.member_pic_url);
});
return ('<li class="selected_item selected_role role" id="'+role_id+'" style="height: unset;"><div class="selected">'+
'<label>' +
'<span class="role-name">' + role_name + '</span>'+
'<span class="badge badge-info" style="position: absolute;right: 1.5em;">' + member_infos.length + '</span>' +
'</label>' +
'<input class="filter-checkbox" type="checkbox" style="width: calc(100% - 1.5em);height: 40px;">'+fa+
'<ul class="sub-items">' +
member_cards +
'</ul></div>' +
'</li>')
};
};
function select_subscribers(){
window.selected_subscriber_ids = [];
var subscriber_cards = $('#member-filter [name="subscriber_ids[]"]:checked').map(function(i, subscriber){
window.selected_subscriber_ids.push(subscriber.value);
return $(subscriber).parent().parent().prop("outerHTML").replace('check-item active','check-item');
}).toArray().join('');
$('#members_field').html('<ul id="card-list-managers" class="checkbox-card clearfix">'+subscriber_cards+'</ul>');
$('#members_field #card-list-managers .check-item').removeClass('hide');
if(subscriber_cards != ""){
$('#members_field').css('display','block');
$('#edit_member_hint').css('display','none');
}else{
$('#members_field').css('display','none');
$('#edit_member_hint').css('display','block');
}
$('#members_field').change();
$("#member-filter").modal('hide');
}
$(document).ready(function(){
$('#subscribers_form [type="submit"]').click(select_subscribers);
if(window.selected_subscriber_ids){
window.selected_subscriber_ids.forEach(function(v){
var $v = $(`#member-filter input[value="${v}"]`);
$v.prop("checked", true);
$v.parent().parent().addClass('active');
})
}
$('.search_emails').off('input').on('input',function(){
var name = $(this).val();
var $check_items = $(this).parent().find('.check-item');
$check_items.each(function(i,item){
if( $(item).find('.subscriber_email').text().search(name) == -1){
$(item).addClass('hide');
}else{
$(item).removeClass('hide');
}
})
})
});
</script>

View File

@ -0,0 +1,339 @@
<%= stylesheet_link_tag "member_select" %>
<%= stylesheet_link_tag "lib/checkbox-card" %>
<div class="mini-layout-body span10">
<div class="tab-content">
<div class="tab-pane fade in active" id="subscribers_block">
<div class="tab-pane-head">
<h4><%= t('e_paper.batch_delete_subscribers') %></h4>
</div>
<ul id="card-list-members" class="checkbox-card clearfix">
<fieldset style="background-color: white;border: 0.125em solid #666;font-family: cursive,serif;">
<%= form_tag admin_e_paper_subscribers_delete_subscribers_path , :id=>'members_form' do %>
<div id="members_field" style="display: none;margin-left: 1.5em;margin-left: 1.5em;"></div>
<p id="edit_member_hint" aligin="center" style="font-size: 1.5em;padding: 2em;text-align: center;"><%= t("e_paper.delete_subscribers_hint") %></p>
<input id="submit-type" name="type" type="hidden" value="">
<% end %>
</fieldset>
</ul>
</div>
</div>
</div>
</div>
<!-- footer -->
<div class="bottomnav clearfix">
<div class="action pull-right">
<div class="btn-group dropup">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
<%= t("select") %>
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="#" id="select_all"><i class="icon-ok"></i> <%= t("select_all") %></a></li>
<li><a href="#" id="deselect_all"><i class="icon-remove"></i> <%= t("de_select_all") %></a></li>
</ul>
</div>
<button class="btn btn-primary select_member_modal" href="#" type="button">
<i class="icons-plus"></i>
<%=t(:add)%>
</button>
<button class="btn" id="remove_subscribers" style="display: none;"><i class="icon-trash"></i> <%=t('site.edit_members.delete_from_choice') %></button>
<button class="btn btn-danger" id="remove_subscribers_forever" style="display: none;"><i class="icon-trash"></i> <%=t('e_paper.delete_subscribers') %></button>
<!-- <button class="btn" id="edit_members" style="display: none;"><i class="icon-trash"></i> <%=t('site.edit_members.edit_members') %></button> -->
</div>
</div>
<% if @thread %>
<div id="threadModal" class="modal hide fade in" tabindex="-1" role="dialog" aria-labelledby="threadModal" aria-hidden="false">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h3 id="threadModal"><%=t("site.edit_members.#{@thread.key}")%></h3>
</div>
<div class="modal-body">
<%= @thread.status[:finish_percent] %> % finished
</div>
<div class="modal-footer">
<button class="btn" id="modal-close-btn" style="width: 4em;" data-dismiss="modal" aria-hidden="true"><%=t('close')%></button>
</div>
</div>
</div>
</div>
<% end %>
<div id='select_member_modal'></div>
<script src="/assets/lib/jquery-ui-1.12.1/jquery-ui.min.js"></script>
<script type="text/javascript">
/* 修正bootstap.js和jquery-ui.js間的衝突,使tab按鈕能正常運作 */
var Button = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, $.fn.button.defaults, options)
}
Button.prototype.setState = function (state) {
var d = 'disabled'
, $el = this.$element
, data = $el.data()
, val = $el.is('input') ? 'val' : 'html'
state = state + 'Text'
data.resetText || $el.data('resetText', $el[val]())
$el[val](data[state] || this.options[state])
// push to event loop to allow forms to submit
setTimeout(function () {
state == 'loadingText' ?
$el.addClass(d).attr(d, d) :
$el.removeClass(d).removeAttr(d)
}, 0)
}
Button.prototype.toggle = function () {
var $parent = this.$element.closest('[data-toggle="buttons-radio"]')
$parent && $parent
.find('.active')
.removeClass('active')
this.$element.toggleClass('active')
}
/* BUTTON PLUGIN DEFINITION
* ======================== */
var old = $.fn.button
$.fn.button = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('button')
, options = typeof option == 'object' && option
if (!data) $this.data('button', (data = new Button(this, options)))
if (option == 'toggle') data.toggle()
else if (option) data.setState(option)
})
}
/* 修正bootstap.js和jquery-ui.js間的衝突,使tab按鈕能正常運作 */
</script>
<link rel="stylesheet" href="/assets/lib/jquery-ui-1.12.1/jquery-ui.min.css">
<script type="text/javascript">
if( Array.prototype.delete == undefined ){
Array.prototype.delete = function(v){
var idx = this.indexOf(v);
if(idx != -1){
return this.splice(idx, 1)[0];
}else{
return null;
}
}
}
function double_confirm_message(message){
if(window.confirm(message)){
if(window.confirm(message)){
return true;
}else{
return false;
};
}else{
return false;
};
};
$(document).ready(function(){
$(document).on('click','.modal-body .tab-content .check-item',function(){
if($(this).hasClass("active"))
$(this).removeClass("active");
else
$(this).addClass("active");
});
$(document).on('click',"#select_all",function(){
$('#members_field').find(".check-item").addClass("active");
$('#members_field').find(".check-item input[type=checkbox]").prop("checked","checked");
$(".bottomnav .dropup").removeClass("open");
if($('#members_field .filter-item').length != 0)
$('#remove_subscribers').css('display','inline-block');
return false;
});
$(document).on('click',"#deselect_all",function(){
$('#members_field').find(".check-item").removeClass("active");
$('#members_field').find(".check-item input[type=checkbox]").removeAttr("checked");
$(".bottomnav .dropup").removeClass("open");
$('#remove_subscribers').css('display','none');
return false;
});
$(document).on('click',".select_all_modal",function(){
$('#select_member_modal .tab-pane.active').find('.check-item:not(.hide)').addClass("active");
$('#select_member_modal .tab-pane.active').find('.check-item:not(.hide) input[type=checkbox]').prop("checked","checked");
$(".modal-body .dropup").removeClass("open");
return false;
});
$(document).on('click',".deselect_all_modal",function(){
$('#select_member_modal .tab-pane.active').find('.check-item:not(.hide)').removeClass("active");
$('#select_member_modal .tab-pane.active').find('.check-item:not(.hide) input[type=checkbox]').prop("checked","");
$(".modal-body .dropup").removeClass("open");
return false;
});
$(document).on('click',".select_member_modal",function () {
$.get("<%= admin_e_paper_subscribers_get_subscribers_modal_path %>").done(function(data){
$("#select_member_modal").html(data);
$("#member-filter").modal('show');
});
return false;
});
$('#remove_subscribers').click(function(){
var $delete_items = $('#members_field .check-item.active');
var delete_count = $delete_items.length;
for(var i = 0;i < delete_count;i++){
window.selected_subscriber_ids.delete($delete_items.eq(i).find('input').val());
$delete_items.eq(i).remove();
};
if($('#members_field .check-item').length == 0)
$('#edit_member_hint').css('display','block');
$(this).css('display','none');
if($('#members_field .check-item').length != 0){
$('#remove_subscribers_forever').css('display','inline-block');
$('#edit_members').css('display','inline-block');
}else{
$('#remove_subscribers_forever').css('display','none');
$('#edit_members').css('display','none');
};
});
$('#members_field').change(function(){
if($('#members_field .check-item').length != 0){
$('#remove_subscribers_forever').css('display','inline-block');
$('#edit_members').css('display','inline-block');
}else{
$('#remove_subscribers_forever').css('display','none');
$('#edit_members').css('display','none');
};
$('#members_field .check-item').off('click').on('click',function(){
$el = $(this).parent().parent();
if($el.hasClass("active")){
$el.removeClass('active');
if($('#members_field .check-item.active').length != 0)
$('#remove_subscribers').css('display','inline-block');
else
$('#remove_subscribers').css('display','none');
}else{
$el.addClass('active');
$('#remove_subscribers').css('display','inline-block');
}
return false;
});
$('#members_field .check-item').off('click').on('click',function(){
if($(this).hasClass("active"))
$(this).removeClass("active");
else
$(this).addClass("active");
if($('#members_field .check-item.active').length != 0)
$('#remove_subscribers').css('display','inline-block');
else
$('#remove_subscribers').css('display','none');
return false;
});
$('.fa-caret-down').off('click').on('click',function(){
if($(this).parent().hasClass('open'))
$(this).parent().removeClass("open");
else
$(this).parent().addClass("open");
})
$('#remove_subscribers_forever').off('click').on('click',function(){
var member_cards = '<ul id="card-list-managers" class="checkbox-card clearfix" style="clear: left;">'+$.map($('#members_field .check-item'),function(member_card,i){
return member_card.outerHTML;
}).join('')+'</ul>';
if(double_confirm_message('<%= t('e_paper.delete_subscribers_hint') %>')){
$("#members_form").before("<div id='dialog-confirm' title='"+"<%= t('site.edit_members.delete_hint1') %>'>"+
"<div style='clear:both;'></div><div><span class='ui-icon ui-icon-alert' style='float:left; margin:0 7px 20px 0;'></span>"+
"<%= t('e_paper.delete_subscribers_hint2') %>"+member_cards+"</div>"+
"</div>");
$( "#dialog-confirm" ).dialog({
resizable: true,
minHeight: 100,
maxHeight: 400,
modal: true,
buttons: {
"<%= t('site.edit_members.confirm') %>": function(){$('#submit-type').val('remove_subscribers_forever');$('#members_form').submit();$( this ).dialog( "close" );},
"<%= t('site.edit_members.cancel') %>": function() {
$( this ).dialog( "close" );
$( this ).remove();
}
}
});
}
});
$('#edit_members').off('click').on('click',function(){
$('#submit-type').val('select_edit_members');
$('#members_form').submit();
});
})
function update_thread(){
$.post("<%=admin_threads_get_status_path%>",{"id": "<%=params[:thread_id]%>"}).done(function(data){
var finish_percent = data["finish_percent"];
var is_finish = (data["status"] == "finish" || data["status"] == "error");
if(finish_percent){
$("#threadModal .modal-body").text(finish_percent + " % finished")
}
if(!is_finish){
window.time_out_id = window.setTimeout(update_thread, 1000);
}else{
if(window.time_out_id)
window.clearTimeout(window.time_out_id);
window.setTimeout(function(){
$("#threadModal").modal("hide");
alert(data["status"]);
}, 3000);
return;
}
});
}
if($("#threadModal").length != 0){
$("#threadModal").modal("show");
window.time_out_id = window.setTimeout(update_thread, 1000);
}
$(document).on('click','.modal-backdrop',function(){
$('.modal').modal('hide');
})
})
</script>
<style type="text/css">
.checkbox-card li:focus-within span{
color: white;
}
.fa-caret-down:before {
content: "\f078";
}
.sub-items{
display: none;
}
.open .sub-items{
display: block;
margin: 0 0.5em;
}
.selected_role .selected_member:hover{
background-color: #cccccc;
}
.selected_role .selected_member{
width: 100%;
}
li.selected_role:hover{
background-color: #696969;
}
.selected:hover{
background-color: #0088cc;
}
.selected{
height: 40px;
}
#remove_subscribers{
background: black;
color: white;
}
#edit_members{
background: green;
color: white;
}
#members_field .checkbox-card li:hover label span.user-name {
color: black;
}
#members_field .checkbox-card li:hover > label > span.user-name {
color: white;
}
</style>

View File

@ -0,0 +1,25 @@
# encoding: utf-8
wb = xlsx_package.workbook
wb.add_worksheet(name: "Subscribe") do |sheet|
heading = sheet.styles.add_style(:b => true, :locked => true)
body = sheet.styles.add_style(:font_name => '微軟正黑體')
row = []
row << t('email')
row << t('language') + " , ex: #{I18n.available_locales.map{|l| l.to_s}.join(', ')}"
sheet.add_row row, :style => heading
end
wb.add_worksheet(name: "Unsubscribe") do |sheet|
heading = sheet.styles.add_style(:b => true, :locked => true)
body = sheet.styles.add_style(:font_name => '微軟正黑體')
row = []
row << t('email')
row << t('language') + " , ex: #{I18n.available_locales.map{|l| l.to_s}.join(', ')}"
sheet.add_row row, :style => heading
end

View File

@ -0,0 +1,39 @@
# encoding: utf-8
wb = xlsx_package.workbook
wb.add_worksheet(name: "Subscribe") do |sheet|
heading = sheet.styles.add_style(:b => true, :locked => true)
body = sheet.styles.add_style(:font_name => '微軟正黑體')
row = []
row << t('email')
row << t('language')
sheet.add_row row, :style => heading
@subscribers.each do |subscriber|
row = []
row << subscriber.email
row << subscriber.language
sheet.add_row row, :style => body, :types => [:string,:string]
end
end
wb.add_worksheet(name: "Unsubscribe") do |sheet|
heading = sheet.styles.add_style(:b => true, :locked => true)
body = sheet.styles.add_style(:font_name => '微軟正黑體')
row = []
row << t('email')
row << t('language')
sheet.add_row row, :style => heading
@unsubscribers.each do |subscriber|
row = []
row << subscriber.email
row << subscriber.language
sheet.add_row row, :style => body, :types => [:string,:string]
end
end

View File

@ -1,3 +1,53 @@
<style>
.pull-right .upload-box {
background: #fff none repeat scroll 0 0;
border: 1px solid #ccc;
border-radius: 5px;
box-sizing: border-box;
height: 70px;
left: auto;
min-width: 350px;
padding: 10px;
position: absolute;
right: -1px;
top: -75px;
z-index: 1000;
}
.dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu {
top: auto;
bottom: 100%;
margin-bottom: 1px;
}
.open > .dropdown-menu {
display: block;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
display: none;
float: left;
min-width: 160px;
padding: 5px 0;
margin: 2px 0 0;
list-style: none;
background-color: #ffffff;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
*border-right-width: 2px;
*border-bottom-width: 2px;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
-webkit-box-shadow: 0 5px 10px rgb(0 0 0 / 20%);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgb(0 0 0 / 20%);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}
</style>
<%= render_filter @filter_fields, "index_table" %>
<div id="index_table">
<%= render 'index'%>

View File

@ -1,7 +1,16 @@
en:
module_name:
e_paper: "ePaper"
restful_actions:
batch_delete_subscribers: "Batch Delete Subscribers"
e_paper:
delete_subscribers_hint2: "Delete Subscribers below forever."
delete_subscribers_hint1: "Do you realy want to delete Subscribers you added forever?"
delete_subscribers_hint: "Click 『Add』 button to select Subscribers"
delete_subscribers: "Delete Subscribers"
batch_delete_subscribers: "Batch Delete Subscribers"
import: "Import"
export: "Export"
get_invalid_emails: get invalid email
get_failed_emails: get sended-failed emails
e_paper: "ePaper"

View File

@ -1,7 +1,16 @@
zh_tw:
module_name:
e_paper: 電子報
restful_actions:
batch_delete_subscribers: "批次刪除訂閱者"
e_paper:
delete_subscribers_hint2: "永久刪除以下訂閱者"
delete_subscribers_hint1: "您確定真的要永久刪除所選的訂閱者嗎?"
delete_subscribers_hint: "點選『新增』按鈕選擇訂閱者"
delete_subscribers: "刪除訂閱者"
batch_delete_subscribers: "批次刪除訂閱者"
import: "匯入"
export: "匯出"
get_invalid_emails: 顯示格式錯誤的email
get_failed_emails: 顯示寄送失敗的email
e_paper: "電子報"

View File

@ -12,6 +12,12 @@ Rails.application.routes.draw do
get "get_invalid_emails"
end
end
get 'e_paper_subscribers/export_excel', to: 'e_paper_subscribers#export_excel'
get 'e_paper_subscribers/download_excel_format', to: 'e_paper_subscribers#download_excel_format'
post 'e_paper_subscribers/import_from_excel', to: 'e_paper_subscribers#import_from_excel'
get 'e_paper_subscribers/get_subscribers_modal', to: 'e_paper_subscribers#get_subscribers_modal'
get 'e_paper_subscribers/batch_delete_subscribers', to: 'e_paper_subscribers#batch_delete_subscribers'
post 'e_paper_subscribers/delete_subscribers', to: 'e_paper_subscribers#delete_subscribers'
resources :e_paper_subscribers
resources :e_papers do
member do

View File

@ -65,9 +65,14 @@ module EPaper
:priority=>6,
:active_for_action=>{'admin/e_paper_subscribers'=>'index'},
:available_for => 'managers'
context_link 'e_paper.batch_delete_subscribers',
:link_path=>"admin_e_paper_subscribers_batch_delete_subscribers_path" ,
:priority=>7,
:active_for_action=>{'admin/e_paper_subscribers'=>'batch_delete_subscribers'},
:available_for => 'managers'
context_link 'e_paper.instructions',
:link_path=>"instructions_admin_e_papers_path" ,
:priority=>7,
:priority=>8,
:active_for_action=>{'admin/e_paper'=>'instructions'},
:available_for => 'users'
end