mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 09:42:02 +08:00
FEATURE: API to customize server side composer errors handling in the client side (#19107)
This will be used by plugins to handle the client side of their custom post validations without having to overwrite the whole composer save action as it was done in other plugins. Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
This commit is contained in:
parent
269f65f14c
commit
e901403621
|
@ -67,6 +67,7 @@ async function loadDraft(store, opts = {}) {
|
|||
}
|
||||
|
||||
const _popupMenuOptionsCallbacks = [];
|
||||
const _composerSaveErrorCallbacks = [];
|
||||
|
||||
let _checkDraftPopup = !isTesting();
|
||||
|
||||
|
@ -82,6 +83,14 @@ export function addPopupMenuOptionsCallback(callback) {
|
|||
_popupMenuOptionsCallbacks.push(callback);
|
||||
}
|
||||
|
||||
export function clearComposerSaveErrorCallback() {
|
||||
_composerSaveErrorCallbacks.length = 0;
|
||||
}
|
||||
|
||||
export function addComposerSaveErrorCallback(callback) {
|
||||
_composerSaveErrorCallbacks.push(callback);
|
||||
}
|
||||
|
||||
export default Controller.extend({
|
||||
topicController: controller("topic"),
|
||||
router: service(),
|
||||
|
@ -1039,9 +1048,20 @@ export default Controller.extend({
|
|||
.catch((error) => {
|
||||
composer.set("disableDrafts", false);
|
||||
if (error) {
|
||||
this.appEvents.one("composer:will-open", () =>
|
||||
this.dialog.alert(error)
|
||||
);
|
||||
this.appEvents.one("composer:will-open", () => {
|
||||
if (
|
||||
_composerSaveErrorCallbacks.length === 0 ||
|
||||
!_composerSaveErrorCallbacks
|
||||
.map((c) => {
|
||||
return c.call(this, error);
|
||||
})
|
||||
.some((i) => {
|
||||
return i;
|
||||
})
|
||||
) {
|
||||
this.dialog.alert(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -55,7 +55,10 @@ import { addNavItem } from "discourse/models/nav-item";
|
|||
import { addPluginDocumentTitleCounter } from "discourse/components/d-document";
|
||||
import { addPluginOutletDecorator } from "discourse/components/plugin-connector";
|
||||
import { addPluginReviewableParam } from "discourse/components/reviewable-item";
|
||||
import { addPopupMenuOptionsCallback } from "discourse/controllers/composer";
|
||||
import {
|
||||
addComposerSaveErrorCallback,
|
||||
addPopupMenuOptionsCallback,
|
||||
} from "discourse/controllers/composer";
|
||||
import { addPostClassesCallback } from "discourse/widgets/post";
|
||||
import {
|
||||
addGroupPostSmallActionCode,
|
||||
|
@ -109,7 +112,7 @@ import { registerModelTransformer } from "discourse/lib/model-transformers";
|
|||
// based on Semantic Versioning 2.0.0. Please update the changelog at
|
||||
// docs/CHANGELOG-JAVASCRIPT-PLUGIN-API.md whenever you change the version
|
||||
// using the format described at https://keepachangelog.com/en/1.0.0/.
|
||||
const PLUGIN_API_VERSION = "1.4.0";
|
||||
const PLUGIN_API_VERSION = "1.5.0";
|
||||
|
||||
// This helper prevents us from applying the same `modifyClass` over and over in test mode.
|
||||
function canModify(klass, type, resolverName, changes) {
|
||||
|
@ -1246,6 +1249,27 @@ class PluginApi {
|
|||
Composer.reopen({ beforeSave: method });
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback function to handle the composer save errors.
|
||||
* This allows you to implement custom logic that will happen before
|
||||
* the raw error is presented to the user.
|
||||
* The passed function is expected to return true if the error was handled,
|
||||
* false otherwise.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* api.addComposerSaveErrorCallback((error) => {
|
||||
* if (error == "my_error") {
|
||||
* //handle error
|
||||
* return true;
|
||||
* }
|
||||
* return false;
|
||||
* })
|
||||
*/
|
||||
addComposerSaveErrorCallback(callback) {
|
||||
addComposerSaveErrorCallback(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a field to topic edit serializer
|
||||
*
|
||||
|
|
|
@ -1106,6 +1106,59 @@ acceptance("Composer - Customizations", function (needs) {
|
|||
});
|
||||
});
|
||||
|
||||
acceptance("Composer - Error Extensibility", function (needs) {
|
||||
needs.user();
|
||||
needs.settings({
|
||||
general_category_id: 1,
|
||||
default_composer_category: 1,
|
||||
});
|
||||
|
||||
needs.hooks.beforeEach(() => {
|
||||
withPluginApi("1.5.0", (api) => {
|
||||
api.addComposerSaveErrorCallback((error) => {
|
||||
if (error.match(/PLUGIN_XYZ ERROR/)) {
|
||||
// handle error
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test("Create a topic with server side errors handled by a plugin", async function (assert) {
|
||||
pretender.post("/posts", function () {
|
||||
return response(422, { errors: ["PLUGIN_XYZ ERROR"] });
|
||||
});
|
||||
|
||||
await visit("/");
|
||||
await click("#create-topic");
|
||||
await fillIn("#reply-title", "this title triggers an error");
|
||||
await fillIn(".d-editor-input", "this is the *content* of a post");
|
||||
await click("#reply-control button.create");
|
||||
assert.notOk(exists(".dialog-body"), "it does not pop up an error message");
|
||||
});
|
||||
|
||||
test("Create a topic with server side errors not handled by a plugin", async function (assert) {
|
||||
pretender.post("/posts", function () {
|
||||
return response(422, { errors: ["PLUGIN_ABC ERROR"] });
|
||||
});
|
||||
|
||||
await visit("/");
|
||||
await click("#create-topic");
|
||||
await fillIn("#reply-title", "this title triggers an error");
|
||||
await fillIn(".d-editor-input", "this is the *content* of a post");
|
||||
await click("#reply-control button.create");
|
||||
assert.ok(exists(".dialog-body"), "it pops up an error message");
|
||||
assert.ok(
|
||||
query(".dialog-body").innerText.match(/PLUGIN_ABC ERROR/),
|
||||
"it contains the server side error text"
|
||||
);
|
||||
await click(".dialog-footer .btn-primary");
|
||||
assert.ok(!exists(".dialog-body"), "it dismisses the error");
|
||||
assert.ok(exists(".d-editor-input"), "the composer input is visible");
|
||||
});
|
||||
});
|
||||
|
||||
acceptance("Composer - Focus Open and Closed", function (needs) {
|
||||
needs.user();
|
||||
needs.settings({ allow_uncategorized_topics: true });
|
||||
|
|
|
@ -7,6 +7,13 @@ in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.5.0] - 2022-11-21
|
||||
|
||||
### Added
|
||||
|
||||
- Adds `addComposerSaveErrorCallback`, which allows users to register custom error handling
|
||||
for server-side errors when submitting on the composer.
|
||||
|
||||
## [1.4.0] - 2022-09-27
|
||||
|
||||
### Added
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
class PostCreator
|
||||
include HasErrors
|
||||
|
||||
attr_reader :opts
|
||||
attr_reader :opts, :post
|
||||
|
||||
# Acceptable options:
|
||||
#
|
||||
|
@ -161,7 +161,7 @@ class PostCreator
|
|||
return false
|
||||
end
|
||||
|
||||
DiscourseEvent.trigger :before_create_post, @post
|
||||
DiscourseEvent.trigger :before_create_post, @post, @opts
|
||||
DiscourseEvent.trigger :validate_post, @post
|
||||
|
||||
post_validator = PostValidator.new(skip_topic: true)
|
||||
|
|
|
@ -621,7 +621,7 @@ class PostRevisor
|
|||
end
|
||||
|
||||
def plugin_callbacks
|
||||
DiscourseEvent.trigger(:before_edit_post, @post)
|
||||
DiscourseEvent.trigger(:before_edit_post, @post, @fields)
|
||||
DiscourseEvent.trigger(:validate_post, @post)
|
||||
end
|
||||
|
||||
|
|
|
@ -119,6 +119,15 @@ RSpec.describe PostCreator do
|
|||
)
|
||||
end
|
||||
|
||||
it "before_create_post event signature contains both post and opts" do
|
||||
events = DiscourseEvent.track_events { creator.create }
|
||||
|
||||
expect(events).to include(
|
||||
event_name: :before_create_post,
|
||||
params: [creator.post, creator.opts]
|
||||
)
|
||||
end
|
||||
|
||||
it "does not notify on system messages" do
|
||||
messages = MessageBus.track_publish do
|
||||
p = PostCreator.create(admin, basic_topic_params.merge(post_type: Post.types[:moderator_action]))
|
||||
|
|
|
@ -691,6 +691,17 @@ RSpec.describe PostRevisor do
|
|||
expect(post.revisions.size).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when editing the before_edit_post event signature' do
|
||||
it 'contains post and params' do
|
||||
params = { raw: 'body (edited)' }
|
||||
events = DiscourseEvent.track_events { subject.revise!(user, params) }
|
||||
expect(events).to include(
|
||||
event_name: :before_edit_post,
|
||||
params: [post, params]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "topic excerpt" do
|
||||
|
|
Loading…
Reference in New Issue
Block a user