mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-12-18 03:25:31 -05:00
FEATURE: export user list
This commit is contained in:
parent
2850ce46b8
commit
d0736a06b6
11 changed files with 222 additions and 1 deletions
26
app/assets/javascripts/admin/models/export_csv.js
Normal file
26
app/assets/javascripts/admin/models/export_csv.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/**
|
||||||
|
Data model for representing an export
|
||||||
|
|
||||||
|
@class ExportCsv
|
||||||
|
@extends Discourse.Model
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.ExportCsv = Discourse.Model.extend({});
|
||||||
|
|
||||||
|
Discourse.ExportCsv.reopenClass({
|
||||||
|
/**
|
||||||
|
Exports user list
|
||||||
|
|
||||||
|
@method export_user_list
|
||||||
|
**/
|
||||||
|
exportUserList: function() {
|
||||||
|
return Discourse.ajax("/admin/export_csv/users.json").then(function(result) {
|
||||||
|
if (result.success) {
|
||||||
|
bootbox.alert(I18n.t("admin.export_csv.success"));
|
||||||
|
} else {
|
||||||
|
bootbox.alert(I18n.t("admin.export_csv.failed"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
|
@ -9,6 +9,12 @@
|
||||||
Discourse.AdminUsersListRoute = Discourse.Route.extend({
|
Discourse.AdminUsersListRoute = Discourse.Route.extend({
|
||||||
renderTemplate: function() {
|
renderTemplate: function() {
|
||||||
this.render('admin/templates/users_list', {into: 'admin/templates/admin'});
|
this.render('admin/templates/users_list', {into: 'admin/templates/admin'});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
exportUsers: function() {
|
||||||
|
Discourse.ExportCsv.exportUserList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -57,7 +63,7 @@ Discourse.AdminUsersListNewRoute = Discourse.Route.extend({
|
||||||
/**
|
/**
|
||||||
Handles the route that lists pending users.
|
Handles the route that lists pending users.
|
||||||
|
|
||||||
@class AdminUsersListNewRoute
|
@class AdminUsersListPendingRoute
|
||||||
@extends Discourse.Route
|
@extends Discourse.Route
|
||||||
@namespace Discourse
|
@namespace Discourse
|
||||||
@module Discourse
|
@module Discourse
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
<div class='username controls'>
|
<div class='username controls'>
|
||||||
{{text-field value=username placeholderKey="search_hint"}}
|
{{text-field value=username placeholderKey="search_hint"}}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="pull-right">
|
||||||
|
<button {{action exportUsers}} class="btn" title="{{i18n admin.export_csv.users.title}}"><i class="fa fa-download"></i>{{i18n admin.export_csv.users.text}}</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="admin-container">
|
<div class="admin-container">
|
||||||
|
|
20
app/controllers/admin/export_csv_controller.rb
Normal file
20
app/controllers/admin/export_csv_controller.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
class Admin::ExportCsvController < Admin::AdminController
|
||||||
|
|
||||||
|
skip_before_filter :check_xhr, only: [:download]
|
||||||
|
|
||||||
|
def export_user_list
|
||||||
|
# export csv file in a background thread
|
||||||
|
Jobs.enqueue(:export_csv_file, entity: 'user', user_id: current_user.id)
|
||||||
|
render json: success_json
|
||||||
|
end
|
||||||
|
|
||||||
|
def download
|
||||||
|
filename = params.fetch(:id)
|
||||||
|
if export_csv_path = ExportCsv.get_download_path(filename)
|
||||||
|
send_file export_csv_path
|
||||||
|
else
|
||||||
|
render nothing: true, status: 404
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
75
app/jobs/regular/export_csv_file.rb
Normal file
75
app/jobs/regular/export_csv_file.rb
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
require 'csv'
|
||||||
|
require_dependency 'system_message'
|
||||||
|
|
||||||
|
module Jobs
|
||||||
|
|
||||||
|
class ExportCsvFile < Jobs::Base
|
||||||
|
sidekiq_options retry: false
|
||||||
|
attr_accessor :current_user
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@file_name = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute(args)
|
||||||
|
entity = args[:entity]
|
||||||
|
@current_user = User.find_by(id: args[:user_id])
|
||||||
|
|
||||||
|
raise Discourse::InvalidParameters.new(:entity) if entity.blank?
|
||||||
|
|
||||||
|
case entity
|
||||||
|
when 'user'
|
||||||
|
query = ::AdminUserIndexQuery.new
|
||||||
|
user_data = query.find_users_query.to_a
|
||||||
|
|
||||||
|
data = Hash.new do |hash, key|
|
||||||
|
hash[key] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
user_data.each do |user|
|
||||||
|
id = user['id']
|
||||||
|
email = user['email']
|
||||||
|
data[id] = email
|
||||||
|
end
|
||||||
|
data = data.to_a
|
||||||
|
end
|
||||||
|
|
||||||
|
if data && data.length > 0
|
||||||
|
set_file_path
|
||||||
|
write_csv_file(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
notify_user
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_file_path
|
||||||
|
@file_name = "export_#{SecureRandom.hex(4)}.csv"
|
||||||
|
# ensure directory exists
|
||||||
|
dir = File.dirname("#{ExportCsv.base_directory}/#{@file_name}")
|
||||||
|
FileUtils.mkdir_p(dir) unless Dir.exists?(dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_csv_file(data)
|
||||||
|
# write to CSV file
|
||||||
|
CSV.open(File.expand_path("#{ExportCsv.base_directory}/#{@file_name}", __FILE__), "w") do |csv|
|
||||||
|
data.each do |value|
|
||||||
|
csv << [value[1]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_user
|
||||||
|
if @current_user
|
||||||
|
if @file_name != "" && File.exists?("#{ExportCsv.base_directory}/#{@file_name}")
|
||||||
|
SystemMessage.create_from_system_user(@current_user, :csv_export_succeeded, download_link: "#{Discourse.base_url}/admin/export_csv/#{@file_name}/download", file_name: @file_name)
|
||||||
|
else
|
||||||
|
SystemMessage.create_from_system_user(@current_user, :csv_export_failed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
16
app/models/export_csv.rb
Normal file
16
app/models/export_csv.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
class ExportCsv
|
||||||
|
|
||||||
|
def self.get_download_path(filename)
|
||||||
|
path = File.join(ExportCsv.base_directory, filename)
|
||||||
|
if File.exists?(path)
|
||||||
|
return path
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.base_directory
|
||||||
|
File.join(Rails.root, "public", "uploads", "csv_exports", RailsMultisite::ConnectionManagement.current_db)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1603,6 +1603,13 @@ en:
|
||||||
title: "Rollback the database to previous working state"
|
title: "Rollback the database to previous working state"
|
||||||
confirm: "Are your sure you want to rollback the database to the previous working state?"
|
confirm: "Are your sure you want to rollback the database to the previous working state?"
|
||||||
|
|
||||||
|
export_csv:
|
||||||
|
users:
|
||||||
|
text: "Export Users"
|
||||||
|
title: "Export user list in a CSV file."
|
||||||
|
success: "Export has been initiated, you will be notified shortly with progress."
|
||||||
|
failed: "Export failed. Please check the logs."
|
||||||
|
|
||||||
customize:
|
customize:
|
||||||
title: "Customize"
|
title: "Customize"
|
||||||
long_title: "Site Customizations"
|
long_title: "Site Customizations"
|
||||||
|
|
|
@ -1387,6 +1387,17 @@ en:
|
||||||
%{logs}
|
%{logs}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
csv_export_succeeded:
|
||||||
|
subject_template: "Data Export completed successfully"
|
||||||
|
text_body_template: |
|
||||||
|
The data export was successful.
|
||||||
|
|
||||||
|
Download CSV file: <a class="attachment" href="%{download_link}">%{file_name}</a>
|
||||||
|
|
||||||
|
csv_export_failed:
|
||||||
|
subject_template: "Export failed"
|
||||||
|
text_body_template: "The export has failed. Please check the logs."
|
||||||
|
|
||||||
email_reject_trust_level:
|
email_reject_trust_level:
|
||||||
subject_template: "Email issue -- Insufficient Trust Level"
|
subject_template: "Email issue -- Insufficient Trust Level"
|
||||||
text_body_template: |
|
text_body_template: |
|
||||||
|
|
|
@ -145,6 +145,15 @@ Discourse::Application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources :export_csv, constraints: AdminConstraint.new do
|
||||||
|
member do
|
||||||
|
get "download" => "export_csv#download", constraints: { id: /[^\/]+/ }
|
||||||
|
end
|
||||||
|
collection do
|
||||||
|
get "users" => "export_csv#export_user_list"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
resources :badges, constraints: AdminConstraint.new do
|
resources :badges, constraints: AdminConstraint.new do
|
||||||
collection do
|
collection do
|
||||||
get "types" => "badges#badge_types"
|
get "types" => "badges#badge_types"
|
||||||
|
|
35
spec/controllers/admin/export_csv_controller_spec.rb
Normal file
35
spec/controllers/admin/export_csv_controller_spec.rb
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
describe Admin::ExportCsvController do
|
||||||
|
|
||||||
|
it "is a subclass of AdminController" do
|
||||||
|
(Admin::ExportCsvController < Admin::AdminController).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:export_filename) { "export_b6a2bc87.csv" }
|
||||||
|
|
||||||
|
context "while logged in as an admin" do
|
||||||
|
|
||||||
|
before { @admin = log_in(:admin) }
|
||||||
|
|
||||||
|
describe ".download" do
|
||||||
|
|
||||||
|
it "uses send_file to transmit the export file" do
|
||||||
|
controller.stubs(:render)
|
||||||
|
export = ExportCsv.new()
|
||||||
|
ExportCsv.expects(:get_download_path).with(export_filename).returns(export)
|
||||||
|
subject.expects(:send_file).with(export)
|
||||||
|
get :download, id: export_filename
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 404 when the export file does not exist" do
|
||||||
|
ExportCsv.expects(:get_download_path).returns(nil)
|
||||||
|
get :download, id: export_filename
|
||||||
|
response.should be_not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
13
spec/jobs/export_csv_file_spec.rb
Normal file
13
spec/jobs/export_csv_file_spec.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Jobs::ExportCsvFile do
|
||||||
|
|
||||||
|
context '.execute' do
|
||||||
|
|
||||||
|
it 'raises an error when the entity is missing' do
|
||||||
|
lambda { Jobs::ExportCsvFile.new.execute(user_id: "1") }.should raise_error(Discourse::InvalidParameters)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in a new issue