Refactor all references for activity-report renaming to learning-dashboard

This commit is contained in:
Gustavo Trott 2021-08-25 11:38:35 -03:00
parent 241ab5f233
commit cd15f17394
31 changed files with 174 additions and 152 deletions

View File

@ -12,7 +12,7 @@ import org.bigbluebutton.core2.AnalyticsActor
import org.bigbluebutton.core2.FromAkkaAppsMsgSenderActor
import org.bigbluebutton.endpoint.redis.AppsRedisSubscriberActor
import org.bigbluebutton.endpoint.redis.RedisRecorderActor
import org.bigbluebutton.endpoint.redis.ActivityTrackerActor
import org.bigbluebutton.endpoint.redis.LearningDashboardActor
import org.bigbluebutton.common2.bus.IncomingJsonMessageBus
import org.bigbluebutton.service.{ HealthzService, MeetingInfoActor, MeetingInfoService }
@ -59,9 +59,9 @@ object Boot extends App with SystemConfiguration {
"redisRecorderActor"
)
val activityTrackerActor = system.actorOf(
ActivityTrackerActor.props(system, outGW),
"activityTrackerActor"
val learningDashboardActor = system.actorOf(
LearningDashboardActor.props(system, outGW),
"LearningDashboardActor"
)
recordingEventBus.subscribe(redisRecorderActor, outMessageChannel)
@ -76,8 +76,8 @@ object Boot extends App with SystemConfiguration {
outBus2.subscribe(analyticsActorRef, outBbbMsgMsgChannel)
bbbMsgBus.subscribe(analyticsActorRef, analyticsChannel)
outBus2.subscribe(activityTrackerActor, outBbbMsgMsgChannel)
bbbMsgBus.subscribe(activityTrackerActor, analyticsChannel)
outBus2.subscribe(learningDashboardActor, outBbbMsgMsgChannel)
bbbMsgBus.subscribe(learningDashboardActor, analyticsChannel)
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, eventBus, bbbMsgBus, outGW, healthzService), "bigbluebutton-actor")
eventBus.subscribe(bbbActor, meetingManagerChannel)

View File

@ -545,12 +545,12 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event)
}
def buildActivityReportEvtMsg(meetingId: String, activityJson: String): BbbCommonEnvCoreMsg = {
def buildLearningDashboardEvtMsg(meetingId: String, activityJson: String): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(ActivityReportEvtMsg.NAME, routing)
val body = ActivityReportEvtMsgBody(activityJson)
val header = BbbCoreHeaderWithMeetingId(ActivityReportEvtMsg.NAME, meetingId)
val event = ActivityReportEvtMsg(header, body)
val envelope = BbbCoreEnvelope(LearningDashboardEvtMsg.NAME, routing)
val body = LearningDashboardEvtMsgBody(activityJson)
val header = BbbCoreHeaderWithMeetingId(LearningDashboardEvtMsg.NAME, meetingId)
val event = LearningDashboardEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}

View File

