-
+
Order Character Screen
Change the order in which the characters appear in your in-game character selection screen, by dragging them, matches in-game position.
-
@@ -69,6 +76,31 @@ public function getHomeRender($characters) {
+
+
emChar;
}
+ /**
+ *
+ * @return \Doctrine\ORM\EntityManager
+ */
+ public function getWorldEm()
+ {
+ return $this->emWorld;
+ }
+
/**
*
* @return string
diff --git a/src/acore-wp-plugin/src/Manager/Opts.php b/src/acore-wp-plugin/src/Manager/Opts.php
index a57257ad4..abcc3d3bb 100644
--- a/src/acore-wp-plugin/src/Manager/Opts.php
+++ b/src/acore-wp-plugin/src/Manager/Opts.php
@@ -47,6 +47,8 @@ class Opts {
[81, 360], // else, 360 days
];
public $acore_name_unlock_allowed_banned_names_table="";
+ public $acore_pdump_enabled="0";
+ public $acore_bug_report_url="https://github.com/azerothcore/acore-cms/issues/new";
public $acore_security_logging="0";
public $acore_allow_old_passwords="0";
public $acore_geoip_lookup="0";
diff --git a/src/acore-wp-plugin/src/boot.php b/src/acore-wp-plugin/src/boot.php
index a7b832cf3..244ae049b 100644
--- a/src/acore-wp-plugin/src/boot.php
+++ b/src/acore-wp-plugin/src/boot.php
@@ -9,6 +9,8 @@
require_once ACORE_PATH_PLG . 'src/Components/AdminPanel/AdminPanel.php';
require_once ACORE_PATH_PLG . 'src/Components/CharactersMenu/CharactersMenu.php';
+require_once ACORE_PATH_PLG . 'src/Components/CharactersMenu/CharacterDumpWriter.php';
+require_once ACORE_PATH_PLG . 'src/Components/CharactersMenu/CharacterDumpApi.php';
require_once ACORE_PATH_PLG . 'src/Components/UnstuckMenu/UnstuckMenu.php';
require_once ACORE_PATH_PLG . 'src/Components/MailReturnMenu/MailReturnMenu.php';
require_once ACORE_PATH_PLG . 'src/Components/ResurrectionScrollMenu/ResurrectionScrollMenu.php';
From 7d2c4373727a06dc83bd89d711e56d33d9127bd2 Mon Sep 17 00:00:00 2001
From: FlyingArowana <16946913+TheSCREWEDSoftware@users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:06:21 +0100
Subject: [PATCH 17/18] Format the single nad all pdump file names
---
.../CharactersMenu/CharacterDumpApi.php | 98 +++++++++++++++++
.../CharactersMenu/CharactersView.php | 103 +++++++++++++++---
2 files changed, 183 insertions(+), 18 deletions(-)
diff --git a/src/acore-wp-plugin/src/Components/CharactersMenu/CharacterDumpApi.php b/src/acore-wp-plugin/src/Components/CharactersMenu/CharacterDumpApi.php
index 41610f375..a88a2642f 100644
--- a/src/acore-wp-plugin/src/Components/CharactersMenu/CharacterDumpApi.php
+++ b/src/acore-wp-plugin/src/Components/CharactersMenu/CharacterDumpApi.php
@@ -11,6 +11,11 @@
'callback' => __NAMESPACE__ . '\handlePdump',
'permission_callback' => '__return_true',
]);
+ register_rest_route(ACORE_SLUG . '/v1', 'pdump/all', [
+ 'methods' => 'POST',
+ 'callback' => __NAMESPACE__ . '\handlePdumpAll',
+ 'permission_callback' => '__return_true',
+ ]);
});
function handlePdump(\WP_REST_Request $request): void
@@ -87,3 +92,96 @@ function handlePdump(\WP_REST_Request $request): void
echo $dump;
exit;
}
+
+function handlePdumpAll(\WP_REST_Request $request): void
+{
+ $accId = ACoreServices::I()->getAcoreAccountId();
+
+ if (Opts::I()->acore_pdump_enabled != '1') {
+ wp_send_json_error(['message' => 'PDUMP export is not enabled on this server.'], 403);
+ exit;
+ }
+
+ if (!$accId) {
+ wp_send_json_error(['message' => 'Forbidden.'], 403);
+ exit;
+ }
+
+ $body = $request->get_json_params();
+ $characters = $body['characters'] ?? [];
+ if (empty($characters) || !is_array($characters)) {
+ wp_send_json_error(['message' => 'No characters provided.'], 400);
+ exit;
+ }
+
+ $conn = ACoreServices::I()->getCharacterEm()->getConnection();
+ $now = date('d_m_Y_H_i_s');
+
+ $phpWarnings = [];
+ set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline) use (&$phpWarnings): bool {
+ $phpWarnings[] = "[{$errno}] {$errstr} in {$errfile}:{$errline}";
+ return true;
+ });
+
+ ob_start();
+
+ $files = [];
+ foreach ($characters as $char) {
+ $guid = isset($char['guid']) ? (int) $char['guid'] : 0;
+ if ($guid < 1) continue;
+
+ $row = $conn->executeQuery(
+ "SELECT `name` FROM `characters`
+ WHERE `guid` = ? AND `account` = ? AND `deleteDate` IS NULL LIMIT 1",
+ [$guid, $accId]
+ )->fetchAssociative();
+
+ if (!$row) continue;
+
+ $writer = new CharacterDumpWriter($conn);
+ $dump = $writer->getDump($guid);
+ if ($dump === null) continue;
+
+ $order = preg_replace('/[^0-9]/', '', (string)($char['order'] ?? '0'));
+ $level = preg_replace('/[^0-9]/', '', (string)($char['level'] ?? '0'));
+ $race = preg_replace('/[^a-zA-Z]/', '', (string)($char['race'] ?? 'Unknown'));
+ $class = preg_replace('/[^a-zA-Z]/', '', (string)($char['class'] ?? 'Unknown'));
+
+ $files["{$order}_{$level}_{$race}_{$class}_{$now}.dump"] = $dump;
+ }
+
+ ob_end_clean();
+ restore_error_handler();
+
+ if (!empty($phpWarnings)) {
+ wp_send_json_error([
+ 'message' => 'An internal error occurred while generating the dump.',
+ 'detail' => implode("\n", $phpWarnings),
+ ], 500);
+ exit;
+ }
+
+ if (empty($files)) {
+ wp_send_json_error(['message' => 'No valid characters could be exported.'], 400);
+ exit;
+ }
+
+ $tmp = tempnam(sys_get_temp_dir(), 'pdump_');
+ $zip = new \ZipArchive();
+ $zip->open($tmp, \ZipArchive::OVERWRITE);
+ foreach ($files as $filename => $content) {
+ $zip->addFromString($filename, $content);
+ }
+ $zip->close();
+
+ if (ob_get_level()) ob_end_clean();
+
+ header('Content-Type: application/zip');
+ header('Content-Disposition: attachment; filename="dump_all_' . $now . '.zip"');
+ header('Content-Length: ' . filesize($tmp));
+ header('Cache-Control: no-cache, no-store, must-revalidate');
+ header('Pragma: no-cache');
+ readfile($tmp);
+ unlink($tmp);
+ exit;
+}
diff --git a/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersView.php b/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersView.php
index af82559a6..bdfad6a3c 100644
--- a/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersView.php
+++ b/src/acore-wp-plugin/src/Components/CharactersMenu/CharactersView.php
@@ -122,7 +122,13 @@ public function getHomeRender($characters, $mutetime = 0, $accBanRow = null, $se
-
+
@@ -343,12 +349,28 @@ public function getHomeRender($characters, $mutetime = 0, $accBanRow = null, $se
var acorePdumpRevisionUrl = = json_encode($serverRevisionUrl ?: '') ?>;
var acorePdumpBugReportUrl = = json_encode($bugReportUrl ?: '') ?>;
var acorePdumpRestBase = = json_encode(get_rest_url(null, ACORE_SLUG . '/v1/pdump/')) ?>;
+ var acorePdumpAllUrl = = json_encode(get_rest_url(null, ACORE_SLUG . '/v1/pdump/all')) ?>;
var acorePdumpNonce = = json_encode(wp_create_nonce('wp_rest')) ?>;
var acorePdumpModal = document.getElementById('acore-pdump-modal');
var acorePdumpBody = document.getElementById('acore-pdump-modal-body');
var acorePdumpConfirm = document.getElementById('acore-pdump-confirm');
var acorePdumpOnConfirm = null;
+ function acorePdumpFilename(order, level, race, cls) {
+ var now = new Date();
+ var pad = function(n) { return String(n).padStart(2, '0'); };
+ return order + '_' + level + '_' + race + '_' + cls + '_'
+ + pad(now.getDate()) + '_' + pad(now.getMonth() + 1) + '_' + now.getFullYear() + '_'
+ + pad(now.getHours()) + '_' + pad(now.getMinutes()) + '_' + pad(now.getSeconds()) + '.dump';
+ }
+
+ function acorePdumpZipFilename() {
+ var now = new Date();
+ var pad = function(n) { return String(n).padStart(2, '0'); };
+ return 'dump_all_' + pad(now.getDate()) + '_' + pad(now.getMonth() + 1) + '_' + now.getFullYear() + '_'
+ + pad(now.getHours()) + '_' + pad(now.getMinutes()) + '_' + pad(now.getSeconds()) + '.zip';
+ }
+
function acorePdumpRevisionLink() {
if (!acorePdumpRevision) return '(unknown revision)';
if (acorePdumpRevisionUrl) {
@@ -402,11 +424,7 @@ function acoreClosePdumpModal() {
acorePdumpOnConfirm = null;
}
- /**
- * Fetch the dump for one character GUID and trigger a browser download.
- * Uses fetch() + Blob so errors are surfaced without a page navigation.
- */
- function acoreDownloadDump(guid, charName) {
+ function acoreDownloadDump(guid, order, level, race, cls) {
fetch(acorePdumpRestBase + guid, {
headers: { 'X-WP-Nonce': acorePdumpNonce }
}).then(function(resp) {
@@ -422,7 +440,45 @@ function acoreDownloadDump(guid, charName) {
}
return resp.blob();
}).then(function(blob) {
- var filename = charName.toUpperCase() + '_' + new Date().toISOString().slice(0,10) + '.dump';
+ var filename = acorePdumpFilename(order, level, race, cls);
+ var url = URL.createObjectURL(blob);
+ var link = document.createElement('a');
+ link.href = url;
+ link.download = filename;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ setTimeout(function() { URL.revokeObjectURL(url); }, 10000);
+ }).catch(function(err) {
+ acoreShowPdumpError(
+ err && err.message ? err.message : String(err),
+ err && err.detail ? err.detail : null
+ );
+ });
+ }
+
+ function acoreDownloadAll(chars) {
+ fetch(acorePdumpAllUrl, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-WP-Nonce': acorePdumpNonce
+ },
+ body: JSON.stringify({ characters: chars })
+ }).then(function(resp) {
+ if (!resp.ok) {
+ return resp.json().then(function(body) {
+ var data = (body && body.data) ? body.data : body;
+ var msg = (data && data.message) ? data.message : 'Export failed (HTTP ' + resp.status + ').';
+ var detail = (data && data.detail) ? data.detail : null;
+ var err = new Error(msg);
+ err.detail = detail;
+ throw err;
+ });
+ }
+ return resp.blob();
+ }).then(function(blob) {
+ var filename = acorePdumpZipFilename();
var url = URL.createObjectURL(blob);
var link = document.createElement('a');
link.href = url;
@@ -449,8 +505,12 @@ function acoreDownloadDump(guid, charName) {
});
$(document).on('click', '.acore-export-btn', function() {
- var guid = $(this).data('char-guid');
- var name = $(this).data('char-name');
+ var guid = $(this).data('char-guid');
+ var name = $(this).data('char-name');
+ var order = $(this).data('char-order');
+ var level = $(this).data('char-level');
+ var race = $(this).data('char-race');
+ var cls = $(this).data('char-class');
var upper = name.toUpperCase();
var msg = 'You\'re about to PDUMP a.k.a make a copy of your character
' + upper + ' that can be used in AzerothCore. '
+ 'This will
NOT COPY any custom contents, for example modules like Transmog or Custom Items like Physical Costumes. '
@@ -463,28 +523,35 @@ function acoreDownloadDump(guid, charName) {
+ '
EQUIPMENT SET NAMES •
CUSTOM CHAT CHANNELS • '
+ '
CHARACTER & ACCOUNT IDs (replaced with random values).';
acoreShowPdumpModal(msg, function() {
- acoreDownloadDump(guid, name);
+ acoreDownloadDump(guid, order, level, race, cls);
});
});
$(document).on('click', '.acore-export-all-btn', function() {
var chars = [];
$('.acore-export-btn').each(function() {
- chars.push({ guid: $(this).data('char-guid'), name: $(this).data('char-name') });
+ chars.push({
+ guid: $(this).data('char-guid'),
+ name: $(this).data('char-name'),
+ order: $(this).data('char-order'),
+ level: $(this).data('char-level'),
+ race: $(this).data('char-race'),
+ 'class': $(this).data('char-class')
+ });
});
var nameList = chars.map(function(c) { return '
' + c.name.toUpperCase() + ''; }).join(', ');
var msg = 'You\'re about to PDUMP a.k.a make a copy of
ALL your characters: ' + nameList + '. '
+ 'This will
NOT COPY any custom contents, for example modules like Transmog or Custom Items like Physical Costumes. '
+ '
This Character Dump was extracted from this version:
' + acorePdumpRevisionLink()
+ '
The following information will be anonymised or omitted from the dump: '
- + 'character name, position, hearthstone location, gold, timestamps, online status, '
- + 'achievement dates, mail contents and sender, item creator/gifter, custom chat channels, '
- + 'and all character & account IDs (replaced with random values).';
+ + '
CHARACTER NAME •
POSITION •
HEARTHSTONE LOCATION • '
+ + '
GOLD •
TIMESTAMPS •
ONLINE STATUS • '
+ + '
ACHIEVEMENT DATES •
MAIL CONTENTS & SENDER • '
+ + '
ITEM CREATOR/GIFTER •
AURA CASTER • '
+ + '
EQUIPMENT SET NAMES •
CUSTOM CHAT CHANNELS • '
+ + '
CHARACTER & ACCOUNT IDs (replaced with random values).';
acoreShowPdumpModal(msg, function() {
- // Download sequentially with a small delay so the browser doesn't block multiple downloads
- chars.forEach(function(c, i) {
- setTimeout(function() { acoreDownloadDump(c.guid, c.name); }, i * 800);
- });
+ acoreDownloadAll(chars);
});
});
});
From f581aba14886a788c56974289ef20761b4b77348 Mon Sep 17 00:00:00 2001
From: FlyingArowana <16946913+TheSCREWEDSoftware@users.noreply.github.com>
Date: Thu, 25 Jun 2026 01:19:40 +0100
Subject: [PATCH 18/18] Toggle On and off showing the ban / mute info and
choose what to appear.
Forgot to add the toggle setting in https://github.com/azerothcore/acore-cms/pull/205
---
.../src/Components/AdminPanel/Pages/Tools.php | 36 +++++++++++++-
.../CharactersMenu/CharactersController.php | 11 ++++-
.../CharactersMenu/CharactersMenu.php | 48 +++++++++++--------
.../CharactersMenu/CharactersView.php | 12 ++---
src/acore-wp-plugin/src/Hooks/User/User.php | 30 ++++++++----
src/acore-wp-plugin/src/Manager/Opts.php | 4 ++
6 files changed, 105 insertions(+), 36 deletions(-)
diff --git a/src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php b/src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php
index 10824e8b3..c1ca7b2a0 100644
--- a/src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php
+++ b/src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php
@@ -189,7 +189,33 @@ class="= Opts::I()->acore_security_logging != '1' ? 'acore-days-inactive-disab
- |
+ |
+
+
+
+
+
+
+
+ |
+
+
+ |
|