Skip to content

Commit

Permalink
Meetings tool code base contributed to Master on the 12th of May 2022
Browse files Browse the repository at this point in the history
  • Loading branch information
Miguel Pellicer committed May 12, 2022
0 parents commit e2c0e6a
Show file tree
Hide file tree
Showing 96 changed files with 8,144 additions and 0 deletions.
27 changes: 27 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.idea
.project
.classpath
.settings
*.iml
*.swp
*.orig
*.rej
target
sakai-demo.tar.gz
sakai-demo.zip
.directory
.sass-cache
.metadata
.DS_Store
*nb-configuration.xml
velocity.log
node_modules
webcomponents/tool/src/main/frontend/dist
rubrics/tool/src/main/frontend
bower_components
rebel.xml
rsf/sakai-rsf-web/templates/overlays
rsf/sakai-rsf-web/test/overlays
.mvn/wrapper/maven-wrapper.jar
.checkstyle
assets
93 changes: 93 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# SAKAI - Online Meetings tool

This is a tool for Sakai dedicated to the creation, management and use of virtual meeting rooms based on different online video conferencing providers. Instructors can schedule new meetings for their students on a site or group basis. Students can see a list of their scheduled meetings for each site and access them directly by clicking on them. Only members of the organization can access these meetings, unless the organizer allows guest access.

#### Features

- Instructors can schedule new meetings for their students.
- Instructors can control access to meetings by site or group.
- Students can access a list of their scheduled meetings.
- Search for scheduled meetings by name.
- Meetings can be added as events to the site calendar.
- Instructors can notify all meeting participants by e-mail.
- Only members of the organization can access Microsoft Teams meetings.
- Only the organizer can invite external users to access the Microsoft Teams meeting.
- Simple, fast and responsive interface.

## Current supported providers
- Microsoft Teams