@ -15,19 +15,19 @@ import ExecutionContext.Implicits.global
case object SendPeriodicReport
case class MeetingActivityTracker(
case class Meeting(
intId: String,
extId: String,
name: String,
activityReportAccessToken: String,
users: Map[String, UserActivityTracker] = Map(),
learningDashboardAccessToken: String,
users: Map[String, User] = Map(),
polls: Map[String, Poll] = Map(),
screenshares: Vector[Screenshare] = Vector(),
createdOn: Long = System.currentTimeMillis(),
endedOn: Long = 0,
)
case class UserActivityTracker(
case class User(
intId: String,
extId: String,
name: String,
@ -73,24 +73,24 @@ case class Screenshare(
)
object ActivityTrackerActor {
object LearningDashboardActor {
def props(
system: ActorSystem,
outGW: OutMessageGateway,
): Props =
Props(
classOf[ActivityTrackerActor],
classOf[LearningDashboardActor],
system,
outGW
)
}
class ActivityTrackerActor(
class LearningDashboardActor(
system: ActorSystem,
val outGW: OutMessageGateway,
) extends Actor with ActorLogging {
private var meetings: Map[String, MeetingActivityTracker] = Map()
private var meetings: Map[String, Meeting] = Map()
private var meetingsLastJsonHash : Map[String,String] = Map()
system.scheduler.schedule(10.seconds, 10.seconds, self, SendPeriodicReport)
@ -155,8 +155,8 @@ class ActivityTrackerActor(
for {
meeting <- meetings.values.find(m => m.intId == msg.header.meetingId)
} yield {
val user: UserActivityTracker = meeting.users.values.find(u => u.intId == msg.body.intId).getOrElse({
UserActivityTracker(
val user: User = meeting.users.values.find(u => u.intId == msg.body.intId).getOrElse({
User(
msg.body.intId, msg.body.extId, msg.body.name, (msg.body.role == Roles.MODERATOR_ROLE)
)
})
@ -235,8 +235,8 @@ class ActivityTrackerActor(
for {
meeting <- meetings.values.find(m => m.intId == msg.header.meetingId)
} yield {
val user: UserActivityTracker = meeting.users.values.find(u => u.intId == msg.body.intId).getOrElse({
UserActivityTracker(
val user: User = meeting.users.values.find(u => u.intId == msg.body.intId).getOrElse({
User(
msg.body.intId, msg.body.callerNum, msg.body.callerName, false, true
)
})
@ -286,7 +286,7 @@ class ActivityTrackerActor(
}
}
private def endUserTalk(meeting: MeetingActivityTracker, user: UserActivityTracker): Unit = {
private def endUserTalk(meeting: Meeting, user: User): Unit = {
if(user.talk.lastTalkStartedOn > 0) {
val updatedUser = user.copy(
talk = user.talk.copy(
@ -354,19 +354,19 @@ class ActivityTrackerActor(
}
private def handleCreateMeetingReqMsg(msg: CreateMeetingReqMsg): Unit = {
if(msg.body.props.meetingProp.activityReportTracking) {
val newMeeting = MeetingActivityTracker(
if(msg.body.props.meetingProp.learningDashboardEnabled) {
val newMeeting = Meeting(
msg.body.props.meetingProp.intId,
msg.body.props.meetingProp.extId,
msg.body.props.meetingProp.name,
msg.body.props.password.activityReportAccessToken,
msg.body.props.password.learningDashboardAccessToken,
)
meetings += (newMeeting.intId -> newMeeting)
log.info("ActivityTracker created for meeting {}.",msg.body.props.meetingProp.intId)
log.info(" created for meeting {}.",msg.body.props.meetingProp.intId)
} else {
log.info("ActivityTracker disabled for meeting {}.",msg.body.props.meetingProp.intId)
log.info(" disabled for meeting {}.",msg.body.props.meetingProp.intId)
}
}
@ -404,7 +404,7 @@ class ActivityTrackerActor(
sendReport(updatedMeeting)
meetings = meetings.-(updatedMeeting.intId)
log.info("ActivityTracker removed for meeting {}.",updatedMeeting.intId)
log.info(" removed for meeting {}.",updatedMeeting.intId)
}
}
@ -414,14 +414,14 @@ class ActivityTrackerActor(
})
}
private def sendReport(meeting : MeetingActivityTracker): Unit = {
private def sendReport(meeting : Meeting): Unit = {
val activityJson: String = JsonUtil.toJson(meeting)
//Avoid send repeated activity jsons
val activityJsonHash : String = MessageDigest.getInstance("MD5").digest(activityJson.getBytes).mkString
if(!meetingsLastJsonHash.contains(meeting.intId) || meetingsLastJsonHash.get(meeting.intId).getOrElse("") != activityJsonHash) {
val event = MsgBuilder.buildActivityReportEvtMsg(meeting.intId, activityJson)
val event = MsgBuilder.buildLearningDashboardEvtMsg(meeting.intId, activityJson)
outGW.send(event)
meetingsLastJsonHash += (meeting.intId -> activityJsonHash)

View File

@ -29,7 +29,7 @@ trait AppsTestFixtures {
val webcamsOnlyForModerator = false;
val moderatorPassword = "modpass"
val viewerPassword = "viewpass"
val activityReportAccessToken = "arToken"
val learningDashboardAccessToken = "ldToken"
val createTime = System.currentTimeMillis
val createDate = "Oct 26, 2015"
val isBreakout = false
@ -53,7 +53,7 @@ trait AppsTestFixtures {
val durationProps = DurationProps(duration = durationInMinutes, createdTime = createTime, createdDate = createDate,
meetingExpireIfNoUserJoinedInMinutes = meetingExpireIfNoUserJoinedInMinutes, meetingExpireWhenLastUserLeftInMinutes = meetingExpireWhenLastUserLeftInMinutes,
userInactivityInspectTimerInMinutes = userInactivityInspectTimerInMinutes, userInactivityThresholdInMinutes = userInactivityInspectTimerInMinutes, userActivitySignResponseDelayInMinutes = userActivitySignResponseDelayInMinutes)
val password = PasswordProp(moderatorPass = moderatorPassword, viewerPass = viewerPassword, activityReportAccessToken = activityReportAccessToken)
val password = PasswordProp(moderatorPass = moderatorPassword, viewerPass = viewerPassword, learningDashboardAccessToken = learningDashboardAccessToken)
val recordProp = RecordProp(record = record, autoStartRecording = autoStartRecording,
allowStartStopRecording = allowStartStopRecording, keepEvents = keepEvents )
val welcomeProp = WelcomeProp(welcomeMsgTemplate = welcomeMsgTemplate, welcomeMsg = welcomeMsg,

View File

@ -0,0 +1,23 @@
Learning Dashboard will be accessible through https://yourdomain/learning-dashboard
# Dev Instructions
## Prepare destination directory
```
mkdir /var/bigbluebutton/learning-dashboard
chown bigbluebutton /var/bigbluebutton/learning-dashboard/
```
## Build instructions
```
cd bbb-learning-dashboard
rm -r node_modules
npm install
npm run build
cp -r build/* /var/bigbluebutton/learning-dashboard
```
## Update nginx config
```
cp bbb-learning-dashboard/learning-dashboard.nginx /etc/bigbluebutton/nginx/
```

View File

@ -1,5 +0,0 @@
location /learning-dashboard/ {
alias /var/bigbluebutton/activity-report/;
autoindex off;
}

View File

@ -0,0 +1,5 @@
location /learning-dashboard/ {
alias /var/bigbluebutton/learning-dashboard/;
autoindex off;
}

View File

@ -1,5 +1,5 @@
{
"name": "activity-report-app",
"name": "learning-dashboard",
"homepage": "/learning-dashboard/",
"version": "0.1.0",
"private": true,

View File

@ -8,7 +8,7 @@ case class DurationProps(duration: Int, createdTime: Long, createdDate: String,
userActivitySignResponseDelayInMinutes: Int,
endWhenNoModerator: Boolean, endWhenNoModeratorDelayInMinutes: Int)
case class MeetingProp(name: String, extId: String, intId: String, isBreakout: Boolean, activityReportTracking: Boolean)
case class MeetingProp(name: String, extId: String, intId: String, isBreakout: Boolean, learningDashboardEnabled: Boolean)
case class BreakoutProps(
parentId: String,
@ -20,7 +20,7 @@ case class BreakoutProps(
privateChatEnabled: Boolean
)
case class PasswordProp(moderatorPass: String, viewerPass: String, activityReportAccessToken: String)
case class PasswordProp(moderatorPass: String, viewerPass: String, learningDashboardAccessToken: String)
case class RecordProp(record: Boolean, autoStartRecording: Boolean, allowStartStopRecording: Boolean, keepEvents: Boolean)

View File

@ -30,7 +30,6 @@ object UserRespondedToPollRecordMsg { val NAME = "UserRespondedToPollRecordMsg"
case class UserRespondedToPollRecordMsg(header: BbbClientMsgHeader, body: UserRespondedToPollRecordMsgBody) extends BbbCoreMsg
case class UserRespondedToPollRecordMsgBody(pollId: String, answerId: Int, answer: String, isSecret: Boolean)
object RespondToPollReqMsg { val NAME = "RespondToPollReqMsg" }
case class RespondToPollReqMsg(header: BbbClientMsgHeader, body: RespondToPollReqMsgBody) extends StandardMsg
case class RespondToPollReqMsgBody(requesterId: String, pollId: String, questionId: Int, answerId: Int)

View File

@ -227,9 +227,9 @@ case class DeletedRecordingSysMsgBody(recordId: String)
/**
* Sent from akka-apps to bbb-web to inform a summary of the meeting activities
*/
object ActivityReportEvtMsg { val NAME = "ActivityReportEvtMsg" }
case class ActivityReportEvtMsg(
object LearningDashboardEvtMsg { val NAME = "LearningDashboardEvtMsg" }
case class LearningDashboardEvtMsg(
header: BbbCoreHeaderWithMeetingId,
body: ActivityReportEvtMsgBody
body: LearningDashboardEvtMsgBody
) extends BbbCoreMsg
case class ActivityReportEvtMsgBody(activityJson: String)
case class LearningDashboardEvtMsgBody(activityJson: String)

View File

@ -25,7 +25,7 @@ trait TestFixtures {
val webcamsOnlyForModerator = false
val moderatorPassword = "modpass"
val viewerPassword = "viewpass"
val activityReportAccessToken = "arToken"
val learningDashboardAccessToken = "ldToken"
val createTime = System.currentTimeMillis
val createDate = "Oct 26, 2015"
val isBreakout = false
@ -48,7 +48,7 @@ trait TestFixtures {
val durationProps = DurationProps(duration = durationInMinutes, createdTime = createTime, createdDate = createDate,
meetingExpireIfNoUserJoinedInMinutes = meetingExpireIfNoUserJoinedInMinutes, meetingExpireWhenLastUserLeftInMinutes = meetingExpireWhenLastUserLeftInMinutes,
userInactivityInspectTimerInMinutes = userInactivityInspectTimerInMinutes, userInactivityThresholdInMinutes = userInactivityInspectTimerInMinutes, userActivitySignResponseDelayInMinutes = userActivitySignResponseDelayInMinutes)
val password = PasswordProp(moderatorPass = moderatorPassword, viewerPass = viewerPassword, activityReportAccessToken = activityReportAccessToken)
val password = PasswordProp(moderatorPass = moderatorPassword, viewerPass = viewerPassword, learningDashboardAccessToken = learningDashboardAccessToken)
val recordProp = RecordProp(record = record, autoStartRecording = autoStartRecording,
allowStartStopRecording = allowStartStopRecording, keepEvents = keepEvents)
val welcomeProp = WelcomeProp(welcomeMsgTemplate = welcomeMsgTemplate, welcomeMsg = welcomeMsg,

View File

@ -55,7 +55,7 @@ public class ApiParams {
public static final String SEQUENCE = "sequence";
public static final String VOICE_BRIDGE = "voiceBridge";
public static final String WEB_VOICE = "webVoice";
public static final String ACTIVITY_REPORT_TRACKING = "activityReportTracking";
public static final String ACTIVITY_REPORT_TRACKING = "learningDashboardEnabled";
public static final String WEBCAMS_ONLY_FOR_MODERATOR = "webcamsOnlyForModerator";
public static final String WELCOME = "welcome";
public static final String HTML5_INSTANCE_ID = "html5InstanceId";

View File

@ -24,19 +24,19 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
public class ActivityService {
private static Logger log = LoggerFactory.getLogger(ActivityService.class);
private static String activitiesDir = "/var/bigbluebutton/activity-report";
public class LearningDashboardService {
private static Logger log = LoggerFactory.getLogger(LearningDashboardService.class);
private static String learningDashboardFilesDir = "/var/bigbluebutton/learning-dashboard";
public void writeActivityJsonFile(String meetingId, String activityReportAccessToken, String activityJson) {
public void writeActivityJsonFile(String meetingId, String learningDashboardAccessToken, String activityJson) {
try {
if(activityReportAccessToken.length() == 0) {
log.error("ActivityReport AccessToken not found. JSON file will not be saved for meeting {}.",meetingId);
if(learningDashboardAccessToken.length() == 0) {
log.error("LearningDashboard AccessToken not found. JSON file will not be saved for meeting {}.",meetingId);
return;
}
File baseDir = new File(this.getDestinationBaseDirectoryName(meetingId,activityReportAccessToken));
File baseDir = new File(this.getDestinationBaseDirectoryName(meetingId,learningDashboardAccessToken));
if (!baseDir.exists()) baseDir.mkdirs();
File jsonFile = new File(baseDir.getAbsolutePath() + File.separatorChar + "activity_report.json");
@ -46,17 +46,17 @@ public class ActivityService {
fileOutput.close();
log.info("Activities JSON ({}) updated for meeting {}.",jsonFile.getAbsolutePath(),meetingId);
log.info("Learning Dashboard ({}) updated for meeting {}.",jsonFile.getAbsolutePath(),meetingId);
} catch(Exception e) {
System.out.println(e);
}
}
private String getDestinationBaseDirectoryName(String meetingId, String activityReportAccessToken) {
return activitiesDir + File.separatorChar + meetingId + File.separatorChar + activityReportAccessToken;
private String getDestinationBaseDirectoryName(String meetingId, String learningDashboardAccessToken) {
return learningDashboardFilesDir + File.separatorChar + meetingId + File.separatorChar + learningDashboardAccessToken;
}
public void setActivitiesDir(String dir) {
activitiesDir = dir;
public void setLearningDashboardFilesDir(String dir) {
learningDashboardFilesDir = dir;
}
}

View File

@ -90,7 +90,7 @@ public class MeetingService implements MessageListener {
private final ConcurrentMap<String, UserSession> sessions;
private RecordingService recordingService;
private ActivityService activityService;
private LearningDashboardService learningDashboardService;
private WaitingGuestCleanupTimerTask waitingGuestCleaner;
private UserCleanupTimerTask userCleaner;
private EnteredUserCleanupTimerTask enteredUserCleaner;
@ -405,7 +405,7 @@ public class MeetingService implements MessageListener {
gw.createMeeting(m.getInternalId(), m.getExternalId(), m.getParentMeetingId(), m.getName(), m.isRecord(),
m.getTelVoice(), m.getDuration(), m.getAutoStartRecording(), m.getAllowStartStopRecording(),
m.getWebcamsOnlyForModerator(), m.getModeratorPassword(), m.getViewerPassword(),
m.getActivityReportTracking(), m.getActivityReportAccessToken(), m.getCreateTime(),
m.getlearningDashboardEnabled(), m.getLearningDashboardAccessToken(), m.getCreateTime(),
formatPrettyDate(m.getCreateTime()), m.isBreakout(), m.getSequence(), m.isFreeJoin(), m.getMetadata(),
m.getGuestPolicy(), m.getAuthenticatedGuest(), m.getMeetingLayout(), m.getWelcomeMessageTemplate(), m.getWelcomeMessage(), m.getModeratorOnlyMessage(),
m.getDialNumber(), m.getMaxUsers(),
@ -953,10 +953,10 @@ public class MeetingService implements MessageListener {
}
}
public void processActivityReport(ActivityReport message) {
public void processLearningDashboard(LearningDashboard message) {
//Get all data from Json instead of getMeeting(message.meetingId), to process messages received even after meeting ended
JsonObject activityJsonObject = new Gson().fromJson(message.activityJson, JsonObject.class).getAsJsonObject();
String activityReportAccessToken = activityJsonObject.get("activityReportAccessToken").getAsString();
String learningDashboardAccessToken = activityJsonObject.get("learningDashboardAccessToken").getAsString();
Map<String, Object> logData = new HashMap<String, Object>();
logData.put("meetingId", activityJsonObject.get("intId").getAsString());
@ -970,7 +970,7 @@ public class MeetingService implements MessageListener {
log.info(" --analytics-- data={}", logStr);
activityService.writeActivityJsonFile(message.meetingId, activityReportAccessToken, message.activityJson);
learningDashboardService.writeActivityJsonFile(message.meetingId, learningDashboardAccessToken, message.activityJson);
}
@Override
@ -1129,8 +1129,8 @@ public class MeetingService implements MessageListener {
processMakePresentationDownloadableMsg((MakePresentationDownloadableMsg) message);
} else if (message instanceof UpdateRecordingStatus) {
processUpdateRecordingStatus((UpdateRecordingStatus) message);
} else if (message instanceof ActivityReport) {
processActivityReport((ActivityReport) message);
} else if (message instanceof LearningDashboard) {
processLearningDashboard((LearningDashboard) message);
}
}
};
@ -1216,8 +1216,8 @@ public class MeetingService implements MessageListener {
recordingService = s;
}
public void setActivityService(ActivityService s) {
activityService = s;
public void setLearningDashboardService(LearningDashboardService s) {
learningDashboardService = s;
}
public void setRedisStorageService(RedisStorageService mess) {

View File

@ -82,7 +82,7 @@ public class ParamsProcessorUtil {
private boolean disableRecordingDefault;
private boolean autoStartRecording;
private boolean allowStartStopRecording;
private boolean activityReportTracking;
private boolean learningDashboardEnabled;
private boolean webcamsOnlyForModerator;
private boolean defaultMuteOnStart = false;
private boolean defaultAllowModsToUnmuteUsers = false;
@ -416,22 +416,22 @@ public class ParamsProcessorUtil {
}
}
boolean activityReportTrack = activityReportTracking;
boolean learningDashboardEnabled = learningDashboardEnabled;
if (!StringUtils.isEmpty(params.get(ApiParams.ACTIVITY_REPORT_TRACKING))) {
try {
activityReportTrack = Boolean.parseBoolean(params
learningDashboardEnabled = Boolean.parseBoolean(params
.get(ApiParams.ACTIVITY_REPORT_TRACKING));
} catch (Exception ex) {
log.warn(
"Invalid param [activityReportTracking] for meeting=[{}]",
"Invalid param [learningDashboardEnabled] for meeting=[{}]",
internalMeetingId);
}
}
//Generate token to access Activity Report
String activityReportAccessToken = "";
if(activityReportTrack == true) {
activityReportAccessToken = RandomStringUtils.randomAlphanumeric(12).toLowerCase();
String learningDashboardAccessToken = "";
if(learningDashboardEnabled == true) {
learningDashboardAccessToken = RandomStringUtils.randomAlphanumeric(12).toLowerCase();
}
boolean webcamsOnlyForMod = webcamsOnlyForModerator;
@ -531,8 +531,8 @@ public class ParamsProcessorUtil {
.withLockSettingsParams(lockSettingsParams)
.withAllowDuplicateExtUserid(defaultAllowDuplicateExtUserid)
.withHTML5InstanceId(html5InstanceId)
.withActivityReportTracking(activityReportTrack)
.withActivityReportAccessToken(activityReportAccessToken)
.withlearningDashboardEnabled(learningDashboardEnabled)
.withLearningDashboardAccessToken(learningDashboardAccessToken)
.build();
if (!StringUtils.isEmpty(params.get(ApiParams.MODERATOR_ONLY_MESSAGE))) {
@ -922,8 +922,8 @@ public class ParamsProcessorUtil {
this.allowStartStopRecording = allowStartStopRecording;
}
public void setActivityReportTracking(boolean activityReportTracking) {
this.activityReportTracking = activityReportTracking;
public void setlearningDashboardEnabled(boolean learningDashboardEnabled) {
this.learningDashboardEnabled = learningDashboardEnabled;
}
public void setWebcamsOnlyForModerator(boolean webcamsOnlyForModerator) {

View File

@ -51,8 +51,8 @@ public class Meeting {
private String webVoice;
private String moderatorPass;
private String viewerPass;
private Boolean activityReportTracking;
private String activityReportAccessToken;
private Boolean learningDashboardEnabled;
private String learningDashboardAccessToken;
private String welcomeMsgTemplate;
private String welcomeMsg;
private String modOnlyMessage = "";
@ -110,8 +110,8 @@ public class Meeting {
intMeetingId = builder.internalId;
viewerPass = builder.viewerPass;
moderatorPass = builder.moderatorPass;
activityReportTracking = builder.activityReportTracking;
activityReportAccessToken = builder.activityReportAccessToken;
learningDashboardEnabled = builder.learningDashboardEnabled;
learningDashboardAccessToken = builder.learningDashboardAccessToken;
maxUsers = builder.maxUsers;
bannerColor = builder.bannerColor;
bannerText = builder.bannerText;
@ -330,12 +330,12 @@ public class Meeting {
return viewerPass;
}
public Boolean getActivityReportTracking() {
return activityReportTracking;
public Boolean getlearningDashboardEnabled() {
return learningDashboardEnabled;
}
public String getActivityReportAccessToken() {
return activityReportAccessToken;
public String getLearningDashboardAccessToken() {
return learningDashboardAccessToken;
}
public String getWelcomeMessageTemplate() {
@ -726,8 +726,8 @@ public class Meeting {
private boolean webcamsOnlyForModerator;
private String moderatorPass;
private String viewerPass;
private Boolean activityReportTracking;
private String activityReportAccessToken;
private Boolean learningDashboardEnabled;
private String learningDashboardAccessToken;
private int duration;
private String webVoice;
private String telVoice;
@ -818,13 +818,13 @@ public class Meeting {
return this;
}
public Builder withActivityReportTracking(Boolean e) {
this.activityReportTracking = e;
public Builder withlearningDashboardEnabled(Boolean e) {
this.learningDashboardEnabled = e;
return this;
}
public Builder withActivityReportAccessToken(String t) {
this.activityReportAccessToken = t;
public Builder withLearningDashboardAccessToken(String t) {
this.learningDashboardAccessToken = t;
return this;
}

View File

@ -17,8 +17,8 @@ public class CreateMeetingMessage {
public boolean webcamsOnlyForModerator;
public final String moderatorPass;
public final String viewerPass;
public final String activityReportAccessToken;
public final Boolean activityReportTracking;
public final String learningDashboardAccessToken;
public final Boolean learningDashboardEnabled;
public final Long createTime;
public final String createDate;
public final Map<String, String> metadata;
@ -27,7 +27,7 @@ public class CreateMeetingMessage {
String voiceBridge, Long duration,
Boolean autoStartRecording, Boolean allowStartStopRecording,
Boolean webcamsOnlyForModerator, String moderatorPass,
String viewerPass, String activityReportAccessToken, Boolean activityReportTracking,
String viewerPass, String learningDashboardAccessToken, Boolean learningDashboardEnabled,
Long createTime, String createDate, Map<String, String> metadata) {
this.id = id;
this.externalId = externalId;
@ -40,8 +40,8 @@ public class CreateMeetingMessage {
this.webcamsOnlyForModerator = webcamsOnlyForModerator;
this.moderatorPass = moderatorPass;
this.viewerPass = viewerPass;
this.activityReportAccessToken = activityReportAccessToken;
this.activityReportTracking = activityReportTracking;
this.learningDashboardAccessToken = learningDashboardAccessToken;
this.learningDashboardEnabled = learningDashboardEnabled;
this.createTime = createTime;
this.createDate = createDate;
this.metadata = metadata;

View File

@ -1,10 +1,10 @@
package org.bigbluebutton.api.messaging.messages;
public class ActivityReport implements IMessage {
public class LearningDashboard implements IMessage {
public final String meetingId;
public final String activityJson;
public ActivityReport(String meetingId, String activityJson) {
public LearningDashboard(String meetingId, String activityJson) {
this.meetingId = meetingId;
this.activityJson = activityJson;
}

View File

@ -17,7 +17,7 @@ public interface IBbbWebApiGWApp {
String parentMeetingID, String meetingName, Boolean recorded,
String voiceBridge, Integer duration, Boolean autoStartRecording,
Boolean allowStartStopRecording, Boolean webcamsOnlyForModerator,
String moderatorPass, String viewerPass, Boolean activityReportTracking, String activityReportAccessToken, Long createTime,
String moderatorPass, String viewerPass, Boolean learningDashboardEnabled, String learningDashboardAccessToken, Long createTime,
String createDate, Boolean isBreakout, Integer sequence, Boolean freejoin, Map<String, String> metadata,
String guestPolicy, Boolean authenticatedGuest, String meetingLayout, String welcomeMsgTemplate, String welcomeMsg, String modOnlyMessage,
String dialNumber, Integer maxUsers,

View File

@ -124,7 +124,7 @@ class BbbWebApiGWApp(
recorded: java.lang.Boolean, voiceBridge: String, duration: java.lang.Integer,
autoStartRecording: java.lang.Boolean,
allowStartStopRecording: java.lang.Boolean, webcamsOnlyForModerator: java.lang.Boolean,
moderatorPass: String, viewerPass: String, activityReportTracking: java.lang.Boolean, activityReportAccessToken: String,
moderatorPass: String, viewerPass: String, learningDashboardEnabled: java.lang.Boolean, learningDashboardAccessToken: String,
createTime: java.lang.Long, createDate: String, isBreakout: java.lang.Boolean,
sequence: java.lang.Integer,
freeJoin: java.lang.Boolean,
@ -146,7 +146,7 @@ class BbbWebApiGWApp(
html5InstanceId: java.lang.Integer): Unit = {
val meetingProp = MeetingProp(name = meetingName, extId = extMeetingId, intId = meetingId,
isBreakout = isBreakout.booleanValue(), activityReportTracking = activityReportTracking.booleanValue())
isBreakout = isBreakout.booleanValue(), learningDashboardEnabled = learningDashboardEnabled.booleanValue())
val durationProps = DurationProps(
duration = duration.intValue(),
createdTime = createTime.longValue(), createDate,
@ -159,7 +159,7 @@ class BbbWebApiGWApp(
endWhenNoModeratorDelayInMinutes.intValue()
)
val password = PasswordProp(moderatorPass = moderatorPass, viewerPass = viewerPass, activityReportAccessToken = activityReportAccessToken)
val password = PasswordProp(moderatorPass = moderatorPass, viewerPass = viewerPass, learningDashboardAccessToken = learningDashboardAccessToken)
val recordProp = RecordProp(record = recorded.booleanValue(), autoStartRecording = autoStartRecording.booleanValue(),
allowStartStopRecording = allowStartStopRecording.booleanValue(), keepEvents = keepEvents.booleanValue())

View File

@ -106,8 +106,8 @@ class ReceivedJsonMsgHdlrActor(val msgFromAkkaAppsEventBus: MsgFromAkkaAppsEvent
route[SetPresentationDownloadableEvtMsg](envelope, jsonNode)
case RecordingStatusChangedEvtMsg.NAME =>
route[RecordingStatusChangedEvtMsg](envelope, jsonNode)
case ActivityReportEvtMsg.NAME =>
route[ActivityReportEvtMsg](envelope, jsonNode)
case LearningDashboardEvtMsg.NAME =>
route[LearningDashboardEvtMsg](envelope, jsonNode)
case _ =>
//log.debug("************ Cannot route envelope name " + envelope.name)

View File

@ -45,7 +45,7 @@ class OldMeetingMsgHdlrActor(val olgMsgGW: OldMessageReceivedGW)
case m: RecordingChapterBreakSysMsg => handleRecordingChapterBreakSysMsg(m)
case m: SetPresentationDownloadableEvtMsg => handleSetPresentationDownloadableEvtMsg(m)
case m: RecordingStatusChangedEvtMsg => handleRecordingStatusChangedEvtMsg(m)
case m: ActivityReportEvtMsg => handleActivityReportEvtMsg(m)
case m: LearningDashboardEvtMsg => handleLearningDashboardEvtMsg(m)
case _ => log.error("***** Cannot handle " + msg.envelope.name)
}
}
@ -181,8 +181,8 @@ class OldMeetingMsgHdlrActor(val olgMsgGW: OldMessageReceivedGW)
olgMsgGW.handle(m)
}
def handleActivityReportEvtMsg(msg: ActivityReportEvtMsg): Unit = {
olgMsgGW.handle(new ActivityReport(msg.header.meetingId, msg.body.activityJson))
def handleLearningDashboardEvtMsg(msg: LearningDashboardEvtMsg): Unit = {
olgMsgGW.handle(new LearningDashboard(msg.header.meetingId, msg.body.activityJson))
}
}

View File

@ -52,7 +52,7 @@ export default function addMeeting(meeting) {
intId: String,
extId: String,
isBreakout: Boolean,
activityReportTracking: Boolean,
learningDashboardEnabled: Boolean,
name: String,
},
usersProp: {
@ -89,7 +89,7 @@ export default function addMeeting(meeting) {
password: {
viewerPass: String,
moderatorPass: String,
activityReportAccessToken: String,
learningDashboardAccessToken: String,
},
voiceProp: {
voiceConf: String,

View File

@ -20,20 +20,20 @@ const isModerator = () => {
return false;
};
const getActivityReportAccessToken = () => ((
const getLearningDashboardAccessToken = () => ((
Meetings.findOne(
{ meetingId: Auth.meetingID },
{
fields: { 'password.activityReportAccessToken': 1 },
fields: { 'password.learningDashboardAccessToken': 1 },
},
) || {}).password || {}).activityReportAccessToken || null;
) || {}).password || {}).learningDashboardAccessToken || null;
const openActivityReportUrl = () => {
window.open(`/learning-dashboard/?meeting=${Auth.meetingID}&report=${getActivityReportAccessToken()}`, '_blank');
const openLearningDashboardUrl = () => {
window.open(`/learning-dashboard/?meeting=${Auth.meetingID}&report=${getLearningDashboardAccessToken()}`, '_blank');
};
export default {
isModerator,
getActivityReportAccessToken,
openActivityReportUrl,
getLearningDashboardAccessToken,
openLearningDashboardUrl,
};

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import { Meteor } from 'meteor/meteor';
import Auth from '/imports/ui/services/auth';
import ActivityReportService from '../activity-report/service';
import LearningDashboardService from '../learning-dashboard/service';
import Button from '/imports/ui/components/button/component';
import allowRedirectToLogoutURL from './service';
import getFromUserSettings from '/imports/ui/services/users-settings';
@ -98,7 +98,7 @@ const intlMessage = defineMessages({
description: 'message for whom was kicked by inactivity',
},
open_activity_report_btn: {
id: 'app.activity-report.clickHereToOpen',
id: 'app.learning-dashboard.clickHereToOpen',
description: 'description of link to open activity report',
},
});
@ -263,14 +263,14 @@ class MeetingEnded extends PureComponent {
{!allowRedirectToLogoutURL() ? null : (
<div>
{
ActivityReportService.isModerator()
&& ActivityReportService.getActivityReportAccessToken() != null
LearningDashboardService.isModerator()
&& LearningDashboardService.getLearningDashboardAccessToken() != null
? (
<div className={styles.text}>
<Button
icon="multi_whiteboard"
color="default"
onClick={ActivityReportService.openActivityReportUrl}
onClick={LearningDashboardService.openLearningDashboardUrl}
className={styles.button}
label={intl.formatMessage(intlMessage.open_activity_report_btn)}
description={intl.formatMessage(intlMessage.open_activity_report_btn)}

View File

@ -94,12 +94,12 @@ const intlMessages = defineMessages({
id: 'app.actionsBar.actionsDropdown.createBreakoutRoomDesc',
description: 'Description of create breakout room option',
},
activityReportLabel: {
id: 'app.activity-report.label',
learningDashboardLabel: {
id: 'app.learning-dashboard.label',
description: 'Activity Report label',
},
activityReportDesc: {
id: 'app.activity-report.description',
learningDashboardDesc: {
id: 'app.learning-dashboard.description',
description: 'Activity Report description',
},
invitationItem: {
@ -146,7 +146,7 @@ class UserOptions extends PureComponent {
this.lockId = _.uniqueId('list-item-');
this.guestPolicyId = _.uniqueId('list-item-');
this.createBreakoutId = _.uniqueId('list-item-');
this.activityReportId = _.uniqueId('list-item-');
this.learningDashboardId = _.uniqueId('list-item-');
this.saveUsersNameId = _.uniqueId('list-item-');
this.captionsId = _.uniqueId('list-item-');
@ -233,8 +233,8 @@ class UserOptions extends PureComponent {
hasBreakoutRoom,
isBreakoutEnabled,
getUsersNotAssigned,
activityReportAccessToken,
openActivityReportUrl,
learningDashboardAccessToken,
openLearningDashboardUrl,
amIModerator,
users,
isMeteorConnected,
@ -305,14 +305,14 @@ class UserOptions extends PureComponent {
icon: 'download',
});
if (activityReportAccessToken != null) {
if (learningDashboardAccessToken != null) {
this.menuItems.push({
icon: "multi_whiteboard",
iconRight: "popout_window",
label: intl.formatMessage(intlMessages.activityReportLabel),
description: intl.formatMessage(intlMessages.activityReportDesc),
key: this.activityReportId,
onClick: openActivityReportUrl,
label: intl.formatMessage(intlMessages.learningDashboardLabel),
description: intl.formatMessage(intlMessages.learningDashboardDesc),
key: this.learningDashboardId,
onClick: openLearningDashboardUrl,
})
}
}

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import Auth from '/imports/ui/services/auth';
import Meetings from '/imports/api/meetings';
import ActionsBarService from '/imports/ui/components/actions-bar/service';
import ActivityReportService from '/imports/ui/components/activity-report/service';
import LearningDashboardService from '/imports/ui/components/learning-dashboard/service';
import UserListService from '/imports/ui/components/user-list/service';
import WaitingUsersService from '/imports/ui/components/waiting-users/service';
import logger from '/imports/startup/client/logger';
@ -24,7 +24,7 @@ const intlMessages = defineMessages({
},
});
const dynamicGuestPolicy = Meteor.settings.public.app.dynamicGuestPolicy;
const { dynamicGuestPolicy } = Meteor.settings.public.app;
const meetingMuteDisabledLog = () => logger.info({
logCode: 'useroptions_unmute_all',
@ -92,8 +92,8 @@ const UserOptionsContainer = withTracker((props) => {
guestPolicy: WaitingUsersService.getGuestPolicy(),
isMeteorConnected: Meteor.status().connected,
meetingName: getMeetingName(),
activityReportAccessToken: ActivityReportService.getActivityReportAccessToken(),
openActivityReportUrl: ActivityReportService.openActivityReportUrl,
learningDashboardAccessToken: LearningDashboardService.getLearningDashboardAccessToken(),
openLearningDashboardUrl: LearningDashboardService.openLearningDashboardUrl,
dynamicGuestPolicy,
};
})(UserOptions);

View File

@ -667,9 +667,9 @@
"app.connection-status.label": "Connection status",
"app.connection-status.notification": "Loss in your connection was detected",
"app.connection-status.offline": "offline",
"app.activity-report.label": "Learning Dashboard",
"app.activity-report.description": "Open dashboard with users activities",
"app.activity-report.clickHereToOpen": "Open Learning Dashboard",
"app.learning-dashboard.label": "Learning Dashboard",
"app.learning-dashboard.description": "Open dashboard with users activities",
"app.learning-dashboard.clickHereToOpen": "Open Learning Dashboard",
"app.recording.startTitle": "Start recording",
"app.recording.stopTitle": "Pause recording",
"app.recording.resumeTitle": "Resume recording",

View File

@ -245,7 +245,7 @@ allowStartStopRecording=true
# Provide moderator with a Dashboard with live summary of meeting activities in real time (available after end of meeting too)
# this is the default value, can be customized using the create API
activityReportTracking=true
learningDashboardEnabled=true
# Allow webcams streaming reception only to and from moderators
webcamsOnlyForModerator=false
@ -319,8 +319,8 @@ captionsDir=/var/bigbluebutton/captions
# The directory where the pre-built configs are stored
configDir=/var/bigbluebutton/configs
# The directory to export Json with Meeting activities
activitiesDir=/var/bigbluebutton/activity-report
# The directory to export Json with Meeting activities (used in Learning Dashboard)
learningDashboardFilesDir=/var/bigbluebutton/learning-dashboard
# If the API is enabled.
serviceEnabled = true

View File

@ -46,7 +46,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<bean id="meetingService" class="org.bigbluebutton.api.MeetingService" init-method="start" destroy-method="stop">
<property name="redisStorageService" ref="redisStorageService"/>
<property name="activityService" ref="activityService"/>
<property name="learningDashboardService" ref="learningDashboardService"/>
<property name="recordingService" ref="recordingService"/>
<property name="presDownloadService" ref="presDownloadService"/>
<property name="paramsProcessorUtil" ref="paramsProcessorUtil"/>
@ -108,8 +108,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<property name="defaultTextTrackUrl" value="${defaultTextTrackUrl}"/>
</bean>
<bean id="activityService" class="org.bigbluebutton.api.ActivityService">
<property name="activitiesDir" value="${activitiesDir}"/>
<bean id="learningDashboardService" class="org.bigbluebutton.api.LearningDashboardService">
<property name="learningDashboardFilesDir" value="${learningDashboardFilesDir}"/>
</bean>
<bean id="html5LoadBalancingService" class="org.bigbluebutton.api.HTML5LoadBalancingService" init-method="init" />
@ -151,7 +151,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<property name="disableRecordingDefault" value="${disableRecordingDefault}"/>
<property name="autoStartRecording" value="${autoStartRecording}"/>
<property name="allowStartStopRecording" value="${allowStartStopRecording}"/>
<property name="activityReportTracking" value="${activityReportTracking}"/>
<property name="learningDashboardEnabled" value="${learningDashboardEnabled}"/>
<property name="webcamsOnlyForModerator" value="${webcamsOnlyForModerator}"/>
<property name="useDefaultAvatar" value="${useDefaultAvatar}"/>
<property name="defaultAvatarURL" value="${defaultAvatarURL}"/>