Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
maxkratz committed Oct 4, 2023
2 parents 980dcbc + b89498a commit d79878f
Show file tree
Hide file tree
Showing 47 changed files with 1,915 additions and 711 deletions.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# MoodleSync

! Attention: Until now MoodleSync is not working with Moodle 4.2. !

[![Build MoodleSync sync-app](https://github.com/maxkratz/moodle-sync-app/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/maxkratz/moodle-sync-app/actions/workflows/build.yml)
[![Test MoodleSync sync-app](https://github.com/maxkratz/moodle-sync-app/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/maxkratz/moodle-sync-app/actions/workflows/test.yml)

Expand All @@ -9,10 +11,15 @@ and managing those. An often used learning platform is Moodle. It offers lecture
to publish lecture notes, recordings and other e-learning materials amongst their students. Because
of the fact that the process to upload and manage data via the browser view of Moodle is very time
consuming, the objective of this project was to develop a desktop application used for file
synchronization between a local directory and the learning platform Moodle. Futhermore a Moodle plugin was developed.
synchronization between a local directory and the learning platform Moodle. Futhermore students can download files or a whole course with simply one click.
A Moodle plugin to add new web-service functions was developed.
For further information about installation and usage please visit the wiki of this repository.

Works with following plugin for Moodle: https://github.com/MoodleSync/sync-plugin.

## Screenshot
![Main view](https://github.com/MoodleSync/sync-app/blob/main/doc/images/mainpage.png)
## Screenshots

Student mode | Trainer mode
:-------------------------:|:-------------------------:
![Main view](https://github.com/MoodleSync/sync-app/blob/main/doc/images/StudentMode.png) | ![Main view](https://github.com/MoodleSync/sync-app/blob/main/doc/images/TrainerMode.png)

Binary file added doc/images/Folderstructure.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/Settings.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/StudentMode.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/StudentModeLabeled.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/TrainerMode.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/images/TrainerModeLabeled.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.lecturestudio.core.beans.StringProperty;

import java.util.Locale;
import java.util.Objects;

/**
* This class represents a configuration containing several settings.
Expand Down Expand Up @@ -52,10 +53,27 @@ public class MoodleSyncConfiguration extends Configuration {
private final BooleanProperty showUnknownFormats = new BooleanProperty();

//Language
private final ObjectProperty<Locale> locale = new ObjectProperty();
private final ObjectProperty<Locale> locale = new ObjectProperty<>();

//Delete file property - still in work
private final BooleanProperty executeDeletion = new BooleanProperty();
public MoodleSyncConfiguration() {
}


public MoodleSyncConfiguration (MoodleSyncConfiguration config) {
this.syncRootPath.set(config.syncRootPath.get());
this.recentCourse.set(config.recentCourse.get());
this.moodleToken.set(config.moodleToken.get());
this.recentSection.set(config.recentSection.get());
this.moodleUrl.set(config.moodleUrl.get());
this.formatsMoodle.set(config.formatsMoodle.get());
this.formatsFileserver.set(config.formatsFileserver.get());
this.ftpserver.set(config.ftpserver.get());
this.ftpuser.set(config.ftpuser.get());
this.ftppassword.set(config.ftppassword.get());
this.ftpport.set(config.ftpport.get());
this.showUnknownFormats.set(config.showUnknownFormats.get());
this.locale.set(config.locale.get());
}

public String getSyncRootPath() {
return syncRootPath.get();
Expand Down Expand Up @@ -213,16 +231,25 @@ public ObjectProperty<Locale> localeProperty() {
return this.locale;
}

public Boolean getExecuteDeletion() {
return executeDeletion.get();
}

public void setExecuteDeletion(Boolean executeDeletion) {
this.executeDeletion.set(executeDeletion);
}

public BooleanProperty executeDeletionProperty() {
return executeDeletion;
public boolean equals(MoodleSyncConfiguration o) {
return Objects.equals(this.syncRootPath.get(), o.syncRootPath.get()) && Objects.equals(this.recentCourse.get(),
o.recentCourse.get()) && Objects.equals(this.moodleToken.get(), o.moodleToken.get()) &&
Objects.equals(this.recentSection.get(), o.recentSection.get()) &&
Objects.equals(this.moodleUrl.get(), o.moodleUrl.get()) &&
Objects.equals(this.formatsMoodle.get(), o.formatsMoodle.get()) &&
Objects.equals(this.formatsFileserver.get(), o.formatsFileserver.get()) &&
Objects.equals(this.ftpserver.get(), o.ftpserver.get()) &&
Objects.equals(this.ftpuser.get(), o.ftpuser.get()) &&
Objects.equals(this.ftppassword.get(), o.ftppassword.get()) &&
Objects.equals(this.ftpport.get(), o.ftpport.get()) &&
Objects.equals(this.showUnknownFormats.get(), o.showUnknownFormats.get()) &&
Objects.equals(this.locale.get(), o.locale.get());
}

@Override
public int hashCode() {
return Objects.hash(syncRootPath, recentCourse, moodleToken, recentSection, moodleUrl, formatsMoodle,
formatsFileserver, ftpserver, ftpuser, ftppassword, ftpport, showUnknownFormats, locale);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
*/
public class Content {
private String filename;
private String fileurl;
private Long timemodified;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package moodle.sync.core.model.json;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.List;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class CoursePermissions {

private List<OptionPermissions> options;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package moodle.sync.core.model.json;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class OptionPermissions {

private String name;

private Boolean available;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package moodle.sync.core.model.json;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.List;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Permissions {

private List<CoursePermissions> courses;

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package moodle.sync.core.util;

import moodle.sync.core.web.service.MoodleDownloadService;

import java.io.File;
import java.io.InputStream;
import java.nio.file.StandardCopyOption;

public final class FileDownloadService {

public static void getFile(String url, String token, String path, String name, String lastModified) throws Exception {
MoodleDownloadService moodleDownloadService = new MoodleDownloadService(url);
try (InputStream download = moodleDownloadService.getDownload(token)) {
if (download.available() != 0) {
File targetFile = new File(path + "/" + name);
java.nio.file.Files.copy(download, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
targetFile.setLastModified(((Long.parseLong(lastModified)*1000)-1));
}
}
}

/** Usage:
* try{
* FileDownloadService.getFile("https://localhost/webservice/pluginfile" +
* ".php/21/mod_resource/content/0/TheModel.pdf?forcedownload=1", token,
* "C:/Users" +
* "/danie/OneDrive/Desktop", "Testdatei.pdf", lastmodifiedtime);
* } catch (Exception e){
* logException(e, "Sync failed");
* }
* }
*/

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public void run() {
watchServices.add(watchService);
boolean poll = true;
while (poll) {
Thread.sleep(500);
poll = pollEvents(watchService);
}
} catch (IOException | InterruptedException | ClosedWatchServiceException e) {
Expand Down Expand Up @@ -80,7 +81,7 @@ protected void notifyListeners(WatchEvent.Kind<?> kind, File file) {
}
} else if (kind == ENTRY_MODIFY) {
for (FileListener listener : listeners) {
listener.onModified(event);
listener.onCreated(event);
}
} else if (kind == ENTRY_DELETE) {
for (FileListener listener : listeners) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;

import moodle.sync.core.model.json.Course;
import moodle.sync.core.model.json.JsonConfigProvider;
import moodle.sync.core.model.json.Section;
import moodle.sync.core.model.json.SiteInfo;
import moodle.sync.core.model.json.*;
import moodle.sync.core.web.filter.LoggingFilter;
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.annotation.RegisterProviders;
Expand Down Expand Up @@ -216,5 +213,9 @@ void setSection(@QueryParam("moodlewsrestformat") String moodlewsrestformat, @Qu
@QueryParam("wsfunction") String function, @QueryParam("courseid") int courseid,
@QueryParam("sectionname") String sectionname, @QueryParam("sectionnum") int sectionnum);


@GET
@Path("")
Permissions getPermissions(@QueryParam("moodlewsrestformat") String moodlewsrestformat,
@QueryParam("wstoken") String token, @QueryParam("wsfunction") String function, @QueryParam(
"courseids[0]") int courseid);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package moodle.sync.core.web.client;

import moodle.sync.core.model.json.JsonConfigProvider;
import moodle.sync.core.web.filter.LoggingFilter;
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
import org.eclipse.microprofile.rest.client.annotation.RegisterProviders;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.io.InputStream;


@Path("")
@RegisterProviders({@RegisterProvider(LoggingFilter.class), @RegisterProvider(JsonConfigProvider.class),})
public interface MoodleDownloadClient {

@GET
@Path("")
@Produces(MediaType.MEDIA_TYPE_WILDCARD)
InputStream getDownload(@QueryParam("token") String token);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package moodle.sync.core.web.service;

import moodle.sync.core.web.client.MoodleDownloadClient;
import org.eclipse.microprofile.rest.client.RestClientBuilder;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.InputStream;
import java.net.URI;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class MoodleDownloadService {

private MoodleDownloadClient moodleClient;

/**
* Creates a new MoodleService.
*
* @param apiUrl The url of the Moodle-platform.
*/
public MoodleDownloadService(String apiUrl) {
setApiUrl(apiUrl);
}

/**
* Method which instantiates a MoodleClient.
*
* @param apiUrl The url of the Moodle-platform.
*/
public void setApiUrl(String apiUrl) {
//Parameter checks.
if (apiUrl == null || apiUrl.isEmpty() || apiUrl.isBlank()) {
return;
}
RestClientBuilder builder = RestClientBuilder.newBuilder();
builder.baseUri(URI.create(apiUrl));
//Usage of https.
if (apiUrl.startsWith("https")) {
builder.sslContext(createSSLContext());
builder.hostnameVerifier((hostname, sslSession) -> hostname.equalsIgnoreCase(sslSession.getPeerHost()));
}
//MoodleClient is instantiated by classes of the MicroProfile Rest Client.
moodleClient = builder.build(MoodleDownloadClient.class);
}

public InputStream getDownload(String token) {
return moodleClient.getDownload(token);
}

private static SSLContext createSSLContext() {
SSLContext sslContext;

try {
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}

public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}

public X509Certificate[] getAcceptedIssuers() {
return null;
}
};

sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, new TrustManager[]{tm}, null);
} catch (Exception e) {
throw new RuntimeException(e);
}

return sslContext;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.List;

import moodle.sync.core.model.json.Course;
import moodle.sync.core.model.json.Permissions;
import moodle.sync.core.model.json.Section;
import moodle.sync.core.model.json.SiteInfo;
import moodle.sync.core.web.client.MoodleClient;
Expand Down Expand Up @@ -63,7 +64,7 @@ public void setApiUrl(String apiUrl) {
* @param token The Moodle-token.
* @return the userid as an int.
*/
public int getUserId(String token) {
public int getUserId(String token) throws Exception {
SiteInfo info = moodleClient.getSiteInfo("json", token, "core_webservice_get_site_info");
return info.getUserid();
}
Expand Down Expand Up @@ -225,6 +226,10 @@ public void setSection(String token, int courseid, String sectionname, int secti
moodleClient.setSection("json", token, "local_course_add_new_section", courseid, sectionname, sectionnum);
}

public Boolean getPermissions(String token, int courseid) throws Exception {
return moodleClient.getPermissions("json", token, "core_course_get_user_administration_options", courseid).getCourses().get(0).getOptions().get(0).getAvailable();
}


/**
* Method user for generating a needed SSLContext for https-communication
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package moodle.sync.event;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import moodle.sync.javafx.model.SyncTableElement;

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class DownloadItemEvent {

private SyncTableElement element;

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public void updateItem(Boolean item, boolean empty) {
setGraphic(null);
} else if (getTableRow() != null) {
if (getTableRow().getItem() != null && (!getTableRow().getItem().isSelectable() ||
getTableRow().getItem().getAction() == MoodleAction.UploadSection)) {
getTableRow().getItem().getAction() == MoodleAction.UploadSection || getTableRow().getItem().getAction() == MoodleAction.ExistingFile
|| getTableRow().getItem().getAction() == MoodleAction.NotLocalFile || getTableRow().getItem().getAction() == MoodleAction.FolderSynchronize)) {
checkBox.setAlignment(Pos.CENTER);
setDisable(false);
setGraphic(null);
Expand Down

0 comments on commit d79878f

Please sign in to comment.