Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace ACore\Components\MailReturnMenu;

use ACore\Components\MailReturnMenu\MailReturnController;

add_action('rest_api_init', function () {
// Get sent unread mails for a character
register_rest_route(ACORE_SLUG . '/v1', 'mail-return/list/(?P<charGuid>\d+)', array(
'methods' => 'GET',
'callback' => function ($request) {
try {
$charGuid = $request->get_param('charGuid');
$mails = MailReturnController::getSentUnreadMails($charGuid);
return new \WP_REST_Response($mails, 200);

} catch (\InvalidArgumentException $e) {
return new \WP_Error('invalid_character', $e->getMessage(), array('status' => 400));

} catch (\Exception $e) {
return new \WP_Error('server_error', 'An unexpected error occurred', array('status' => 500));
}
},
'permission_callback' => function () {
return is_user_logged_in();
},
));

// Return a mail
register_rest_route(ACORE_SLUG . '/v1', 'mail-return', array(
'methods' => 'POST',
'callback' => function ($request) {
try {
$charGuid = $request->get_param('charGuid');
$mailId = $request->get_param('mailId');
$message = MailReturnController::returnMail($charGuid, $mailId);
return new \WP_REST_Response(['message' => $message], 200);

} catch (\InvalidArgumentException $e) {
return new \WP_Error('invalid_request', $e->getMessage(), array('status' => 400));

} catch (\Exception $e) {
return new \WP_Error('server_error', 'An unexpected error occurred', array('status' => 500));
}
},
'permission_callback' => function () {
return is_user_logged_in();
},
));
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
<?php

namespace ACore\Components\MailReturnMenu;

use ACore\Manager\ACoreServices;
use ACore\Manager\Opts;
use ACore\Components\MailReturnMenu\MailReturnView;
use InvalidArgumentException;

class MailReturnController
{
private $view;

public function __construct()
{
$this->view = new MailReturnView($this);
}

public function renderCharacters()
{
echo $this->view->getMailReturnRender(self::getCharactersByAcId());
}

private static function validateAccountId()
{
$accId = ACoreServices::I()->getAcoreAccountId();

if (!isset($accId) || $accId === null || $accId === '' || trim($accId) === '' || !is_numeric($accId)) {
throw new InvalidArgumentException("Invalid user account ID provided.");
}

return intval($accId);
}

private static function validateCharacterOwnership($conn, $charGuid, $accId)
{
$stmt = $conn->prepare(
"SELECT `guid`, `name` FROM `characters`
WHERE `guid` = ? AND `account` = ? AND `deleteDate` IS NULL"
);
$stmt->bindValue(1, $charGuid);
$stmt->bindValue(2, $accId);
$result = $stmt->executeQuery();
$character = $result->fetchAssociative();

if (!$character) {
throw new InvalidArgumentException("Character not found");
}

return $character;
}

public static function getSentUnreadMails($charGuid)
{
if (!is_numeric($charGuid)) {
throw new InvalidArgumentException("Invalid parameters");
}
$charGuid = intval($charGuid);

$accId = self::validateAccountId();
$conn = ACoreServices::I()->getCharacterEm()->getConnection();

self::validateCharacterOwnership($conn, $charGuid, $accId);

// Get mails sent by this character that are unread by the receiver
// messageType = 0 means player mail
$query = "SELECT m.`id`, m.`subject`, m.`has_items`, m.`money`,
m.`expire_time`, m.`deliver_time`,
rc.`name` AS receiver_name, rc.`race` AS receiver_race,
rc.`class` AS receiver_class, rc.`gender` AS receiver_gender,
rc.`level` AS receiver_level
FROM `mail` m
JOIN `characters` rc ON m.`receiver` = rc.`guid`
WHERE m.`sender` = ?
AND m.`messageType` = 0
AND (m.`checked` & 1) = 0
ORDER BY m.`deliver_time` DESC";

$stmt = $conn->prepare($query);
$stmt->bindValue(1, $charGuid);
$result = $stmt->executeQuery();
$mails = $result->fetchAllAssociative();

// Fetch items for mails that have items
$mailIds = [];
foreach ($mails as $mail) {
if ($mail['has_items'] == 1) {
$mailIds[] = $mail['id'];
}
}

$itemsByMail = [];
if (!empty($mailIds)) {
$placeholders = implode(',', array_fill(0, count($mailIds), '?'));
$worldDb = Opts::I()->acore_db_world_name;
$itemQuery = "SELECT mi.`mail_id`, ii.`itemEntry`, ii.`count`,
it.`name` AS item_name
FROM `mail_items` mi
JOIN `item_instance` ii ON mi.`item_guid` = ii.`guid`
JOIN `$worldDb`.`item_template` it ON ii.`itemEntry` = it.`entry`
WHERE mi.`mail_id` IN ($placeholders)";
$stmt = $conn->prepare($itemQuery);
$i = 1;
foreach ($mailIds as $id) {
$stmt->bindValue($i++, $id);
}
$itemResult = $stmt->executeQuery();
foreach ($itemResult->fetchAllAssociative() as $item) {
$itemsByMail[$item['mail_id']][] = $item;
}
}

// Attach items to their mails
foreach ($mails as &$mail) {
$mail['items'] = $itemsByMail[$mail['id']] ?? [];
}

return $mails;
}

public static function returnMail($charGuid, $mailId)
{
$accId = self::validateAccountId();
$conn = ACoreServices::I()->getCharacterEm()->getConnection();

// Validate charGuid and mailId are integers to prevent injection
if (!is_numeric($charGuid) || !is_numeric($mailId)) {
throw new InvalidArgumentException("Invalid parameters");
}
$charGuid = intval($charGuid);
$mailId = intval($mailId);

// Verify the sender character belongs to the current user
$sender = self::validateCharacterOwnership($conn, $charGuid, $accId);

// Verify the mail was sent by this character and is still unread
$mailQuery = "SELECT m.`id`, m.`receiver`
FROM `mail` m
WHERE m.`id` = ? AND m.`sender` = ? AND m.`messageType` = 0 AND (m.`checked` & 1) = 0";
$stmt = $conn->prepare($mailQuery);
$stmt->bindValue(1, $mailId);
$stmt->bindValue(2, $charGuid);
$mailResult = $stmt->executeQuery();
$mail = $mailResult->fetchAssociative();

if (!$mail) {
throw new InvalidArgumentException("Mail not found or already read");
}

// Verify the receiver character actually exists and has this mail
$receiverQuery = "SELECT `guid`, `name` FROM `characters` WHERE `guid` = ? AND `deleteDate` IS NULL";
$stmt = $conn->prepare($receiverQuery);
$stmt->bindValue(1, $mail['receiver']);
$receiverResult = $stmt->executeQuery();
$receiver = $receiverResult->fetchAssociative();

if (!$receiver) {
throw new InvalidArgumentException("Receiver character not found");
}

// Use the receiver name from the database, not from user input
$receiverName = $receiver['name'];

// Execute SOAP command to return the mail
$soap = ACoreServices::I()->getGameMailSoap();
$result = $soap->executeCommand(".mail return $receiverName $mailId");

return "Mail #$mailId returned successfully";
}

public static function getCharactersByAcId()
{
$accId = self::validateAccountId();

$query = "SELECT
c.`guid`, c.`name`, c.`order`, c.`race`, c.`class`, c.`level`, c.`gender`
FROM `characters` c
WHERE c.`deleteDate` IS NULL
AND c.`account` = ?
ORDER BY COALESCE(c.`order`, c.`guid`)
";
$conn = ACoreServices::I()->getCharacterEm()->getConnection();
$stmt = $conn->prepare($query);
$stmt->bindValue(1, $accId);
$result = $stmt->executeQuery();
return $result->fetchAllAssociative();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace ACore\Components\MailReturnMenu;

use ACore\Components\MailReturnMenu\MailReturnController;

require_once 'MailReturnApi.php';

add_action('init', __NAMESPACE__ . '\\mail_return_menu_init');

class MailReturnMenu
{
private static $instance = null;

/**
* Singleton
* @return MailReturnMenu
*/
public static function I()
{
if (!self::$instance) {
self::$instance = new self();
}

return self::$instance;
}

function acore_mail_return_menu()
{
add_submenu_page('profile.php', 'Mail Return', 'Mail Return', 'read', ACORE_SLUG . '-mail-return-menu', array($this, 'acore_mail_return_menu_page'));
}

function acore_mail_return_menu_page()
{
$controller = new MailReturnController();
$controller->renderCharacters();
}
}

function mail_return_menu_init()
{
$mailReturnMenu = MailReturnMenu::I();

add_action('admin_menu', array($mailReturnMenu, 'acore_mail_return_menu'));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace ACore\Components\MailReturnMenu;


class MailReturnView
{
private $controller;

public function __construct($controller)
{
$this->controller = $controller;
}

public function getMailReturnRender($chars)
{
ob_start();

wp_enqueue_style('bootstrap-css', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', array(), '5.1.3');
wp_enqueue_style('acore-css', ACORE_URL_PLG . 'web/assets/css/main.css', array(), '0.1');
wp_enqueue_script('bootstrap-js', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js', array(), '5.1.3');
wp_enqueue_script('jquery');
wp_enqueue_script('acore-mail-return-js', ACORE_URL_PLG . 'web/assets/mail-return/mail-return.js', array('jquery'), null, true);

?>

<div class="wrap">
<div class="col-sm-6">
<div class="card">
<div class="card-body">
<h3>Mail Return</h3>
<p>You can return sent mails that have not yet been read by the recipient in this page. Select the character, the sent mail and hit return.</p>
<hr>

<label for="mail-return-char-select"><strong>Select Character:</strong></label>
<select id="mail-return-char-select" class="form-select mb-3">
<option value="">-- Select a character --</option>
<?php foreach ($chars as $char) { ?>
<option value="<?= intval($char["guid"]) ?>" data-name="<?= esc_attr($char["name"]) ?>">
<?= esc_html($char["name"]) ?> (Level <?= intval($char["level"]) ?>)
</option>
<?php } ?>
</select>

<div id="mail-return-loading" style="display:none;" class="text-center my-3">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>

<div id="mail-return-list" style="display:none;">
<h5>Unread Sent Mails</h5>
<ul id="mail-return-items" class="list-unstyled"></ul>
<p id="mail-return-empty" style="display:none;" class="text-muted">No unread sent mails found for this character.</p>
</div>
</div>
</div>
</div>
</div>

<script>
var mailReturnData = {
nonce: "<?php echo esc_js(wp_create_nonce('wp_rest')); ?>",
mailsUrl: "<?php echo esc_url(get_rest_url(null, ACORE_SLUG . '/v1/mail-return/list')); ?>",
returnUrl: "<?php echo esc_url(get_rest_url(null, ACORE_SLUG . '/v1/mail-return')); ?>",
assetsUrl: "<?php echo esc_url(ACORE_URL_PLG . 'web/assets/'); ?>"
};
</script>
<script>var whTooltips = {colorLinks: true, iconizeLinks: true, renameLinks: true};</script>
<script src="https://wow.zamimg.com/js/tooltips.js"></script>
<?php
return ob_get_clean();
}
}
1 change: 1 addition & 0 deletions src/acore-wp-plugin/src/boot.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
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/UnstuckMenu/UnstuckMenu.php';
require_once ACORE_PATH_PLG . 'src/Components/MailReturnMenu/MailReturnMenu.php';
require_once ACORE_PATH_PLG . 'src/Components/ResurrectionScrollMenu/ResurrectionScrollMenu.php';
require_once ACORE_PATH_PLG . 'src/Components/ServerInfo/ServerInfo.php';
require_once ACORE_PATH_PLG . 'src/Components/Tools/ToolsInfo.php';
Expand Down
Loading
Loading