Update initialization of datatables

This commit is contained in:
printempw 2018-02-24 15:59:56 +08:00
parent a13d5a947a
commit efe5c6229c
9 changed files with 287 additions and 342 deletions

View File

@ -65,6 +65,8 @@ describe('tests for "customize" module', () => {
describe('tests for "players" module', () => {
const modulePath = '../admin/players';
// TODO: test initializing players table
it('show "change player texture" modal dialog', () => {
const trans = jest.fn(key => key);
const showModal = jest.fn();
@ -401,6 +403,8 @@ describe('tests for "players" module', () => {
describe('tests for "plugins" module', () => {
const modulePath = '../admin/plugins';
// TODO: test initializing plugins table
it('enable a plugin', async () => {
const fetch = jest.fn()
.mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
@ -673,6 +677,8 @@ describe('tests for "update" module', () => {
describe('tests for "users" module', () => {
const modulePath = '../admin/users';
// TODO: test initializing users table
it('change user email', async () => {
const fetch = jest.fn()
.mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
@ -1126,6 +1132,7 @@ describe('tests for "common" module', () => {
const fetch = jest.fn()
.mockReturnValue(Promise.resolve({ errno: 0, msg: 'Recorded.' }));
$.fn.dataTable = { defaults: {} };
window.document.cookie = '';
window.fetch = fetch;
window.blessing = {
@ -1148,27 +1155,4 @@ describe('tests for "common" module', () => {
await sendFeedback();
expect(fetch).toHaveBeenCalledTimes(1);
});
it('initialize data tables', () => {
$.fn.dataTable = { defaults: {} };
const initUsersTable = jest.fn();
const initPlayersTable = jest.fn();
const initPluginsTable = jest.fn();
window.initUsersTable = initUsersTable;
window.initPlayersTable = initPlayersTable;
window.initPluginsTable = initPluginsTable;
const { initTables } = require(modulePath);
document.body.innerHTML = '<div id="user-table"></div>';
initTables();
expect(initUsersTable).toBeCalled();
document.body.innerHTML = '<div id="player-table"></div>';
initTables();
expect(initPlayersTable).toBeCalled();
document.body.innerHTML = '<div id="plugin-table"></div>';
initTables();
expect($.pluginsTable).not.toBeNull();
});
});

View File

@ -1,29 +1,13 @@
/* global initUsersTable, initPlayersTable, initPluginsTable */
'use strict';
$.pluginsTable = null;
$(document).ready(initTables);
function initTables() {
$.extend(true, $.fn.dataTable.defaults, {
language: trans('vendor.datatables'),
scrollX: true,
pageLength: 25,
autoWidth: false,
processing: true,
serverSide: true
});
if ($('#user-table').length === 1) {
initUsersTable();
} else if ($('#player-table').length === 1) {
initPlayersTable();
} else if ($('#plugin-table').length === 1) {
$.pluginsTable = initPluginsTable();
}
}
$.extend(true, $.fn.dataTable.defaults, {
language: trans('vendor.datatables'),
scrollX: true,
pageLength: 25,
autoWidth: false,
processing: true,
serverSide: true
});
async function sendFeedback() {
if (document.cookie.replace(/(?:(?:^|.*;\s*)feedback_sent\s*=\s*([^;]*).*$)|^.*$/, '$1') !== '') {
@ -55,6 +39,5 @@ async function sendFeedback() {
if (process.env.NODE_ENV === 'test') {
module.exports = {
sendFeedback,
initTables
};
}

View File

@ -1,5 +1,88 @@
'use strict';
if ($('#player-table').length === 1) {
$(document).ready(initPlayersTable);
}
function initPlayersTable() {
const specificUid = getQueryString('uid');
const query = specificUid ? `?uid=${specificUid}` : '';
$('#player-table').DataTable({
ajax: url(`admin/player-data${query}`),
scrollY: ($('.content-wrapper').height() - $('.content-header').outerHeight()) * 0.7,
fnDrawCallback: () => $('[data-toggle="tooltip"]').tooltip(),
columnDefs: playersTableColumnDefs
});
}
const playersTableColumnDefs = [
{
targets: 0,
data: 'pid',
width: '1%'
},
{
targets: 1,
data: 'uid',
render: (data, type, row) => `<a href="${url('admin/users?uid=' + row.uid)}" title="${trans('admin.inspectHisOwner')}" data-toggle="tooltip" data-placement="right">${data}</span>`
},
{
targets: 2,
data: 'player_name'
},
{
targets: 3,
data: 'preference',
render: data => {
return `
<select class="form-control" onchange="changePreference.call(this)">
<option ${(data === 'default') ? 'selected=selected' : ''} value="default">Default</option>
<option ${(data === 'slim') ? 'selected=selected' : ''} value="slim">Slim</option>
</select>`;
}
},
{
targets: 4,
searchable: false,
orderable: false,
render: (data, type, row) => ['steve', 'alex', 'cape'].reduce((html, type) => {
const currentTypeTid = row[`tid_${type}`];
const imageId = `${row.pid}-${currentTypeTid}`;
if (currentTypeTid === 0) {
return html + `<img id="${imageId}" width="64" />`;
} else {
return html + `
<a href="${ url('skinlib/show/' + currentTypeTid) }">
<img id="${imageId}" width="64" src="${url('/preview/64/' + currentTypeTid)}.png" />
</a>`;
}
}, '')
},
{
targets: 5,
data: 'last_modified'
},
{
targets: 6,
searchable: false,
orderable: false,
render: (data, type, row) => `
<div class="btn-group">
<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
${ trans('admin.operationsTitle') } <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a style="cursor: pointer" onclick="changeTexture(${row.pid}, '${row.player_name}');">${trans('admin.changeTexture')}</a></li>
<li><a style="cursor: pointer" onclick="changePlayerName(${row.pid}, '${row.player_name}');">${trans('admin.changePlayerName')}</a></li>
<li><a style="cursor: pointer" onclick="changeOwner(${row.pid});">${trans('admin.changeOwner')}</a></li>
</ul>
</div>
<a class="btn btn-danger btn-sm" style="cursor: pointer" onclick="deletePlayer(${row.pid});">${trans('admin.deletePlayer')}</a>`
}
];
async function changePreference() {
try {
const { errno, msg } = await fetch({
@ -100,7 +183,7 @@ async function changePlayerName(pid, oldName) {
}
} catch (error) {
showAjaxError(error);
}
}
}
function changeOwner(pid) {
@ -193,6 +276,7 @@ async function deletePlayer(pid) {
if (process.env.NODE_ENV === 'test') {
module.exports = {
initPlayersTable,
changeOwner,
showNicknameInSwal,
deletePlayer,

View File

@ -1,5 +1,67 @@
'use strict';
if ($('#plugin-table').length === 1) {
$(document).ready(initPluginsTable);
}
function initPluginsTable() {
$.pluginsTable = $('#plugin-table').DataTable({
ajax: url('admin/plugins/data'),
fnDrawCallback: () => $('[data-toggle="tooltip"]').tooltip(),
columnDefs: pluginsTableColumnDefs
});
}
const pluginsTableColumnDefs = [
{
targets: 0,
data: 'title'
},
{
targets: 1,
data: 'description',
width: '35%'
},
{
targets: 2,
data: 'author',
render: data => isEmpty(data.url) ? data.author : `<a href="${data.url}" target="_blank">${data.author}</a>`
},
{
targets: 3,
data: 'version'
},
{
targets: 4,
data: 'status'
},
{
targets: 5,
data: 'operations',
searchable: false,
orderable: false,
render: (data, type, row) => {
let toggleButton, configViewButton;
if (data.enabled) {
toggleButton = `<a class="btn btn-warning btn-sm" onclick="disablePlugin('${row.name}');">${trans('admin.disablePlugin')}</a>`;
} else {
toggleButton = `<a class="btn btn-primary btn-sm" onclick="enablePlugin('${row.name}');">${trans('admin.enablePlugin')}</a>`;
}
if (data.enabled && data.hasConfigView) {
configViewButton = `<a class="btn btn-default btn-sm" href="${url('/')}admin/plugins/config/${row.name}">${trans('admin.configurePlugin')}</a>`;
} else {
configViewButton = `<a class="btn btn-default btn-sm" disabled="disabled" title="${trans('admin.noPluginConfigNotice')}" data-toggle="tooltip" data-placement="top">${trans('admin.configurePlugin')}</a>`;
}
const deletePluginButton = `<a class="btn btn-danger btn-sm" onclick="deletePlugin('${row.name}');">${trans('admin.deletePlugin')}</a>`;
return toggleButton + configViewButton + deletePluginButton;
}
}
];
async function enablePlugin(name) {
try {
const { errno, msg } = await fetch({
@ -69,6 +131,7 @@ async function deletePlugin(name) {
if (process.env.NODE_ENV === 'test') {
module.exports = {
initPluginsTable,
deletePlugin,
enablePlugin,
disablePlugin,

View File

@ -1,289 +0,0 @@
'use strict';
function initUsersTable() {
const uid = getQueryString('uid');
const dataUrl = url('admin/user-data') + (uid ? `?uid=${uid}` : '');
$('#user-table').DataTable({
ajax: dataUrl,
scrollY: ($('.content-wrapper').height() - $('.content-header').outerHeight()) * 0.7,
fnDrawCallback: () => {
$('[data-toggle="tooltip"]').tooltip();
},
rowCallback: (row, data) => {
$(row).attr('id', `user-${data.uid}`);
},
columnDefs: [
{
targets: 0,
data: 'uid',
width: '1%'
},
{
targets: 1,
data: 'email'
},
{
targets: 2,
data: 'nickname'
},
{
targets: 3,
data: 'score',
render: data => {
return `<input type="number" class="form-control score" value="${data}" title="${trans('admin.scoreTip')}" data-toggle="tooltip" data-placement="right">`;
}
},
{
targets: 4,
data: 'players_count',
searchable: false,
orderable: false,
render: (data, type, row) => {
return `<span title="${trans('admin.doubleClickToSeePlayers')}"
style="cursor: pointer;"
ondblclick="window.location.href = '${url('admin/players?uid=') + row.uid}'"
data-toggle="tooltip" data-placement="top">${data}</span>`;
}
},
{
targets: 5,
data: 'permission',
className: 'status',
render: data => {
switch (data) {
case -1:
return trans('admin.banned');
case 0:
return trans('admin.normal');
case 1:
return trans('admin.admin');
case 2:
return trans('admin.superAdmin');
}
}
},
{
targets: 6,
data: 'register_at'
},
{
targets: 7,
data: 'operations',
searchable: false,
orderable: false,
render: (data, type, row) => {
let adminOption = '', bannedOption = '', deleteUserButton;
if (row.permission !== 2) {
if (data === 2) {
if (row.permission === 1) {
adminOption = `<li class="divider"></li>
<li><a id="admin-${row.uid}" data="admin" style="cursor: pointer" onclick="changeAdminStatus(${row.uid});">${trans('admin.unsetAdmin')}</a></li>`;
} else {
adminOption = `<li class="divider"></li>
<li><a id="admin-${row.uid}" data="normal" style="cursor: pointer" onclick="changeAdminStatus(${row.uid});">${trans('admin.setAdmin')}</a></li>`;
}
}
if (row.permission === -1) {
bannedOption = `<li class="divider"></li>
<li><a id="ban-${row.uid}" data="banned" style="cursor: pointer" onclick="changeBanStatus(${row.uid});">${trans('admin.unban')}</a></li>`;
} else {
bannedOption = `<li class="divider"></li>
<li><a id="ban-${row.uid}" data="normal" style="cursor: pointer" onclick="changeBanStatus(${row.uid});">${trans('admin.ban')}</a></li>`;
}
}
if (data === 2) {
if (row.permission === 2) {
deleteUserButton = `
<a class="btn btn-danger btn-sm" disabled="disabled" data-toggle="tooltip" data-placement="bottom" title="${trans('admin.cannotDeleteSuperAdmin')}">${trans('admin.deleteUser')}</a>`;
} else {
deleteUserButton = `
<a class="btn btn-danger btn-sm" style="cursor: pointer" onclick="deleteUserAccount(${row.uid});">${trans('admin.deleteUser')}</a>`;
}
} else {
if (row.permission === 1 || row.permission === 2) {
deleteUserButton = `
<a class="btn btn-danger btn-sm" disabled="disabled" data-toggle="tooltip" data-placement="bottom" title="${trans('admin.cannotDeleteAdmin')}">${trans('admin.deleteUser')}</a>`;
} else {
deleteUserButton = `
<a class="btn btn-danger btn-sm" style="cursor: pointer" onclick="deleteUserAccount(${row.uid});">${trans('admin.deleteUser')}</a>`;
}
}
return `
<div class="btn-group">
<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
${trans('admin.operationsTitle')} <span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a style="cursor: pointer" onclick="changeUserEmail(${row.uid});">${trans('admin.changeEmail')}</a></li>
<li><a style="cursor: pointer" onclick="changeUserNickName(${row.uid});">${trans('admin.changeNickName')}</a></li>
<li><a style="cursor: pointer" onclick="changeUserPwd(${row.uid});">${trans('admin.changePassword')}</a></li>
${adminOption}${bannedOption}
</ul>
</div>
${deleteUserButton}`;
}
}
]
});
}
function initPlayersTable() {
const uid = getQueryString('uid');
const dataUrl = url('admin/player-data') + (uid ? `?uid=${uid}` : '');
$('#player-table').DataTable({
ajax: dataUrl,
scrollY: ($('.content-wrapper').height() - $('.content-header').outerHeight()) * 0.7,
fnDrawCallback: () => {
$('[data-toggle="tooltip"]').tooltip();
},
columnDefs: [
{
targets: 0,
data: 'pid',
width: '1%'
},
{
targets: 1,
data: 'uid',
render: (data, type, row) => {
return `<span title="${trans('admin.doubleClickToSeeUser')}"
style="cursor: pointer;"
ondblclick="window.location.href = '${url('admin/users?uid=') + row.uid}'"
data-toggle="tooltip" data-placement="top">${data}</span>`;
}
},
{
targets: 2,
data: 'player_name'
},
{
targets: 3,
data: 'preference',
render: data => {
return `
<select class="form-control" onchange="changePreference.call(this)">
<option ${(data === 'default') ? 'selected=selected' : ''} value="default">Default</option>
<option ${(data === 'slim') ? 'selected=selected' : ''} value="slim">Slim</option>
</select>`;
}
},
{
targets: 4,
searchable: false,
orderable: false,
render: (data, type, row) => {
const html = { steve: '', alex: '', cape: '' };
['steve', 'alex', 'cape'].forEach(textureType => {
if (row['tid_' + textureType] === 0) {
html[textureType] = `<img id="${row.pid}-${row['tid_' + textureType]}" width="64" />`;
} else {
html[textureType] = `
<a href="${url('/')}skinlib/show/${row['tid_' + textureType]}">
<img id="${row.pid}-${row['tid_' + textureType]}" width="64" src="${url('/')}preview/64/${row['tid_' + textureType]}.png" />
</a>`;
}
});
return html.steve + html.alex + html.cape;
}
},
{
targets: 5,
data: 'last_modified'
},
{
targets: 6,
searchable: false,
orderable: false,
render: (data, type, row) => {
return `
<div class="btn-group">
<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
${trans('admin.operationsTitle')} <span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a style="cursor: pointer" onclick="changeTexture(${row.pid}, '${row.player_name}');">${trans('admin.changeTexture')}</a></li>
<li><a style="cursor: pointer" onclick="changePlayerName(${row.pid}, '${row.player_name}');">${trans('admin.changePlayerName')}</a></li>
<li><a style="cursor: pointer" onclick="changeOwner(${row.pid});">${trans('admin.changeOwner')}</a></li>
</ul>
</div>
<a class="btn btn-danger btn-sm" style="cursor: pointer" onclick="deletePlayer(${row.pid});">${trans('admin.deletePlayer')}</a>`;
}
}
]
});
}
function initPluginsTable() {
return $('#plugin-table').DataTable({
ajax: url('admin/plugins/data'),
fnDrawCallback: () => {
$('[data-toggle="tooltip"]').tooltip();
},
columnDefs: [
{
targets: 0,
data: 'title'
},
{
targets: 1,
data: 'description',
width: '35%'
},
{
targets: 2,
data: 'author',
render: data => {
if (data.url === '' || data.url === null) {
return data.author;
} else {
return `<a href="${data.url}" target="_blank">${data.author}</a>`;
}
}
},
{
targets: 3,
data: 'version'
},
{
targets: 4,
data: 'status'
},
{
targets: 5,
data: 'operations',
searchable: false,
orderable: false,
render: (data, type, row) => {
let switchEnableButton, configViewButton;
if (data.enabled) {
switchEnableButton = `
<a class="btn btn-warning btn-sm" style="cursor: pointer" onclick="disablePlugin('${row.name}');">${trans('admin.disablePlugin')}</a>`;
} else {
switchEnableButton = `
<a class="btn btn-primary btn-sm" style="cursor: pointer" onclick="enablePlugin('${row.name}');">${trans('admin.enablePlugin')}</a>`;
}
if (data.enabled && data.hasConfigView) {
configViewButton = `
<a class="btn btn-default btn-sm" href="${url('/')}admin/plugins/config/${row.name}">${trans('admin.configurePlugin')}</a>`;
} else {
configViewButton = `
<a class="btn btn-default btn-sm" disabled="disabled" title="${trans('admin.noPluginConfigNotice')}" data-toggle="tooltip" data-placement="top">${trans('admin.configurePlugin')}</a>`;
}
const deletePluginButton = `
<a class="btn btn-danger btn-sm" style="cursor: pointer" onclick="deletePlugin('${row.name}');">${trans('admin.deletePlugin')}</a>`;
return switchEnableButton + configViewButton + deletePluginButton;
}
}
]
});
}
if (process.env.NODE_ENV === 'test') {
module.exports = {
initUsersTable,
initPlayersTable,
initPluginsTable,
};
}

View File

@ -1,5 +1,122 @@
'use strict';
if ($('#user-table').length === 1) {
$(document).ready(initUsersTable);
}
function initUsersTable() {
const specificUid = getQueryString('uid');
const query = specificUid ? `?uid=${specificUid}` : '';
$('#user-table').DataTable({
ajax: url(`admin/user-data${query}`),
scrollY: ($('.content-wrapper').height() - $('.content-header').outerHeight()) * 0.7,
fnDrawCallback: () => $('[data-toggle="tooltip"]').tooltip(),
rowCallback: (row, data) => $(row).attr('id', `user-${data.uid}`),
columnDefs: usersTableColumnDefs
});
}
const userPermissions = {
'-1': 'banned',
'0': 'normal',
'1': 'admin',
'2': 'superAdmin'
};
const usersTableColumnDefs = [
{
targets: 0,
data: 'uid',
width: '1%'
},
{
targets: 1,
data: 'email'
},
{
targets: 2,
data: 'nickname'
},
{
targets: 3,
data: 'score',
render: data => `<input type="number" class="form-control score" value="${data}" title="${trans('admin.scoreTip')}" data-toggle="tooltip" data-placement="right">`
},
{
targets: 4,
data: 'players_count',
searchable: false,
orderable: false,
render: (data, type, row) => `<a href="${url('admin/players?uid='+row.uid)}" title="${trans('admin.inspectHisPlayers')}" data-toggle="tooltip" data-placement="right">${data}</span>`
},
{
targets: 5,
data: 'permission',
className: 'status',
render: data => trans('admin.' + userPermissions[data])
},
{
targets: 6,
data: 'register_at'
},
{
targets: 7,
data: 'operations',
searchable: false,
orderable: false,
render: renderUsersTableOperations
}
];
function renderUsersTableOperations(currentUserPermission, type, row) {
let adminOption = '', bannedOption = '', deleteUserButton;
if (row.permission !== 2) {
// Only SUPER admins are allowed to set/unset admins
if (currentUserPermission === 2) {
const adminStatus = row.permission === 1 ? 'admin' : 'normal';
adminOption = `<li class="divider"></li> <li><a id="admin-${row.uid}" data="${adminStatus}" onclick="changeAdminStatus(${row.uid});">
${ adminStatus === 'admin' ? trans('admin.unsetAdmin') : trans('admin.setAdmin') }
</a></li>`;
}
const banStatus = row.permission === -1 ? 'banned' : 'normal';
bannedOption = `<li class="divider"></li> <li><a id="ban-${row.uid}" data="${banStatus}" onclick="changeBanStatus(${row.uid});">
${ banStatus === 'banned' ? trans('admin.unban') : trans('admin.ban') }
</a></li>`;
}
if (currentUserPermission === 2) {
if (row.permission === 2) {
deleteUserButton = `<a class="btn btn-danger btn-sm" disabled="disabled" data-toggle="tooltip" data-placement="bottom" title="${trans('admin.cannotDeleteSuperAdmin')}">${trans('admin.deleteUser')}</a>`;
} else {
deleteUserButton = `<a class="btn btn-danger btn-sm" onclick="deleteUserAccount(${row.uid});">${trans('admin.deleteUser')}</a>`;
}
} else {
if (row.permission === 1 || row.permission === 2) {
deleteUserButton = `<a class="btn btn-danger btn-sm" disabled="disabled" data-toggle="tooltip" data-placement="bottom" title="${trans('admin.cannotDeleteAdmin')}">${trans('admin.deleteUser')}</a>`;
} else {
deleteUserButton = `<a class="btn btn-danger btn-sm" onclick="deleteUserAccount(${row.uid});">${trans('admin.deleteUser')}</a>`;
}
}
return `
<div class="btn-group">
<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
${trans('admin.operationsTitle')} <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a onclick="changeUserEmail(${row.uid});">${trans('admin.changeEmail')}</a></li>
<li><a onclick="changeUserNickName(${row.uid});">${trans('admin.changeNickName')}</a></li>
<li><a onclick="changeUserPwd(${row.uid});">${trans('admin.changePassword')}</a></li>
${adminOption}
${bannedOption}
</ul>
</div>
${deleteUserButton}`;
}
async function changeUserEmail(uid) {
const dom = $(`tr#user-${uid} > td:nth-child(2)`);
let newUserEmail = '';
@ -217,6 +334,7 @@ $('body').on('keypress', '.score', function(event){
if (process.env.NODE_ENV === 'test') {
module.exports = {
initUsersTable,
changeUserPwd,
changeBanStatus,
changeUserEmail,

View File

@ -24,6 +24,8 @@ td {
img {
margin-left: 10px;
}
cursor: pointer;
}
a:first-child > img {

View File

@ -140,8 +140,8 @@
newUserPassword: 'Please enter the new password:',
deleteUserNotice: 'Are you sure to delete this user? It\' permanent.',
scoreTip: 'Press ENTER to submit new score',
doubleClickToSeeUser: 'Double click to see info of this user',
doubleClickToSeePlayers: 'Double click to see his/her players',
inspectHisOwner: 'Click to inspect the owner of this player',
inspectHisPlayers: 'Click to inspect the players he owns',
// Status
banned: 'Banned',

View File

@ -142,8 +142,8 @@
newUserPassword: '请输入新密码:',
deleteUserNotice: '真的要删除此用户吗?此操作不可恢复',
scoreTip: '输入修改后的积分,回车提交',
doubleClickToSeeUser: '双击可查看该用户的信息',
doubleClickToSeePlayers: '双击可查看该用户的角色',
inspectHisOwner: '点击查看该角色的所有者',
inspectHisPlayers: '点击查看该用户的角色',
// Status
banned: '封禁',