mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 08:36:12 +01:00
feat: #8460, export groups members as csv
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
"hidden": "Hidden",
|
||||
"private": "Private",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"download-csv": "Download CSV",
|
||||
"search-placeholder": "Search",
|
||||
"create": "Create Group",
|
||||
"description-placeholder": "A short description about your group",
|
||||
|
||||
@@ -2276,6 +2276,26 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
/api/admin/groups/{groupname}/csv:
|
||||
get:
|
||||
tags:
|
||||
- admin
|
||||
summary: Get members of a group (.csv)
|
||||
parameters:
|
||||
- in: header
|
||||
name: referer
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
example: /admin/manage/groups
|
||||
responses:
|
||||
"200":
|
||||
description: "A CSV file containing all users in the group"
|
||||
content:
|
||||
text/csv:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
/api/admin/analytics:
|
||||
get:
|
||||
tags:
|
||||
|
||||
@@ -52,7 +52,7 @@ define('admin/manage/groups', ['translator', 'benchpress'], function (translator
|
||||
});
|
||||
});
|
||||
|
||||
$('.groups-list').on('click', 'button[data-action]', function () {
|
||||
$('.groups-list').on('click', '[data-action]', function () {
|
||||
var el = $(this);
|
||||
var action = el.attr('data-action');
|
||||
var groupName = el.parents('tr[data-groupname]').attr('data-groupname');
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const nconf = require('nconf');
|
||||
const validator = require('validator');
|
||||
|
||||
const db = require('../../database');
|
||||
const user = require('../../user');
|
||||
const groups = require('../../groups');
|
||||
const meta = require('../../meta');
|
||||
const pagination = require('../../pagination');
|
||||
const events = require('../../events');
|
||||
|
||||
const groupsController = module.exports;
|
||||
|
||||
@@ -60,3 +63,29 @@ async function getGroupNames() {
|
||||
const groupNames = await db.getSortedSetRange('groups:createtime', 0, -1);
|
||||
return groupNames.filter(name => name !== 'registered-users' && !groups.isPrivilegeGroup(name));
|
||||
}
|
||||
|
||||
groupsController.getCSV = async function (req, res) {
|
||||
const referer = req.headers.referer;
|
||||
|
||||
if (!referer || !referer.replace(nconf.get('url'), '').startsWith('/admin/manage/groups')) {
|
||||
return res.status(403).send('[[error:invalid-origin]]');
|
||||
}
|
||||
await events.log({
|
||||
type: 'getGroupCSV',
|
||||
uid: req.uid,
|
||||
ip: req.ip,
|
||||
});
|
||||
const groupName = req.params.groupname;
|
||||
const members = (await groups.getMembersOfGroups([groupName]))[0];
|
||||
const fields = ['email', 'username', 'uid'];
|
||||
const userData = await user.getUsersFields(members, fields);
|
||||
let csvContent = fields.join(',') + '\n';
|
||||
csvContent += userData.reduce((memo, user) => {
|
||||
memo += user.email + ',' + user.username + ',' + user.uid + '\n';
|
||||
return memo;
|
||||
}, '');
|
||||
|
||||
res.attachment(validator.escape(groupName) + '_members.csv');
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
res.end(csvContent);
|
||||
};
|
||||
|
||||
@@ -69,6 +69,7 @@ module.exports = function (app, middleware, controllers) {
|
||||
|
||||
function apiRoutes(router, middleware, controllers) {
|
||||
router.get('/api/admin/users/csv', middleware.authenticate, helpers.tryRoute(controllers.admin.users.getCSV));
|
||||
router.get('/api/admin/groups/:groupname/csv', middleware.authenticate, helpers.tryRoute(controllers.admin.groups.getCSV));
|
||||
router.get('/api/admin/analytics', middleware.authenticate, helpers.tryRoute(controllers.admin.dashboard.getAnalytics));
|
||||
|
||||
const multipart = require('connect-multiparty');
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<!-- BEGIN groups -->
|
||||
<tr data-groupname="{groups.displayName}">
|
||||
<td>
|
||||
{groups.displayName}
|
||||
<a href="{config.relative_path}/admin/manage/groups/{groups.nameEncoded}">{groups.displayName}</a>
|
||||
</td>
|
||||
<td>
|
||||
<span class="label label-default" style="color:{groups.textColor}; background-color: {groups.labelColor};"><!-- IF groups.icon --><i class="fa {groups.icon}"></i> <!-- ENDIF groups.icon -->{groups.userTitle}</span>
|
||||
@@ -42,13 +42,15 @@
|
||||
{groups.memberCount}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group ">
|
||||
<a href="{config.relative_path}/admin/manage/groups/{groups.nameEncoded}" class="btn btn-default btn-xs">
|
||||
<i class="fa fa-edit"></i> [[admin/manage/groups:edit]]
|
||||
</a>
|
||||
<!-- IF !groups.system -->
|
||||
<button class="btn btn-danger btn-xs" data-action="delete"><i class="fa fa-times"></i></button>
|
||||
<!-- ENDIF !groups.system -->
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown" type="button"><i class="fa fa-fw fa-ellipsis-h"></i></button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li><a href="{config.relative_path}/admin/manage/groups/{groups.nameEncoded}"><i class="fa fa-fw fa-edit"></i> [[admin/manage/groups:edit]]</a></li>
|
||||
<li><a href="{config.relative_path}/api/admin/groups/{groups.nameEncoded}/csv"><i class="fa fa-fw fa-file-text"></i> [[admin/manage/groups:download-csv]]</a></li>
|
||||
<!-- IF !groups.system -->
|
||||
<li data-action="delete"><a href="#"><i class="fa fa-fw fa-times"></i> [[admin/manage/groups:delete]]</a></li>
|
||||
<!-- ENDIF !groups.system -->
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -364,6 +364,43 @@ describe('Admin Controllers', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should return 403 if no referer', function (done) {
|
||||
request(nconf.get('url') + '/api/admin/groups/administrators/csv', { jar: jar }, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 403);
|
||||
assert.equal(body, '[[error:invalid-origin]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return 403 if referer is not /api/admin/groups/administrators/csv', function (done) {
|
||||
request(nconf.get('url') + '/api/admin/groups/administrators/csv', {
|
||||
jar: jar,
|
||||
headers: {
|
||||
referer: '/topic/1/test',
|
||||
},
|
||||
}, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 403);
|
||||
assert.equal(body, '[[error:invalid-origin]]');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load /api/admin/groups/administrators/csv', function (done) {
|
||||
request(nconf.get('url') + '/api/admin/groups/administrators/csv', {
|
||||
jar: jar,
|
||||
headers: {
|
||||
referer: nconf.get('url') + '/admin/manage/groups',
|
||||
},
|
||||
}, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert(body);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load /admin/advanced/hooks', function (done) {
|
||||
request(nconf.get('url') + '/api/admin/advanced/hooks', { jar: jar, json: true }, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
|
||||
Reference in New Issue
Block a user