forked from potsda.mn/mobilizon
Merge branch 'fixes' into 'master'
Fixes See merge request framasoft/mobilizon!580
This commit is contained in:
commit
ffa310a138
|
@ -2,7 +2,7 @@
|
||||||
<div>
|
<div>
|
||||||
<form
|
<form
|
||||||
class="new-comment"
|
class="new-comment"
|
||||||
v-if="currentActor.id && event.options.commentModeration !== CommentModeration.CLOSED"
|
v-if="isAbleToComment"
|
||||||
@submit.prevent="createCommentForEvent(newComment)"
|
@submit.prevent="createCommentForEvent(newComment)"
|
||||||
@keyup.ctrl.enter="createCommentForEvent(newComment)"
|
@keyup.ctrl.enter="createCommentForEvent(newComment)"
|
||||||
>
|
>
|
||||||
|
@ -22,11 +22,7 @@
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</form>
|
</form>
|
||||||
<b-notification
|
<b-notification v-else :closable="false">{{ $t("Comments have been closed.") }}</b-notification>
|
||||||
v-else-if="event.options.commentModeration === CommentModeration.CLOSED"
|
|
||||||
:closable="false"
|
|
||||||
>{{ $t("Comments have been closed.") }}</b-notification
|
|
||||||
>
|
|
||||||
<transition name="comment-empty-list" mode="out-in">
|
<transition name="comment-empty-list" mode="out-in">
|
||||||
<transition-group name="comment-list" v-if="comments.length" class="comment-list" tag="ul">
|
<transition-group name="comment-list" v-if="comments.length" class="comment-list" tag="ul">
|
||||||
<comment
|
<comment
|
||||||
|
@ -51,7 +47,6 @@
|
||||||
import { Prop, Vue, Component, Watch } from "vue-property-decorator";
|
import { Prop, Vue, Component, Watch } from "vue-property-decorator";
|
||||||
import Comment from "@/components/Comment/Comment.vue";
|
import Comment from "@/components/Comment/Comment.vue";
|
||||||
import IdentityPickerWrapper from "@/views/Account/IdentityPickerWrapper.vue";
|
import IdentityPickerWrapper from "@/views/Account/IdentityPickerWrapper.vue";
|
||||||
import { SnackbarProgrammatic as Snackbar } from "buefy";
|
|
||||||
import { CommentModel, IComment } from "../../types/comment.model";
|
import { CommentModel, IComment } from "../../types/comment.model";
|
||||||
import {
|
import {
|
||||||
CREATE_COMMENT_FROM_EVENT,
|
CREATE_COMMENT_FROM_EVENT,
|
||||||
|
@ -190,8 +185,11 @@ export default class CommentTree extends Vue {
|
||||||
|
|
||||||
// and reset the new comment field
|
// and reset the new comment field
|
||||||
this.newComment = new CommentModel();
|
this.newComment = new CommentModel();
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
Snackbar.open({ message: e.message, type: "is-danger", position: "is-bottom" });
|
console.error(error);
|
||||||
|
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
|
||||||
|
this.$notifier.error(error.graphQLErrors[0].message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,8 +258,11 @@ export default class CommentTree extends Vue {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// this.comments = this.comments.filter(commentItem => commentItem.id !== comment.id);
|
// this.comments = this.comments.filter(commentItem => commentItem.id !== comment.id);
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
Snackbar.open({ message: e.message, type: "is-danger", position: "is-bottom" });
|
console.error(error);
|
||||||
|
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
|
||||||
|
this.$notifier.error(error.graphQLErrors[0].message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,6 +280,18 @@ export default class CommentTree extends Vue {
|
||||||
get filteredOrderedComments(): IComment[] {
|
get filteredOrderedComments(): IComment[] {
|
||||||
return this.orderedComments.filter((comment) => !comment.deletedAt || comment.totalReplies > 0);
|
return this.orderedComments.filter((comment) => !comment.deletedAt || comment.totalReplies > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isAbleToComment(): boolean {
|
||||||
|
if (this.currentActor.id) {
|
||||||
|
if (
|
||||||
|
this.event.options.commentModeration !== CommentModeration.CLOSED ||
|
||||||
|
(this.event.organizerActor && this.currentActor.id === this.event.organizerActor.id)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
"Admin": "Admin",
|
"Admin": "Admin",
|
||||||
"Administration": "Administration",
|
"Administration": "Administration",
|
||||||
"All the places have already been taken": "All the places have been taken|One place is still available|{places} places are still available",
|
"All the places have already been taken": "All the places have been taken|One place is still available|{places} places are still available",
|
||||||
"Allow all comments": "Allow all comments",
|
|
||||||
"Allow registrations": "Allow registrations",
|
"Allow registrations": "Allow registrations",
|
||||||
"Anonymous participant": "Anonymous participant",
|
"Anonymous participant": "Anonymous participant",
|
||||||
"Anonymous participants will be asked to confirm their participation through e-mail.": "Anonymous participants will be asked to confirm their participation through e-mail.",
|
"Anonymous participants will be asked to confirm their participation through e-mail.": "Anonymous participants will be asked to confirm their participation through e-mail.",
|
||||||
|
@ -785,5 +784,6 @@
|
||||||
"Accessible only to members": "Accessible only to members",
|
"Accessible only to members": "Accessible only to members",
|
||||||
"Created by {name}": "Created by {name}",
|
"Created by {name}": "Created by {name}",
|
||||||
"View all posts": "View all posts",
|
"View all posts": "View all posts",
|
||||||
"Didn't receive the instructions?": "Didn't receive the instructions?"
|
"Didn't receive the instructions?": "Didn't receive the instructions?",
|
||||||
|
"Allow all comments from users with accounts": "Allow all comments from logged-in users"
|
||||||
}
|
}
|
||||||
|
|
|
@ -823,5 +823,6 @@
|
||||||
"{number} posts": "Aucun billet|Un billet|{number} billets",
|
"{number} posts": "Aucun billet|Un billet|{number} billets",
|
||||||
"{profile} (by default)": "{profile} (par défault)",
|
"{profile} (by default)": "{profile} (par défault)",
|
||||||
"{title} ({count} todos)": "{title} ({count} todos)",
|
"{title} ({count} todos)": "{title} ({count} todos)",
|
||||||
"© The OpenStreetMap Contributors": "© Les Contributeur⋅ices OpenStreetMap"
|
"© The OpenStreetMap Contributors": "© Les Contributeur⋅ices OpenStreetMap",
|
||||||
|
"Allow all comments from users with accounts": "Autoriser tous les commentaires d'utilisateur·ices avec des comptes"
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,7 +169,7 @@
|
||||||
v-model="event.options.commentModeration"
|
v-model="event.options.commentModeration"
|
||||||
name="commentModeration"
|
name="commentModeration"
|
||||||
:native-value="CommentModeration.ALLOW_ALL"
|
:native-value="CommentModeration.ALLOW_ALL"
|
||||||
>{{ $t("Allow all comments") }}</b-radio
|
>{{ $t("Allow all comments from users with accounts") }}</b-radio
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do
|
||||||
Handles the comment-related GraphQL calls.
|
Handles the comment-related GraphQL calls.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
alias Mobilizon.{Actors, Admin, Discussions}
|
alias Mobilizon.{Actors, Admin, Discussions, Events}
|
||||||
alias Mobilizon.Actors.Actor
|
alias Mobilizon.Actors.Actor
|
||||||
alias Mobilizon.Discussions.Comment, as: CommentModel
|
alias Mobilizon.Discussions.Comment, as: CommentModel
|
||||||
|
alias Mobilizon.Events.{Event, EventOptions}
|
||||||
alias Mobilizon.Users
|
alias Mobilizon.Users
|
||||||
alias Mobilizon.Users.User
|
alias Mobilizon.Users.User
|
||||||
import Mobilizon.Web.Gettext
|
import Mobilizon.Web.Gettext
|
||||||
|
@ -20,7 +21,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do
|
||||||
|
|
||||||
def create_comment(
|
def create_comment(
|
||||||
_parent,
|
_parent,
|
||||||
%{actor_id: actor_id} = args,
|
%{actor_id: actor_id, event_id: event_id} = args,
|
||||||
%{
|
%{
|
||||||
context: %{
|
context: %{
|
||||||
current_user: %User{} = user
|
current_user: %User{} = user
|
||||||
|
@ -28,10 +29,23 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do
|
||||||
}
|
}
|
||||||
) do
|
) do
|
||||||
with {:is_owned, %Actor{} = _organizer_actor} <- User.owns_actor(user, actor_id),
|
with {:is_owned, %Actor{} = _organizer_actor} <- User.owns_actor(user, actor_id),
|
||||||
|
{:find_event,
|
||||||
|
{:ok,
|
||||||
|
%Event{
|
||||||
|
options: %EventOptions{comment_moderation: comment_moderation},
|
||||||
|
organizer_actor_id: organizer_actor_id
|
||||||
|
}}} <-
|
||||||
|
{:find_event, Events.get_event(event_id)},
|
||||||
|
{actor_id, ""} <- Integer.parse(actor_id),
|
||||||
|
{:allowed, true} <-
|
||||||
|
{:allowed, comment_moderation != :closed || actor_id == organizer_actor_id},
|
||||||
{:ok, _, %CommentModel{} = comment} <-
|
{:ok, _, %CommentModel{} = comment} <-
|
||||||
Comments.create_comment(args) do
|
Comments.create_comment(args) do
|
||||||
{:ok, comment}
|
{:ok, comment}
|
||||||
else
|
else
|
||||||
|
{:allowed, false} ->
|
||||||
|
{:error, :unauthorized}
|
||||||
|
|
||||||
{:is_owned, nil} ->
|
{:is_owned, nil} ->
|
||||||
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
||||||
end
|
end
|
||||||
|
|
|
@ -66,7 +66,7 @@ defmodule Mobilizon.GraphQL.Schema.Discussions.CommentType do
|
||||||
@desc "Create a comment"
|
@desc "Create a comment"
|
||||||
field :create_comment, type: :comment do
|
field :create_comment, type: :comment do
|
||||||
arg(:text, non_null(:string))
|
arg(:text, non_null(:string))
|
||||||
arg(:event_id, :id)
|
arg(:event_id, non_null(:id))
|
||||||
arg(:in_reply_to_comment_id, :id)
|
arg(:in_reply_to_comment_id, :id)
|
||||||
arg(:actor_id, non_null(:id))
|
arg(:actor_id, non_null(:id))
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,11 @@ defmodule Mobilizon.GraphQL.Resolvers.CommentTest do
|
||||||
|
|
||||||
import Mobilizon.Factory
|
import Mobilizon.Factory
|
||||||
|
|
||||||
|
alias Mobilizon.Events
|
||||||
|
alias Mobilizon.Events.{Event, EventOptions}
|
||||||
alias Mobilizon.GraphQL.AbsintheHelpers
|
alias Mobilizon.GraphQL.AbsintheHelpers
|
||||||
|
|
||||||
@comment %{text: "I love this event"}
|
@comment_text "I love this event"
|
||||||
|
|
||||||
setup %{conn: conn} do
|
setup %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -16,75 +18,104 @@ defmodule Mobilizon.GraphQL.Resolvers.CommentTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "Comment Resolver" do
|
describe "Comment Resolver" do
|
||||||
|
@create_comment_mutation """
|
||||||
|
mutation CreateComment($text: String!, $actorId: ID, $eventId: ID, $inReplyToCommentId: ID) {
|
||||||
|
createComment(text: $text, actorId: $actorId, eventId: $eventId, inReplyToCommentId: $inReplyToCommentId) {
|
||||||
|
id,
|
||||||
|
text,
|
||||||
|
uuid,
|
||||||
|
inReplyToComment {
|
||||||
|
id,
|
||||||
|
text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
test "create_comment/3 creates a comment", %{
|
test "create_comment/3 creates a comment", %{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
actor: actor,
|
actor: actor,
|
||||||
user: user,
|
user: user,
|
||||||
event: event
|
event: event
|
||||||
} do
|
} do
|
||||||
mutation = """
|
|
||||||
mutation {
|
|
||||||
createComment(
|
|
||||||
text: "#{@comment.text}",
|
|
||||||
actor_id: "#{actor.id}",
|
|
||||||
event_id: "#{event.id}"
|
|
||||||
) {
|
|
||||||
text,
|
|
||||||
uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> AbsintheHelpers.graphql_query(query: mutation, variables: %{})
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @create_comment_mutation,
|
||||||
|
variables: %{text: @comment_text, actorId: actor.id, eventId: event.id}
|
||||||
|
)
|
||||||
|
|
||||||
assert res["data"]["createComment"]["text"] == @comment.text
|
assert res["data"]["createComment"]["text"] == @comment_text
|
||||||
end
|
end
|
||||||
|
|
||||||
test "create_comment/3 checks that user owns actor", %{conn: conn, user: user} do
|
test "create_comment/3 checks that user owns actor", %{conn: conn, user: user, event: event} do
|
||||||
actor = insert(:actor)
|
actor = insert(:actor)
|
||||||
|
|
||||||
mutation = """
|
|
||||||
mutation {
|
|
||||||
createComment(
|
|
||||||
text: "#{@comment.text}",
|
|
||||||
actor_id: "#{actor.id}"
|
|
||||||
) {
|
|
||||||
text,
|
|
||||||
uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> AbsintheHelpers.graphql_query(query: mutation, variables: %{})
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @create_comment_mutation,
|
||||||
|
variables: %{text: @comment_text, actorId: actor.id, eventId: event.id}
|
||||||
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] ==
|
assert hd(res["errors"])["message"] ==
|
||||||
"Profile is not owned by authenticated user"
|
"Profile is not owned by authenticated user"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "create_comment/3 requires that the user needs to be authenticated", %{conn: conn} do
|
test "create_comment/3 doesn't allow creating events if it's disabled", %{
|
||||||
actor = insert(:actor)
|
conn: conn,
|
||||||
|
actor: actor,
|
||||||
mutation = """
|
user: user,
|
||||||
mutation {
|
event: event
|
||||||
createComment(
|
} do
|
||||||
text: "#{@comment.text}",
|
{:ok, %Event{options: %EventOptions{comment_moderation: :closed}}} =
|
||||||
actor_id: "#{actor.id}"
|
Events.update_event(event, %{options: %{comment_moderation: :closed}})
|
||||||
) {
|
|
||||||
text,
|
|
||||||
uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> AbsintheHelpers.graphql_query(query: mutation, variables: %{})
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @create_comment_mutation,
|
||||||
|
variables: %{text: @comment_text, actorId: actor.id, eventId: event.id}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert hd(res["errors"])["message"] ==
|
||||||
|
"You don't have permission to do this"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create_comment/3 allows creating events if it's disabled but we're the organizer", %{
|
||||||
|
conn: conn,
|
||||||
|
actor: actor,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
event = insert(:event, organizer_actor: actor, options: %{comment_moderation: :closed})
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> auth_conn(user)
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @create_comment_mutation,
|
||||||
|
variables: %{text: @comment_text, actorId: actor.id, eventId: event.id}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert is_nil(res["errors"])
|
||||||
|
assert res["data"]["createComment"]["text"] == @comment_text
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create_comment/3 requires that the user needs to be authenticated", %{
|
||||||
|
conn: conn,
|
||||||
|
event: event
|
||||||
|
} do
|
||||||
|
actor = insert(:actor)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @create_comment_mutation,
|
||||||
|
variables: %{text: @comment_text, actorId: actor.id, eventId: event.id}
|
||||||
|
)
|
||||||
|
|
||||||
assert hd(res["errors"])["message"] ==
|
assert hd(res["errors"])["message"] ==
|
||||||
"You are not allowed to create a comment if not connected"
|
"You are not allowed to create a comment if not connected"
|
||||||
|
@ -98,35 +129,24 @@ defmodule Mobilizon.GraphQL.Resolvers.CommentTest do
|
||||||
} do
|
} do
|
||||||
comment = insert(:comment)
|
comment = insert(:comment)
|
||||||
|
|
||||||
mutation = """
|
|
||||||
mutation {
|
|
||||||
createComment(
|
|
||||||
text: "#{@comment.text}",
|
|
||||||
actor_id: "#{actor.id}",
|
|
||||||
event_id: "#{event.id}",
|
|
||||||
in_reply_to_comment_id: "#{comment.id}"
|
|
||||||
) {
|
|
||||||
id,
|
|
||||||
text,
|
|
||||||
uuid,
|
|
||||||
in_reply_to_comment {
|
|
||||||
id,
|
|
||||||
text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> auth_conn(user)
|
|> auth_conn(user)
|
||||||
|> AbsintheHelpers.graphql_query(query: mutation, variables: %{})
|
|> AbsintheHelpers.graphql_query(
|
||||||
|
query: @create_comment_mutation,
|
||||||
|
variables: %{
|
||||||
|
text: @comment_text,
|
||||||
|
actorId: actor.id,
|
||||||
|
eventId: event.id,
|
||||||
|
inReplyToCommentId: comment.id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
assert res["errors"] == nil
|
assert is_nil(res["errors"])
|
||||||
assert res["data"]["createComment"]["text"] == @comment.text
|
assert res["data"]["createComment"]["text"] == @comment_text
|
||||||
uuid = res["data"]["createComment"]["uuid"]
|
uuid = res["data"]["createComment"]["uuid"]
|
||||||
|
|
||||||
assert res["data"]["createComment"]["in_reply_to_comment"]["id"] ==
|
assert res["data"]["createComment"]["inReplyToComment"]["id"] ==
|
||||||
to_string(comment.id)
|
to_string(comment.id)
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
|
@ -144,7 +164,7 @@ defmodule Mobilizon.GraphQL.Resolvers.CommentTest do
|
||||||
|> AbsintheHelpers.graphql_query(query: query, variables: %{})
|
|> AbsintheHelpers.graphql_query(query: query, variables: %{})
|
||||||
|
|
||||||
assert res["errors"] == nil
|
assert res["errors"] == nil
|
||||||
assert res["data"]["thread"] == [%{"uuid" => uuid, "text" => @comment.text}]
|
assert res["data"]["thread"] == [%{"uuid" => uuid, "text" => @comment_text}]
|
||||||
end
|
end
|
||||||
|
|
||||||
@delete_comment """
|
@delete_comment """
|
||||||
|
|
Loading…
Reference in a new issue