diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/ClientSettings.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/ClientSettings.scala index bec18e1f6a..afee8869b3 100644 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/ClientSettings.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/ClientSettings.scala @@ -140,26 +140,26 @@ object ClientSettings extends SystemConfiguration { } yield { if (dataChannel.contains("name")) { val channelName = dataChannel("name").toString - val writePermission = { - if (dataChannel.contains("writePermission")) { - dataChannel("writePermission") match { + val pushPermission = { + if (dataChannel.contains("pushPermission")) { + dataChannel("pushPermission") match { case wPerm: List[String] => wPerm case _ => { - logger.warn(s"Invalid writePermission for channel $channelName in plugin $pluginName") + logger.warn(s"Invalid pushPermission for channel $channelName in plugin $pluginName") List() } } } else { - logger.warn(s"Missing config writePermission for channel $channelName in plugin $pluginName") + logger.warn(s"Missing config pushPermission for channel $channelName in plugin $pluginName") List() } } - val deletePermission = { - if (dataChannel.contains("deletePermission")) { - dataChannel("deletePermission") match { + val replaceOrDeletePermission = { + if (dataChannel.contains("replaceOrDeletePermission")) { + dataChannel("replaceOrDeletePermission") match { case dPerm: List[String] => dPerm case _ => { - logger.warn(s"Invalid deletePermission for channel $channelName in plugin $pluginName") + logger.warn(s"Invalid replaceOrDeletePermission for channel $channelName in plugin $pluginName") List() } } @@ -168,7 +168,7 @@ object ClientSettings extends SystemConfiguration { } } - pluginDataChannels += (channelName -> DataChannel(channelName, writePermission, deletePermission)) + pluginDataChannels += (channelName -> DataChannel(channelName, pushPermission, replaceOrDeletePermission)) } } case _ => logger.warn(s"Plugin $pluginName has an invalid dataChannels format") @@ -184,7 +184,7 @@ object ClientSettings extends SystemConfiguration { pluginsFromConfig } - case class DataChannel(name: String, writePermission: List[String], deletePermission: List[String]) + case class DataChannel(name: String, pushPermission: List[String], replaceOrDeletePermission: List[String]) case class Plugin(name: String, url: String, dataChannels: Map[String, DataChannel]) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelDeleteEntryMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelDeleteEntryMsgHdlr.scala index 17c8ca7e78..2cc9f5104c 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelDeleteEntryMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelDeleteEntryMsgHdlr.scala @@ -25,21 +25,21 @@ trait PluginDataChannelDeleteEntryMsgHdlr extends HandlerHelpers { println(s"Data channel '${msg.body.channelName}' not found in plugin '${msg.body.pluginName}'.") } else { val hasPermission = for { - deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).deletePermission + replaceOrDeletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).replaceOrDeletePermission } yield { - deletePermission.toLowerCase match { + replaceOrDeletePermission.toLowerCase match { case "all" => true case "moderator" => user.role == Roles.MODERATOR_ROLE case "presenter" => user.presenter - case "sender" => { - val senderUserId = PluginDataChannelEntryDAO.getMessageSender( + case "creator" => { + val creatorUserId = PluginDataChannelEntryDAO.getEntryCreator( meetingId, msg.body.pluginName, msg.body.channelName, msg.body.subChannelName, msg.body.entryId ) - senderUserId == msg.header.userId + creatorUserId == msg.header.userId } case _ => false } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelPushEntryMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelPushEntryMsgHdlr.scala index 32b0b52604..fc6b8c4c45 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelPushEntryMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelPushEntryMsgHdlr.scala @@ -25,9 +25,9 @@ trait PluginDataChannelPushEntryMsgHdlr extends HandlerHelpers { println(s"Data channel '${msg.body.channelName}' not found in plugin '${msg.body.pluginName}'.") } else { val hasPermission = for { - writePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).writePermission + pushPermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).pushPermission } yield { - writePermission.toLowerCase match { + pushPermission.toLowerCase match { case "all" => true case "moderator" => user.role == Roles.MODERATOR_ROLE case "presenter" => user.presenter diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelReplaceEntryMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelReplaceEntryMsgHdlr.scala new file mode 100755 index 0000000000..453f64587d --- /dev/null +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelReplaceEntryMsgHdlr.scala @@ -0,0 +1,63 @@ +package org.bigbluebutton.core.apps.plugin + +import org.bigbluebutton.ClientSettings +import org.bigbluebutton.common2.msgs.PluginDataChannelReplaceEntryMsg +import org.bigbluebutton.core.db.{JsonUtils, PluginDataChannelEntryDAO} +import org.bigbluebutton.core.domain.MeetingState2x +import org.bigbluebutton.core.models.{Roles, Users2x} +import org.bigbluebutton.core.running.{HandlerHelpers, LiveMeeting} + +trait PluginDataChannelReplaceEntryMsgHdlr extends HandlerHelpers { + + def handle(msg: PluginDataChannelReplaceEntryMsg, state: MeetingState2x, liveMeeting: LiveMeeting): Unit = { + val pluginsDisabled: Boolean = liveMeeting.props.meetingProp.disabledFeatures.contains("plugins") + val meetingId = liveMeeting.props.meetingProp.intId + + for { + _ <- if (!pluginsDisabled) Some(()) else None + user <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId) + } yield { + val pluginsConfig = ClientSettings.getPluginsFromConfig(ClientSettings.clientSettingsFromFile) + + if (!pluginsConfig.contains(msg.body.pluginName)) { + println(s"Plugin '${msg.body.pluginName}' not found.") + } else if (!pluginsConfig(msg.body.pluginName).dataChannels.contains(msg.body.channelName)) { + println(s"Data channel '${msg.body.channelName}' not found in plugin '${msg.body.pluginName}'.") + } else { + val hasPermission = for { + replaceOrDeletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).replaceOrDeletePermission + } yield { + replaceOrDeletePermission.toLowerCase match { + case "all" => true + case "moderator" => user.role == Roles.MODERATOR_ROLE + case "presenter" => user.presenter + case "creator" => { + val creatorUserId = PluginDataChannelEntryDAO.getEntryCreator( + meetingId, + msg.body.pluginName, + msg.body.channelName, + msg.body.subChannelName, + msg.body.entryId + ) + creatorUserId == msg.header.userId + } + case _ => false + } + } + + if (!hasPermission.contains(true)) { + println(s"No permission to write in plugin: '${msg.body.pluginName}', data channel: '${msg.body.channelName}'.") + } else { + PluginDataChannelEntryDAO.replace( + msg.header.meetingId, + msg.body.pluginName, + msg.body.channelName, + msg.body.subChannelName, + msg.body.entryId, + JsonUtils.mapToJson(msg.body.payloadJson), + ) + } + } + } + } +} diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelResetMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelResetMsgHdlr.scala index 16ac38b0ae..32a403f862 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelResetMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginDataChannelResetMsgHdlr.scala @@ -25,9 +25,9 @@ trait PluginDataChannelResetMsgHdlr extends HandlerHelpers { println(s"Data channel '${msg.body.channelName}' not found in plugin '${msg.body.pluginName}'.") } else { val hasPermission = for { - deletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).deletePermission + replaceOrDeletePermission <- pluginsConfig(msg.body.pluginName).dataChannels(msg.body.channelName).replaceOrDeletePermission } yield { - deletePermission.toLowerCase match { + replaceOrDeletePermission.toLowerCase match { case "all" => true case "moderator" => user.role == Roles.MODERATOR_ROLE case "presenter" => user.presenter diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginHdlrs.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginHdlrs.scala index d9f8bb78f8..d1dbf285fc 100644 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginHdlrs.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/plugin/PluginHdlrs.scala @@ -6,6 +6,7 @@ import org.bigbluebutton.common2.msgs.PluginDataChannelDeleteEntryMsgBody class PluginHdlrs(implicit val context: ActorContext) extends PluginDataChannelPushEntryMsgHdlr + with PluginDataChannelReplaceEntryMsgHdlr with PluginDataChannelDeleteEntryMsgHdlr with PluginDataChannelResetMsgHdlr { diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/PluginDataChannelEntryDAO.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/PluginDataChannelEntryDAO.scala index 424b91ea17..e53d924aa2 100644 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/PluginDataChannelEntryDAO.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/db/PluginDataChannelEntryDAO.scala @@ -2,6 +2,7 @@ package org.bigbluebutton.core.db import PostgresProfile.api._ import org.bigbluebutton.core.db.DatabaseConnection.{db, logger} +import org.bigbluebutton.core.util.RandomStringGenerator import spray.json.JsValue import scala.concurrent.ExecutionContext.Implicits.global @@ -18,9 +19,9 @@ case class PluginDataChannelEntryDbModel( pluginName: String, channelName: String, subChannelName: String, -// entryId: Option[String] = None, + entryId: Option[String] = None, payloadJson: JsValue, - fromUserId: String, + createdBy: String, toRoles: Option[List[String]], toUserIds: Option[List[String]], createdAt: java.sql.Timestamp, @@ -32,28 +33,29 @@ class PluginDataChannelEntryDbTableDef(tag: Tag) extends Table[PluginDataChannel val pluginName = column[String]("pluginName", O.PrimaryKey) val channelName = column[String]("channelName", O.PrimaryKey) val subChannelName = column[String]("subChannelName") -// val entryId = column[Option[String]]("messageId", O.PrimaryKey) //// The messageId is generated by the database + val entryId = column[Option[String]]("entryId", O.PrimaryKey) val payloadJson = column[JsValue]("payloadJson") - val fromUserId = column[String]("fromUserId") + val createdBy = column[String]("createdBy") val toRoles = column[Option[List[String]]]("toRoles") val toUserIds = column[Option[List[String]]]("toUserIds") val createdAt = column[java.sql.Timestamp]("createdAt") val deletedAt = column[Option[java.sql.Timestamp]]("deletedAt") - override def * = (meetingId, pluginName, channelName, subChannelName, payloadJson, fromUserId, toRoles, toUserIds, createdAt, deletedAt) <> (PluginDataChannelEntryDbModel.tupled, PluginDataChannelEntryDbModel.unapply) + override def * = (meetingId, pluginName, channelName, subChannelName, entryId, payloadJson, createdBy, toRoles, toUserIds, createdAt, deletedAt) <> (PluginDataChannelEntryDbModel.tupled, PluginDataChannelEntryDbModel.unapply) } object PluginDataChannelEntryDAO { - def insert(meetingId: String, pluginName: String, channelName: String, subChannelName: String, senderUserId: String, + def insert(meetingId: String, pluginName: String, channelName: String, subChannelName: String, createdBy: String, payloadJson: Map[String, Any], toRoles: List[String], toUserIds: List[String]) = { DatabaseConnection.db.run( TableQuery[PluginDataChannelEntryDbTableDef].forceInsert( PluginDataChannelEntryDbModel( + entryId = Some(RandomStringGenerator.randomAlphanumericString(50)), meetingId = meetingId, pluginName = pluginName, channelName = channelName, subChannelName = subChannelName, payloadJson = JsonUtils.mapToJson(payloadJson), - fromUserId = senderUserId, + createdBy = createdBy, toRoles = toRoles.map(_.toUpperCase).filter(Permission.allowedRoles.contains) match { case Nil => None case filtered => Some(filtered) @@ -86,9 +88,9 @@ object PluginDataChannelEntryDAO { } } - def getMessageSender(meetingId: String, pluginName: String, channelName: String, - subChannelName: String, entryId: String): String = { - val query = sql"""SELECT "fromUserId" + def getEntryCreator(meetingId: String, pluginName: String, channelName: String, + subChannelName: String, entryId: String): String = { + val query = sql"""SELECT "createdBy" FROM "pluginDataChannelEntry" WHERE "deletedAt" is null AND "meetingId" = ${meetingId} @@ -123,4 +125,23 @@ object PluginDataChannelEntryDAO { } } + def replace(meetingId: String, pluginName: String, channelName: String, + subChannelName: String, entryId: String, payloadJson: JsValue) = { + + DatabaseConnection.db.run( + TableQuery[PluginDataChannelEntryDbTableDef] + .filter(_.meetingId === meetingId) + .filter(_.pluginName === pluginName) + .filter(_.channelName === channelName) + .filter(_.subChannelName === subChannelName) + .filter(_.entryId === entryId) + .filter(_.deletedAt.isEmpty) + .map(_.payloadJson) + .update(payloadJson) + ).onComplete { + case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated with new payloadJson on pluginDataChannelEntry table!") + case Failure(e) => DatabaseConnection.logger.debug(s"Error updating with new payloadJson for table pluginDataChannelEntry: $e") + } + } + } \ No newline at end of file diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala index 3b768eace1..cc1bd7d5b4 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala @@ -418,6 +418,9 @@ class ReceivedJsonMsgHandlerActor( case PluginDataChannelPushEntryMsg.NAME => routeGenericMsg[PluginDataChannelPushEntryMsg](envelope, jsonNode) + case PluginDataChannelReplaceEntryMsg.NAME => + routeGenericMsg[PluginDataChannelReplaceEntryMsg](envelope, jsonNode) + case PluginDataChannelDeleteEntryMsg.NAME => routeGenericMsg[PluginDataChannelDeleteEntryMsg](envelope, jsonNode) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala index 3a744970f0..7ba1a20d41 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala @@ -714,9 +714,10 @@ class MeetingActor( state = groupChatApp.handle(m, state, liveMeeting, msgBus) // Plugin - case m: PluginDataChannelPushEntryMsg => pluginHdlrs.handle(m, state, liveMeeting) - case m: PluginDataChannelDeleteEntryMsg => pluginHdlrs.handle(m, state, liveMeeting) - case m: PluginDataChannelResetMsg => pluginHdlrs.handle(m, state, liveMeeting) + case m: PluginDataChannelPushEntryMsg => pluginHdlrs.handle(m, state, liveMeeting) + case m: PluginDataChannelReplaceEntryMsg => pluginHdlrs.handle(m, state, liveMeeting) + case m: PluginDataChannelDeleteEntryMsg => pluginHdlrs.handle(m, state, liveMeeting) + case m: PluginDataChannelResetMsg => pluginHdlrs.handle(m, state, liveMeeting) // Webcams case m: UserBroadcastCamStartMsg => diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/PluginMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/PluginMsgs.scala index 9e4aef15b6..e4ba5e601b 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/PluginMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/PluginMsgs.scala @@ -16,20 +16,30 @@ case class PluginDataChannelPushEntryMsgBody( toUserIds: List[String], ) +object PluginDataChannelReplaceEntryMsg { val NAME = "PluginDataChannelReplaceEntryMsg" } +case class PluginDataChannelReplaceEntryMsg(header: BbbClientMsgHeader, body: PluginDataChannelReplaceEntryMsgBody) extends StandardMsg +case class PluginDataChannelReplaceEntryMsgBody( + pluginName: String, + channelName: String, + subChannelName: String, + payloadJson: Map[String, Any], + entryId: String, + ) + object PluginDataChannelDeleteEntryMsg { val NAME = "PluginDataChannelDeleteEntryMsg" } case class PluginDataChannelDeleteEntryMsg(header: BbbClientMsgHeader, body: PluginDataChannelDeleteEntryMsgBody) extends StandardMsg case class PluginDataChannelDeleteEntryMsgBody( - pluginName: String, - subChannelName: String, - channelName: String, - entryId: String - ) + pluginName: String, + subChannelName: String, + channelName: String, + entryId: String + ) object PluginDataChannelResetMsg { val NAME = "PluginDataChannelResetMsg" } case class PluginDataChannelResetMsg(header: BbbClientMsgHeader, body: PluginDataChannelResetMsgBody) extends StandardMsg case class PluginDataChannelResetMsgBody( - pluginName: String, - subChannelName: String, - channelName: String - ) + pluginName: String, + subChannelName: String, + channelName: String + ) diff --git a/bbb-graphql-actions/src/actions/pluginDataChannelReplaceEntry.ts b/bbb-graphql-actions/src/actions/pluginDataChannelReplaceEntry.ts new file mode 100644 index 0000000000..ed22c24020 --- /dev/null +++ b/bbb-graphql-actions/src/actions/pluginDataChannelReplaceEntry.ts @@ -0,0 +1,38 @@ +import { RedisMessage } from '../types'; +import { ValidationError } from '../types/ValidationError'; +import {throwErrorIfInvalidInput} from "../imports/validation"; + +export default function buildRedisMessage(sessionVariables: Record, input: Record): RedisMessage { + throwErrorIfInvalidInput(input, + [ + {name: 'pluginName', type: 'string', required: true}, + {name: 'subChannelName', type: 'string', required: true}, + {name: 'channelName', type: 'string', required: true}, + {name: 'payloadJson', type: 'json', required: true}, + {name: 'entryId', type: 'string', required: true}, + ] + ) + + const eventName = `PluginDataChannelReplaceEntryMsg`; + + const routing = { + meetingId: sessionVariables['x-hasura-meetingid'] as String, + userId: sessionVariables['x-hasura-userid'] as String + }; + + const header = { + name: eventName, + meetingId: routing.meetingId, + userId: routing.userId + }; + + const body = { + pluginName: input.pluginName, + channelName: input.channelName, + subChannelName: input.subChannelName, + payloadJson: input.payloadJson, + entryId: input.entryId, + }; + + return { eventName, routing, header, body }; +} diff --git a/bbb-graphql-server/bbb_schema.sql b/bbb-graphql-server/bbb_schema.sql index cb40ae8cce..d27211249d 100644 --- a/bbb-graphql-server/bbb_schema.sql +++ b/bbb-graphql-server/bbb_schema.sql @@ -2033,13 +2033,13 @@ CREATE TABLE "pluginDataChannelEntry" ( "entryId" varchar(50) DEFAULT uuid_generate_v4(), "subChannelName" varchar(255), "payloadJson" jsonb, - "fromUserId" varchar(50), + "createdBy" varchar(50), "toRoles" varchar[], --MODERATOR, VIEWER, PRESENTER "toUserIds" varchar[], "createdAt" timestamp with time zone DEFAULT current_timestamp, "deletedAt" timestamp with time zone, CONSTRAINT "pluginDataChannel_pkey" PRIMARY KEY ("meetingId","pluginName","channelName","entryId", "subChannelName"), - FOREIGN KEY ("meetingId", "fromUserId") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE + FOREIGN KEY ("meetingId", "createdBy") REFERENCES "user"("meetingId","userId") ON DELETE CASCADE ); create index "idx_pluginDataChannelEntry_pk_reverse" on "pluginDataChannelEntry"("pluginName", "meetingId", "channelName", "subChannelName"); create index "idx_pluginDataChannelEntry_pk_reverse_b" on "pluginDataChannelEntry"("channelName", "pluginName", "meetingId", "subChannelName"); @@ -2048,7 +2048,7 @@ create index "idx_pluginDataChannelEntry_channelName" on "pluginDataChannelEntry create index "idx_pluginDataChannelEntry_roles" on "pluginDataChannelEntry"("meetingId", "toRoles", "toUserIds", "createdAt") where "deletedAt" is null; CREATE OR REPLACE VIEW "v_pluginDataChannelEntry" AS -SELECT u."meetingId", u."userId", m."pluginName", m."channelName", m."subChannelName", m."entryId", m."payloadJson", m."fromUserId", m."toRoles", m."createdAt" +SELECT u."meetingId", u."userId", m."pluginName", m."channelName", m."subChannelName", m."entryId", m."payloadJson", m."createdBy", m."toRoles", m."createdAt" FROM "user" u JOIN "pluginDataChannelEntry" m ON m."meetingId" = u."meetingId" AND ((m."toRoles" IS NULL AND m."toUserIds" IS NULL) diff --git a/bbb-graphql-server/metadata/actions.graphql b/bbb-graphql-server/metadata/actions.graphql index 1720590bb8..d2dcc6b3eb 100644 --- a/bbb-graphql-server/metadata/actions.graphql +++ b/bbb-graphql-server/metadata/actions.graphql @@ -234,6 +234,16 @@ type Mutation { ): Boolean } +type Mutation { + pluginDataChannelReplaceEntry( + pluginName: String! + subChannelName: String! + channelName: String! + entryId: String! + payloadJson: String! + ): Boolean +} + type Mutation { pluginDataChannelReset( pluginName: String! diff --git a/bbb-graphql-server/metadata/actions.yaml b/bbb-graphql-server/metadata/actions.yaml index 7f4a0aef40..cb2cb62959 100644 --- a/bbb-graphql-server/metadata/actions.yaml +++ b/bbb-graphql-server/metadata/actions.yaml @@ -203,6 +203,12 @@ actions: handler: '{{HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL}}' permissions: - role: bbb_client + - name: pluginDataChannelReplaceEntry + definition: + kind: synchronous + handler: '{{HASURA_BBB_GRAPHQL_ACTIONS_ADAPTER_URL}}' + permissions: + - role: bbb_client - name: pluginDataChannelReset definition: kind: synchronous diff --git a/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_pluginDataChannelEntry.yaml b/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_pluginDataChannelEntry.yaml index 50723d0084..c21c618b72 100644 --- a/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_pluginDataChannelEntry.yaml +++ b/bbb-graphql-server/metadata/databases/BigBlueButton/tables/public_v_pluginDataChannelEntry.yaml @@ -7,11 +7,11 @@ configuration: custom_name: pluginDataChannelEntry custom_root_fields: {} object_relationships: - - name: sender + - name: creator using: manual_configuration: column_mapping: - fromUserId: userId + createdBy: userId meetingId: meetingId insertion_order: null remote_table: @@ -27,7 +27,7 @@ select_permissions: - channelName - entryId - payloadJson - - fromUserId + - createdBy - toRoles filter: _and: diff --git a/bigbluebutton-html5/imports/ui/components/plugins-engine/data-channel/channel-manager/writer-manager.tsx b/bigbluebutton-html5/imports/ui/components/plugins-engine/data-channel/channel-manager/writer-manager.tsx index f77ce615a0..a67a1f6e76 100644 --- a/bigbluebutton-html5/imports/ui/components/plugins-engine/data-channel/channel-manager/writer-manager.tsx +++ b/bigbluebutton-html5/imports/ui/components/plugins-engine/data-channel/channel-manager/writer-manager.tsx @@ -5,12 +5,16 @@ import * as PluginSdk from 'bigbluebutton-html-plugin-sdk'; import { DataChannelArguments, PushEntryFunction, ObjectTo, ToRole, ToUserId, + ReplaceEntryFunctionArguments, } from 'bigbluebutton-html-plugin-sdk/dist/cjs/data-channel/types'; import { DataChannelHooks } from 'bigbluebutton-html-plugin-sdk/dist/cjs/data-channel/enums'; import { HookEvents } from 'bigbluebutton-html-plugin-sdk/dist/cjs/core/enum'; import { HookEventWrapper, UpdatedEventDetails } from 'bigbluebutton-html-plugin-sdk/dist/cjs/core/types'; -import { PLUGIN_DATA_CHANNEL_DELETE_MUTATION, PLUGIN_DATA_CHANNEL_PUSH_MUTATION, PLUGIN_DATA_CHANNEL_RESET_MUTATION } from '../mutations'; +import { + PLUGIN_DATA_CHANNEL_DELETE_MUTATION, PLUGIN_DATA_CHANNEL_PUSH_MUTATION, + PLUGIN_DATA_CHANNEL_REPLACE_MUTATION, PLUGIN_DATA_CHANNEL_RESET_MUTATION, +} from '../mutations'; export interface DataChannelItemManagerWriterProps { pluginName: string; @@ -46,9 +50,10 @@ const DataChannelItemManagerWriter: React.ElementType { const argumentsOfPushEntryFunction = { @@ -82,7 +87,7 @@ const DataChannelItemManagerWriter: React.ElementType 0) argumentsOfPushEntryFunction.variables.toRoles = rolesTo; if (usersTo.length > 0) argumentsOfPushEntryFunction.variables.toUserIds = usersTo; } - pushEntryFunctionPluginDataChannelMessage(argumentsOfPushEntryFunction); + pushEntryFunctionPluginDataChannel(argumentsOfPushEntryFunction); }) as PushEntryFunction; pluginApi.mapOfPushEntryFunctions[dataChannelIdentifier] = useDataChannelHandlerFunction; @@ -93,7 +98,7 @@ const DataChannelItemManagerWriter: React.ElementType; const hookArguments = eventDetails?.hookArguments as DataChannelArguments | undefined; - deleteEntryFunctionPluginDataChannelMessage({ + deleteEntryFunctionPluginDataChannel({ variables: { pluginName: hookArguments?.pluginName, channelName: hookArguments?.channelName, @@ -104,7 +109,7 @@ const DataChannelItemManagerWriter: React.ElementType; const hookArguments = eventDetails?.hookArguments as DataChannelArguments | undefined; - resetFunctionPluginDataChannelMessage({ + resetFunctionPluginDataChannel({ variables: { pluginName: hookArguments?.pluginName, channelName: hookArguments?.channelName, @@ -114,10 +119,29 @@ const DataChannelItemManagerWriter: React.ElementType) => { + if (event.detail.hook === DataChannelHooks.DATA_CHANNEL_REPLACE) { + const eventDetails = event.detail as UpdatedEventDetails>; + const hookArguments = eventDetails?.hookArguments as DataChannelArguments | undefined; + replaceEntryFunctionPluginDataChannel({ + variables: { + pluginName: hookArguments?.pluginName, + channelName: hookArguments?.channelName, + subChannelName: hookArguments?.subChannelName, + entryId: eventDetails.data.entryId, + payloadJson: eventDetails.data.payloadJson, + }, + }); + } + }) as EventListener; + useEffect(() => { window.addEventListener(HookEvents.UPDATED, deleteOrResetHandler); + window.addEventListener(HookEvents.UPDATED, replaceEntryHandler); return () => { window.removeEventListener(HookEvents.UPDATED, deleteOrResetHandler); + window.removeEventListener(HookEvents.UPDATED, replaceEntryHandler); }; }, []); return null; diff --git a/bigbluebutton-html5/imports/ui/components/plugins-engine/data-channel/mutations.ts b/bigbluebutton-html5/imports/ui/components/plugins-engine/data-channel/mutations.ts index d93a0aa137..8a4f562cd6 100644 --- a/bigbluebutton-html5/imports/ui/components/plugins-engine/data-channel/mutations.ts +++ b/bigbluebutton-html5/imports/ui/components/plugins-engine/data-channel/mutations.ts @@ -36,3 +36,17 @@ export const PLUGIN_DATA_CHANNEL_DELETE_MUTATION = gql` ) } `; + +export const PLUGIN_DATA_CHANNEL_REPLACE_MUTATION = gql` + mutation PluginDataChannelReplaceEntry($pluginName: String!, + $subChannelName: String!, $channelName: String!, + $payloadJson: json!, $entryId: String!) { + pluginDataChannelReplaceEntry( + entryId: $entryId, + pluginName: $pluginName, + channelName: $channelName, + subChannelName: $subChannelName, + payloadJson: $payloadJson + ) + } +`; diff --git a/bigbluebutton-html5/imports/ui/components/plugins-engine/data-channel/subscriptions.ts b/bigbluebutton-html5/imports/ui/components/plugins-engine/data-channel/subscriptions.ts index f2af9c8215..dbae7c7ef4 100644 --- a/bigbluebutton-html5/imports/ui/components/plugins-engine/data-channel/subscriptions.ts +++ b/bigbluebutton-html5/imports/ui/components/plugins-engine/data-channel/subscriptions.ts @@ -16,7 +16,7 @@ const PLUGIN_DATA_CHANNEL_NEW_ITEMS = gql` subChannelName, entryId, payloadJson, - fromUserId, + createdBy, pluginName, toRoles, } @@ -40,7 +40,7 @@ const PLUGIN_DATA_CHANNEL_All_ITEMS = gql` subChannelName, entryId, payloadJson, - fromUserId, + createdBy, pluginName, toRoles, } @@ -65,7 +65,7 @@ const PLUGIN_DATA_CHANNEL_LATEST_ITEM = gql` subChannelName, entryId, payloadJson, - fromUserId, + createdBy, pluginName, toRoles, }