## Prerequisites
You need:
- A Sakai 21.x instance or higher. (For Sakai 20.x instances you might want to checkout the 20.x branch if it's available, for newer versions Master is fine).
- For Microsoft Teams integration:
- A Microsoft Azure Active Directory application.
- Azure Active Directory users must have the same email in Sakai to be identified as members of the organization.

## Microsoft Teams
### Azure AD configuration
You must create a new application in the _App Registrations_ section of the Azure Active Directory portal by clicking on the _New Registration_ button.

![App registrations](docs/images/1.png "App registration")

You can enter a name and select the supported account types. The _Single tenant_ option is marked by default.

![Registering new app](docs/images/2.png "Registering new app")

To grant **Meetings tool** access to your registered Azure application, you will need a **client secret**. To obtain this, you can access the _Certificates & secrets_ section within the configuration page of your registered Azure application.

![Client secret](docs/images/3.png "Client secret")

Once the app is created, you need to configure the permissions for your registered Azure App in the _API Permissions_ section. To add a new permission you must click _Add a permission_, then select _Microsoft Graph_ and _Application Permissions_.

![Permissions](docs/images/4.png "Permissions")

The permissions to enable are defined in the following table:

```sh
Directory.Read.All
Directory.ReadWrite.All
Group.Create
Group.Read.All
Group.ReadWrite.All
OnlineMeetings.ReadWrite.All
Team.Create
Teamwork.Migrate.All
User.Read
User.Read.All
```

Then you must click on the _Grant admin consent_ button for your Azure directory.

### Application access directives
In order for Sakai to manage meetings on its own, without user authentication by Microsoft, you need to set up an application user and its access policies. You can read the official Microsoft documentation on these steps:

https://docs.microsoft.com/en-us/graph/cloud-communication-online-meeting-application-access-policy

## Sakai configuration
### Microsoft Teams
You have to configure your server's Sakai properties file so that the Meetings tool can access your Azure App. These are the properties you need to configure:


| PROPERTY | VALUE | DESCRIPTION |
| ------ | ------ | ------ |
| meetings.msteams.authority | https://login.microsoftonline.com/{tenant}/ | {tenant} is the Tenant ID of your Azure Active Directory |
| meetings.msteams.clientId | {clientId} | {clientId} is the Application (Client) Id from your _App registration_ |
| meetings.msteams.secret | {secret} | This is the secret you created under _Certificates & secrets_ section |
| meetings.msteams.scope | https://graph.microsoft.com/.default | This is a fixed value |

## Future plans and Roadmap

- Improve documentation.
- Right now is a contrib project, institutions can start evaluating and using it.
- We want feedback from institutions about the tool.
- Improve Microsoft permissions for institutions using Microsoft accounts.
- Contribution to Sakai Master may happen soon depending on discussions and feedback.
- Reuse the meeting card component in other tools like Lessons or create a meeting widget.
- Support other webconference providers like Zoom or BBB depending on funding or contributions.

## Compatibility
Version 1.0+ of Meetings is compatible with Sakai 21.x and higher, Sakai 20.x compatibility is under review because of Hibernate and Spring dependencies.

## Contact
If you have any questions please contact the devs at **Entornos de Formacion S.L.** at [email protected]
60 changes: 60 additions & 0 deletions api/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?xml version="1.0"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.sakaiproject.meetings</groupId>
<artifactId>meetings</artifactId>
<version>23-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<name>meetings-api</name>
<groupId>org.sakaiproject.meetings</groupId>
<artifactId>meetings-api</artifactId>
<packaging>jar</packaging>

<properties>
<deploy.target>shared</deploy.target>
</properties>


<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.sakaiproject.kernel</groupId>
<artifactId>sakai-kernel-api</artifactId>
</dependency>
<dependency>
<groupId>org.sakaiproject.kernel</groupId>
<artifactId>sakai-kernel-private</artifactId>
</dependency>
</dependencies>

<build>
<resources>
<resource>
<directory>${basedir}/src/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.metaprops</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>${basedir}/src/java</directory>
<includes>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
</project>
23 changes: 23 additions & 0 deletions api/src/java/org/sakaiproject/meetings/api/MeetingService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.sakaiproject.meetings.api;

import java.util.List;
import java.util.Optional;

import org.sakaiproject.meetings.api.model.Meeting;

public interface MeetingService {

public Iterable<Meeting> getAllMeetings();
public List<Meeting> getAllMeetingsFromSite(String siteId);
public List<Meeting> getUserMeetings(String userId, String siteId, List <String> groupIds);
public Meeting createMeeting(Meeting meetingData);
public void updateMeeting(Meeting meetingData);
public void deleteMeetingById(String id);
public Optional<Meeting> getMeetingById(String id);
public Meeting getMeeting(String id);
public void removeSiteAndGroupAttendeesByMeetingId(String id);
public void setMeetingProperty(Meeting meeting, String property, String value);
public String getMeetingProperty(Meeting meeting, String property);
public void removeMeetingProperty(Meeting meeting, String property);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.sakaiproject.meetings.api.model;

public enum AttendeeType {
USER,
SITE,
GROUP
}
70 changes: 70 additions & 0 deletions api/src/java/org/sakaiproject/meetings/api/model/Meeting.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.sakaiproject.meetings.api.model;

import java.time.Instant;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "meetings")
@AllArgsConstructor
@Data
@NoArgsConstructor
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Meeting {

@Id
@Column(name = "meeting_id", length = 99, nullable = false)
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;

@Column(name = "meeting_title", length = 255, nullable = false)
private String title;

@Column(name = "meeting_description", length = 255)
private String description;

@Column(name = "meeting_site_id", length = 99)
private String siteId;

@Column(name = "meeting_start_date")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS]Z", timezone = "UTC")
private Instant startDate;

@Column(name = "meeting_end_date")
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS]Z", timezone = "UTC")
private Instant endDate;

@Column(name = "meeting_url", length = 255)
private String url;

@Column(name = "meeting_owner_id", length = 99)
private String ownerId;

@ManyToOne
@JoinColumn(name="meeting_provider_id")
private MeetingsProvider provider;

@OneToMany(mappedBy="meeting", cascade = CascadeType.ALL)
private List<MeetingAttendee> attendees;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.sakaiproject.meetings.api.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "meeting_attendees")
@AllArgsConstructor
@Data
@NoArgsConstructor
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class MeetingAttendee {

@Id
@Column(name = "attendee_id")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "meeting_attendee_sequence")
@SequenceGenerator(name = "meeting_attendee_sequence", sequenceName = "MEETING_ATTENDEE_S")
private Long id;

@ManyToOne
@JoinColumn(name="attendee_meeting_id")
private Meeting meeting;

@Column(name = "attendee_type")
private AttendeeType type;

@Column(name = "attendee_object_id", length = 255)
private String objectId;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.sakaiproject.meetings.api.model;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "meeting_properties")
@AllArgsConstructor
@Data
@NoArgsConstructor
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class MeetingProperty {

@Id
@Column(name = "prop_id")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "meeting_property_sequence")
@SequenceGenerator(name = "meeting_property_sequence", sequenceName = "MEETING_PROPERTY_S")
private Long id;

@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="prop_meeting_id")
private Meeting meeting;

@Column(name = "prop_name", length = 255, nullable = false)
private String name;

@Column(name = "prop_value", length = 255)
private String value;

}
Loading

0 comments on commit e2c0e6a

Please sign in to comment.