import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend";
import { ApiResult, apiResultFromError, apiResultFromValue } from "./Result";
-export async function postObject(endpoint: string): Promise<ApiResult<[]>> {
+type Payload = Blob | FormData | Record<string, unknown>;
+
+export async function postObject(endpoint: string, payload?: Payload): Promise<ApiResult<[]>> {
try {
- await prepareRequest(endpoint).post().fetchAsJson();
+ await prepareRequest(endpoint).post(payload).fetchAsJson();
} catch (e) {
return apiResultFromError(e);
}
--- /dev/null
+import { confirmationFactory } from "WoltLabSuite/Core/Component/Confirmation";
+
+export enum ConfirmationType {
+ None = "None",
+ SoftDelete = "SoftDelete",
+ SoftDeleteWithReason = "SoftDeleteWithReason",
+ Restore = "Restore",
+ Delete = "Delete",
+ Custom = "Custom",
+}
+
+type ResultConfirmationWithReason = {
+ result: boolean;
+ reason?: string;
+};
+
+export async function handleConfirmation(
+ objectName: string,
+ confirmationType: ConfirmationType,
+ customMessage: string = "",
+): Promise<ResultConfirmationWithReason> {
+ if (confirmationType == ConfirmationType.SoftDelete) {
+ return await confirmationFactory().softDelete(objectName);
+ }
+
+ if (confirmationType == ConfirmationType.SoftDeleteWithReason) {
+ return await confirmationFactory().softDelete(objectName, true);
+ }
+
+ if (confirmationType == ConfirmationType.Restore) {
+ return {
+ result: await confirmationFactory().restore(objectName ? objectName : undefined),
+ };
+ }
+
+ if (confirmationType == ConfirmationType.Delete) {
+ return {
+ result: await confirmationFactory().delete(objectName ? objectName : undefined),
+ };
+ }
+
+ if (confirmationType == ConfirmationType.Custom) {
+ return {
+ result: await confirmationFactory().custom(customMessage).withoutMessage(),
+ };
+ }
+
+ return {
+ result: true,
+ };
+}
--- /dev/null
+import { deleteObject } from "WoltLabSuite/Core/Api/DeleteObject";
+import { postObject } from "WoltLabSuite/Core/Api/PostObject";
+import { show as showNotification } from "WoltLabSuite/Core/Ui/Notification";
+import { ConfirmationType, handleConfirmation } from "./Confirmation";
+
+async function handleRpcAction(
+ row: HTMLTableRowElement,
+ objectName: string,
+ endpoint: string,
+ confirmationType: ConfirmationType,
+ customConfirmationMessage: string = "",
+): Promise<void> {
+ const confirmationResult = await handleConfirmation(objectName, confirmationType, customConfirmationMessage);
+ if (!confirmationResult.result) {
+ return;
+ }
+
+ if (confirmationType == ConfirmationType.Delete) {
+ const result = await deleteObject(endpoint);
+ if (!result.ok) {
+ return;
+ }
+ } else {
+ const result = await postObject(
+ endpoint,
+ confirmationResult.reason ? { reason: confirmationResult.reason } : undefined,
+ );
+ if (!result.ok) {
+ return;
+ }
+ }
+
+ if (confirmationType == ConfirmationType.Delete) {
+ row.remove();
+ } else {
+ row.dispatchEvent(
+ new CustomEvent("refresh", {
+ bubbles: true,
+ }),
+ );
+
+ // TODO: This shows a generic success message and should be replaced with a more specific message.
+ showNotification();
+ }
+}
+
+export function setup(table: HTMLTableElement): void {
+ table.addEventListener("action", (event: CustomEvent) => {
+ if (event.detail.action === "rpc") {
+ void handleRpcAction(
+ event.target as HTMLTableRowElement,
+ event.detail.objectName,
+ event.detail.endpoint,
+ event.detail.confirmationType,
+ event.detail.confirmationMessage,
+ );
+ }
+ });
+}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.postObject = postObject;
- async function postObject(endpoint) {
+ async function postObject(endpoint, payload) {
try {
- await (0, Backend_1.prepareRequest)(endpoint).post().fetchAsJson();
+ await (0, Backend_1.prepareRequest)(endpoint).post(payload).fetchAsJson();
}
catch (e) {
return (0, Result_1.apiResultFromError)(e);
--- /dev/null
+define(["require", "exports", "WoltLabSuite/Core/Component/Confirmation"], function (require, exports, Confirmation_1) {
+ "use strict";
+ Object.defineProperty(exports, "__esModule", { value: true });
+ exports.ConfirmationType = void 0;
+ exports.handleConfirmation = handleConfirmation;
+ var ConfirmationType;
+ (function (ConfirmationType) {
+ ConfirmationType["None"] = "None";
+ ConfirmationType["SoftDelete"] = "SoftDelete";
+ ConfirmationType["SoftDeleteWithReason"] = "SoftDeleteWithReason";
+ ConfirmationType["Restore"] = "Restore";
+ ConfirmationType["Delete"] = "Delete";
+ ConfirmationType["Custom"] = "Custom";
+ })(ConfirmationType || (exports.ConfirmationType = ConfirmationType = {}));
+ async function handleConfirmation(objectName, confirmationType, customMessage = "") {
+ if (confirmationType == ConfirmationType.SoftDelete) {
+ return await (0, Confirmation_1.confirmationFactory)().softDelete(objectName);
+ }
+ if (confirmationType == ConfirmationType.SoftDeleteWithReason) {
+ return await (0, Confirmation_1.confirmationFactory)().softDelete(objectName, true);
+ }
+ if (confirmationType == ConfirmationType.Restore) {
+ return {
+ result: await (0, Confirmation_1.confirmationFactory)().restore(objectName ? objectName : undefined),
+ };
+ }
+ if (confirmationType == ConfirmationType.Delete) {
+ return {
+ result: await (0, Confirmation_1.confirmationFactory)().delete(objectName ? objectName : undefined),
+ };
+ }
+ if (confirmationType == ConfirmationType.Custom) {
+ return {
+ result: await (0, Confirmation_1.confirmationFactory)().custom(customMessage).withoutMessage(),
+ };
+ }
+ return {
+ result: true,
+ };
+ }
+});
--- /dev/null
+define(["require", "exports", "WoltLabSuite/Core/Api/DeleteObject", "WoltLabSuite/Core/Api/PostObject", "WoltLabSuite/Core/Ui/Notification", "./Confirmation"], function (require, exports, DeleteObject_1, PostObject_1, Notification_1, Confirmation_1) {
+ "use strict";
+ Object.defineProperty(exports, "__esModule", { value: true });
+ exports.setup = setup;
+ async function handleRpcAction(row, objectName, endpoint, confirmationType, customConfirmationMessage = "") {
+ const confirmationResult = await (0, Confirmation_1.handleConfirmation)(objectName, confirmationType, customConfirmationMessage);
+ if (!confirmationResult.result) {
+ return;
+ }
+ if (confirmationType == Confirmation_1.ConfirmationType.Delete) {
+ const result = await (0, DeleteObject_1.deleteObject)(endpoint);
+ if (!result.ok) {
+ return;
+ }
+ }
+ else {
+ const result = await (0, PostObject_1.postObject)(endpoint, confirmationResult.reason ? { reason: confirmationResult.reason } : undefined);
+ if (!result.ok) {
+ return;
+ }
+ }
+ if (confirmationType == Confirmation_1.ConfirmationType.Delete) {
+ row.remove();
+ }
+ else {
+ row.dispatchEvent(new CustomEvent("refresh", {
+ bubbles: true,
+ }));
+ // TODO: This shows a generic success message and should be replaced with a more specific message.
+ (0, Notification_1.show)();
+ }
+ }
+ function setup(table) {
+ table.addEventListener("action", (event) => {
+ if (event.detail.action === "rpc") {
+ void handleRpcAction(event.target, event.detail.objectName, event.detail.endpoint, event.detail.confirmationType, event.detail.confirmationMessage);
+ }
+ });
+ }
+});
--- /dev/null
+<?php
+
+namespace wcf\system\gridView\action;
+
+enum ActionConfirmationType
+{
+ case None;
+ case SoftDelete;
+ case SoftDeleteWithReason;
+ case Restore;
+ case Delete;
+ case Custom;
+
+ public function toString(): string
+ {
+ return match ($this) {
+ self::None => 'None',
+ self::SoftDelete => 'SoftDelete',
+ self::SoftDeleteWithReason => 'SoftDeleteWithReason',
+ self::Restore => 'Restore',
+ self::Delete => 'Delete',
+ self::Custom => 'Custom',
+ };
+ }
+}
--- /dev/null
+<?php
+
+namespace wcf\system\gridView\action;
+
+use Closure;
+use wcf\action\ApiAction;
+use wcf\data\DatabaseObject;
+use wcf\data\ITitledObject;
+use wcf\system\gridView\AbstractGridView;
+use wcf\system\request\LinkHandler;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Represents an action that call a rpc endpoint.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since 6.2
+ */
+class RpcAction extends AbstractAction
+{
+ public function __construct(
+ protected readonly string $endpoint,
+ protected readonly string|Closure $languageItem,
+ protected readonly ActionConfirmationType $confirmationType = ActionConfirmationType::None,
+ protected readonly string|Closure $confirmationMessage = '',
+ ?Closure $isAvailableCallback = null
+ ) {
+ parent::__construct($isAvailableCallback);
+ }
+
+ #[\Override]
+ public function render(mixed $row): string
+ {
+ \assert($row instanceof DatabaseObject);
+
+ if (\is_string($this->languageItem)) {
+ $label = WCF::getLanguage()->get($this->languageItem);
+ } else {
+ $label = ($this->languageItem)($row);
+ }
+
+ if (\is_string($this->confirmationMessage)) {
+ $confirmationMessage = WCF::getLanguage()->get($this->confirmationMessage);
+ } else {
+ $confirmationMessage = ($this->confirmationMessage)($row);
+ }
+
+ $endpoint = StringUtil::encodeHTML(
+ LinkHandler::getInstance()->getControllerLink(ApiAction::class, ['id' => 'rpc']) .
+ \sprintf($this->endpoint, $row->getObjectID())
+ );
+
+ if ($row instanceof ITitledObject) {
+ $objectName = StringUtil::encodeHTML($row->getTitle());
+ } else {
+ $objectName = '';
+ }
+
+ return <<<HTML
+ <button
+ type="button"
+ data-action="rpc"
+ data-object-name="{$objectName}"
+ data-endpoint="{$endpoint}"
+ data-confirmation-type="{$this->confirmationType->toString()}"
+ data-confirmation-message="{$confirmationMessage}"
+ >
+ {$label}
+ </button>
+ HTML;
+ }
+
+ #[\Override]
+ public function renderInitialization(AbstractGridView $gridView): ?string
+ {
+ $id = StringUtil::encodeJS($gridView->getID());
+
+ return <<<HTML
+ <script data-relocate="true">
+ require(['WoltLabSuite/Core/Component/GridView/Action/Rpc'], ({ setup }) => {
+ setup(document.getElementById('{$id}_table'));
+ });
+ </script>
+ HTML;
+ }
+}