mirror of
https://github.com/maxkratz/moodle-sync-app.git
synced 2024-05-20 12:00:28 +00:00
initial commit
This commit is contained in:
parent
c89449765b
commit
c6aceb6010
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Eclipse Core
|
||||
**/.classpath
|
||||
**/.project
|
||||
**/.settings/
|
||||
|
||||
# IntelliJ IDEA
|
||||
**/*.iml
|
||||
**/.idea
|
||||
|
||||
# VS Code
|
||||
.vscode
|
||||
|
||||
# Maven Build
|
||||
**/target
|
||||
|
||||
# Node
|
||||
node_modules/
|
10
README.md
Normal file
10
README.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# MoodleSync
|
||||
Due to the digitalization in education and the resulting increasing amount of e-learning resources,
|
||||
many universities and other educational institutions use browser based learning platforms for sharing
|
||||
and managing those. An often used learning platform is Moodle. It offers lecturers the possibility
|
||||
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.
|
||||
|
||||
Works with following plugin for Moodle: https://github.com/lectureStudio/moodle-local_sync_service.
|
113
moodle-sync-cli/pom.xml
Normal file
113
moodle-sync-cli/pom.xml
Normal file
|
@ -0,0 +1,113 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>moodle.sync</groupId>
|
||||
<artifactId>moodle-sync</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>moodle-sync-cli</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<jackson.version>2.13.3</jackson.version>
|
||||
<log4j.version>2.17.2</log4j.version>
|
||||
<resteasy.version>4.7.5.Final</resteasy.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<classifier>${envClassifier}</classifier>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>moodle.sync.cli.CommandLineInterface</mainClass>
|
||||
<addClasspath>true</addClasspath>
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
<classpathLayoutType>custom</classpathLayoutType>
|
||||
<customClasspathLayout>${artifact.artifactId}.${artifact.extension}</customClasspathLayout>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<Class-Path>lib/javafx-controls-win.jar lib/javafx-graphics-win.jar lib/javafx-base-win.jar lib/javafx-fxml-win.jar lib/javafx-swing-win.jar lib/javafx-media-win.jar</Class-Path>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<targetPath>resources</targetPath>
|
||||
<filtering>false</filtering>
|
||||
<excludes>
|
||||
<exclude>log4j2.xml</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>log4j2.xml</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>moodle.sync</groupId>
|
||||
<artifactId>moodle-sync-core</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<version>1.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.lecturestudio.core</groupId>
|
||||
<artifactId>lect-core</artifactId>
|
||||
<version>4.0.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-client-microprofile</artifactId>
|
||||
<version>${resteasy.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-json-binding-provider</artifactId>
|
||||
<version>${resteasy.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-multipart-provider</artifactId>
|
||||
<version>${resteasy.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.sun.activation</groupId>
|
||||
<artifactId>jakarta.activation</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,224 @@
|
|||
package moodle.sync.cli;
|
||||
|
||||
import static java.util.Objects.nonNull;
|
||||
|
||||
import moodle.sync.cli.inject.ApplicationModule;
|
||||
import moodle.sync.core.model.json.Course;
|
||||
import moodle.sync.core.model.json.MoodleUpload;
|
||||
import moodle.sync.core.model.json.Section;
|
||||
import moodle.sync.core.web.service.MoodleService;
|
||||
import moodle.sync.core.web.service.MoodleUploadTemp;
|
||||
|
||||
import org.apache.commons.cli.*;
|
||||
|
||||
import org.lecturestudio.core.app.dictionary.Dictionary;
|
||||
import org.lecturestudio.core.inject.GuiceInjector;
|
||||
import org.lecturestudio.core.inject.Injector;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class CommandLineInterface {
|
||||
|
||||
private final MoodleService moodleService;
|
||||
|
||||
private final Dictionary dictionary;
|
||||
|
||||
private String url;
|
||||
|
||||
private String token;
|
||||
|
||||
private int userId;
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
Injector injector = new GuiceInjector(new ApplicationModule());
|
||||
|
||||
CommandLineInterface cliTool = injector.getInstance(CommandLineInterface.class);
|
||||
cliTool.execute(args);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public CommandLineInterface(MoodleService moodleService, Dictionary dictionary) {
|
||||
this.moodleService = moodleService;
|
||||
this.dictionary = dictionary;
|
||||
}
|
||||
|
||||
private void execute(String[] args) {
|
||||
Options options = generateDefaultOptions();
|
||||
|
||||
try {
|
||||
CommandLine cmd = parseCommandLine(options, args);
|
||||
|
||||
// Check for required parameters
|
||||
checkParameters(cmd);
|
||||
|
||||
// Login to moodle
|
||||
String loginConfigPath = cmd.getOptionValue("l");
|
||||
login(loginConfigPath);
|
||||
|
||||
// Get the dedicated course
|
||||
String courseId = cmd.getOptionValue("course");
|
||||
Course course = getCourse(courseId);
|
||||
|
||||
// Get the dedicated section
|
||||
String sectionId = cmd.getOptionValue("section");
|
||||
Section section = getSection(course, sectionId);
|
||||
|
||||
// Get file path to upload
|
||||
Path file = getFilePath(cmd.getOptionValue("path"));
|
||||
|
||||
// Execute file upload
|
||||
upload(course, section, file);
|
||||
} catch (Exception e) {
|
||||
if (nonNull(e.getMessage())) {
|
||||
printError(e.getMessage());
|
||||
}
|
||||
printHelp();
|
||||
}
|
||||
}
|
||||
|
||||
private Options generateDefaultOptions() {
|
||||
Options options = new Options();
|
||||
options.addOption("c", "course", true, dictionary.get("cli.option.course"));
|
||||
options.addOption("s", "section", true, dictionary.get("cli.option.section"));
|
||||
options.addOption("p", "path", true, dictionary.get("cli.option.path"));
|
||||
options.addOption("l", "login", true, dictionary.get("cli.option.login"));
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
private CommandLine parseCommandLine(Options options, String[] args) {
|
||||
CommandLineParser parser = new DefaultParser();
|
||||
|
||||
try {
|
||||
return parser.parse(options, args);
|
||||
} catch (Exception e) {
|
||||
// If parameter specified but no value given
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkParameters(CommandLine cmd) {
|
||||
if (!cmd.hasOption("c") || !cmd.hasOption("s") || !cmd.hasOption("p")) {
|
||||
throw new IllegalArgumentException("cli.args.incomplete");
|
||||
}
|
||||
}
|
||||
|
||||
private Course getCourse(String courseId) {
|
||||
Course course;
|
||||
|
||||
try {
|
||||
List<Course> courses = moodleService.getEnrolledCourses(token, userId);
|
||||
|
||||
course = courses.stream()
|
||||
.filter(c -> c.getId().toString().equals(courseId))
|
||||
.findFirst().orElse(null);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("cli.moodle.external.service.error");
|
||||
}
|
||||
|
||||
if (course == null) {
|
||||
throw new IllegalArgumentException("cli.moodle.course.invalid");
|
||||
}
|
||||
|
||||
return course;
|
||||
}
|
||||
|
||||
private Section getSection(Course course, String sectionId) {
|
||||
try {
|
||||
return moodleService.getCourseContentSection(token, course.getId(),
|
||||
Integer.parseInt(sectionId)).get(0);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("cli.moodle.section.invalid");
|
||||
}
|
||||
}
|
||||
|
||||
private Path getFilePath(String filePath) {
|
||||
Path path = Path.of(filePath);
|
||||
|
||||
if (!Files.exists(path)) {
|
||||
throw new IllegalArgumentException("cli.path.invalid");
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private void login(String configPath) throws IllegalAccessException {
|
||||
if (nonNull(configPath) && !configPath.isEmpty() && !configPath.isBlank()) {
|
||||
// Parse config file to get credentials
|
||||
Properties moodleProps = new Properties();
|
||||
|
||||
try {
|
||||
moodleProps.load(new FileInputStream(Path.of(configPath).toFile()));
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException("cli.login.properties.invalid");
|
||||
}
|
||||
|
||||
url = moodleProps.getProperty("url");
|
||||
token = moodleProps.getProperty("token");
|
||||
} else {
|
||||
// Read credentials from environment variables
|
||||
url = System.getenv("MOODLE_SYNC_URL");
|
||||
token = System.getenv("MOODLE_SYNC_TOKEN");
|
||||
}
|
||||
|
||||
if (url == null || token == null) {
|
||||
throw new IllegalArgumentException("cli.moodle.login.invalid");
|
||||
}
|
||||
|
||||
moodleService.setApiUrl(url);
|
||||
|
||||
// Check if user exists
|
||||
try {
|
||||
userId = moodleService.getUserId(token);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalAccessException("cli.moodle.connection.failed");
|
||||
}
|
||||
}
|
||||
|
||||
private void upload(Course course, Section section, Path file) throws IOException {
|
||||
MoodleUploadTemp uploader = new MoodleUploadTemp();
|
||||
MoodleUpload upload;
|
||||
|
||||
String fileName = file.getFileName().toString();
|
||||
|
||||
try {
|
||||
upload = uploader.upload(fileName, file.toString(), url, token);
|
||||
|
||||
moodleService.setResource(token, course.getId(), section.getSection(),
|
||||
upload.getItemid(), null, true, fileName, -1);
|
||||
} catch (Exception e) {
|
||||
throw new IOException("cli.moodle.upload.error", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void printHelp() {
|
||||
String prefix = dictionary.get("cli.usage");
|
||||
String header = dictionary.get("cli.help.header");
|
||||
String footer = dictionary.get("cli.help.footer");
|
||||
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
PrintWriter writer = new PrintWriter(new PrintStream(System.out, true,
|
||||
StandardCharsets.UTF_16));
|
||||
|
||||
formatter.setSyntaxPrefix(prefix);
|
||||
formatter.printHelp(writer, 80, "moodle-sync-cli", header,
|
||||
generateDefaultOptions(), 3, 5, footer, true);
|
||||
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
private void printError(String error) {
|
||||
System.err.println(dictionary.contains(error) ? dictionary.get(error) : error);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package moodle.sync.cli.inject;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
|
||||
import moodle.sync.core.web.service.MoodleService;
|
||||
|
||||
import org.lecturestudio.core.app.LocaleProvider;
|
||||
import org.lecturestudio.core.app.dictionary.Dictionary;
|
||||
import org.lecturestudio.core.beans.StringProperty;
|
||||
import org.lecturestudio.core.util.AggregateBundle;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class ApplicationModule extends AbstractModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ResourceBundle createResourceBundle() throws Exception {
|
||||
LocaleProvider localeProvider = new LocaleProvider();
|
||||
Locale locale = localeProvider.getBestSupported(Locale.getDefault());
|
||||
|
||||
return new AggregateBundle(locale, "resources.i18n.core", "resources.i18n.dict", "resources.i18n.cli");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
AggregateBundle createAggregateBundle(ResourceBundle resourceBundle) {
|
||||
return (AggregateBundle) resourceBundle;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
Dictionary provideDictionary(ResourceBundle resourceBundle) {
|
||||
return new Dictionary() {
|
||||
|
||||
@Override
|
||||
public String get(String key) throws NullPointerException {
|
||||
return resourceBundle.getString(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String key) {
|
||||
return resourceBundle.containsKey(key);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
MoodleService createMoodleService(){
|
||||
return new MoodleService(new StringProperty("http://localhost/"));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package moodle.sync.cli.log;
|
||||
|
||||
import org.apache.logging.log4j.core.config.Order;
|
||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
import org.lecturestudio.core.app.AppDataLocator;
|
||||
import org.lecturestudio.core.log.Log4jXMLConfigurationFactory;
|
||||
import org.lecturestudio.core.model.VersionInfo;
|
||||
|
||||
@Plugin(name = "Log4jConfigurationFactory", category = "ConfigurationFactory")
|
||||
@Order(10)
|
||||
public class Log4jConfigurationFactory extends Log4jXMLConfigurationFactory {
|
||||
|
||||
public Log4jConfigurationFactory() {
|
||||
AppDataLocator dataLocator = new AppDataLocator("MoodleSyncCli");
|
||||
|
||||
System.setProperty("logAppVersion", VersionInfo.getAppVersion());
|
||||
System.setProperty("logFilePath", dataLocator.getAppDataPath());
|
||||
}
|
||||
|
||||
}
|
17
moodle-sync-cli/src/main/resources/i18n/cli_de_DE.properties
Normal file
17
moodle-sync-cli/src/main/resources/i18n/cli_de_DE.properties
Normal file
|
@ -0,0 +1,17 @@
|
|||
cli.usage = Verwendung:
|
||||
cli.args.incomplete = \u00Dcbergebene Parameter nicht vollst\u00E4ndig
|
||||
cli.moodle.login.invalid= URl und/oder Zugriffstoken nicht angegeben
|
||||
cli.moodle.connection.failed = Verbindungsaufbau zur Moodle-Plattform fehlgeschlagen
|
||||
cli.moodle.external.service.error = Nutzung des Externen Services fehlgeschlagen. Bitte Berechtigungen \u00FCberpr\u00FCfen
|
||||
cli.moodle.course.invalid = Kurs nicht gefunden, bitte id \u00FCberpr\u00FCfen
|
||||
cli.moodle.section.invalid = Sektion nicht gefunden, bitte Sektions-Nummer \u00FCberpr\u00FCfen
|
||||
cli.moodle.upload.error = Hochladen und ver\u00F6ffentlichten der Datei fehlgeschlagen
|
||||
cli.path.invalid = Dateipfad zur hochzuladenden Datei ung\u00FCltig
|
||||
cli.option.course = ID des Kurses (pflicht)
|
||||
cli.option.section = ID der Sektion (pflicht)
|
||||
cli.option.path = Pfad zur hochzuladenden Datei (pflicht)
|
||||
cli.option.login = Pfad zur Konfigurationsdatei mit Login-Daten
|
||||
cli.login.properties.missing = Konfigurationsdatei nicht gefunden
|
||||
cli.login.properties.invalid = Konfigurationsdatei fehlerbehaftet
|
||||
cli.help.header = Folgende Parameter sind zu \u00FCbergeben
|
||||
cli.help.footer = \nWenn Sie einen Beitrag leisten oder ein Problem melden m\u00F6chten, besuchen Sie GitHub: https://github.com/lectureStudio/MoodleSync
|
17
moodle-sync-cli/src/main/resources/i18n/cli_en_US.properties
Normal file
17
moodle-sync-cli/src/main/resources/i18n/cli_en_US.properties
Normal file
|
@ -0,0 +1,17 @@
|
|||
cli.usage = Usage:
|
||||
cli.args.incomplete = Params not complete
|
||||
cli.moodle.login.invalid = URL and/or access token are missing
|
||||
cli.moodle.connection.failed = Connecting to moodle failed
|
||||
cli.moodle.external.service.error = Using the external service failed, please check your permissions
|
||||
cli.moodle.course.invalid = Course not found, please check id
|
||||
cli.moodle.section.invalid = Section not found, please check section id
|
||||
cli.moodle.upload.error = Error uploading and publishing the file
|
||||
cli.path.invalid = Path to file you want to upload is invalid
|
||||
cli.option.course = ID of the course (mandatory)
|
||||
cli.option.section = ID of the section (mandatory)
|
||||
cli.option.path = Path to the file to upload (mandatory)
|
||||
cli.option.login = Path to configuration file with login information
|
||||
cli.login.properties.missing = Configuration file not found
|
||||
cli.login.properties.invalid = Configuration file is faulty
|
||||
cli.help.header = The following parameters are required:
|
||||
cli.help.footer = \nIf you would like to contribute or report an issue, go to GitHub: https://github.com/lectureStudio/MoodleSync
|
42
moodle-sync-cli/src/main/resources/log4j2.xml
Normal file
42
moodle-sync-cli/src/main/resources/log4j2.xml
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration>
|
||||
|
||||
<Properties>
|
||||
<Property name="filename">moodle-sync</Property>
|
||||
</Properties>
|
||||
|
||||
<Appenders>
|
||||
<Console name="STDOUT" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d %-5p %c{1}:%L - %m%n" />
|
||||
</Console>
|
||||
<RollingFile
|
||||
name="FILE"
|
||||
fileName="${sys:logFilePath}/${filename}-${sys:logAppVersion}.log"
|
||||
filePattern="${sys:logFilePath}/${filename}-${sys:logAppVersion}-%d{MM-dd-yyyy}-%i.log"
|
||||
immediateFlush="true">
|
||||
<PatternLayout pattern="%d %-5p %c{1}:%L - %m%n" />
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
|
||||
<SizeBasedTriggeringPolicy size="10 MB"/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy>
|
||||
<Delete basePath="${sys:logFilePath}" maxDepth="1">
|
||||
<IfFileName glob="${filename}-*.log"/>
|
||||
<IfAccumulatedFileCount exceeds="3"/>
|
||||
</Delete>
|
||||
</DefaultRolloverStrategy>
|
||||
</RollingFile>
|
||||
</Appenders>
|
||||
|
||||
<Loggers>
|
||||
<Logger name="moodle.sync.core.web.json" level="debug" />
|
||||
<Logger name="org.lecturestudio" level="error" />
|
||||
<Logger name="org.jboss" level="error" />
|
||||
|
||||
<Root level="info">
|
||||
<AppenderRef ref="STDOUT" />
|
||||
<AppenderRef ref="FILE" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
|
||||
</Configuration>
|
148
moodle-sync-core/pom.xml
Normal file
148
moodle-sync-core/pom.xml
Normal file
|
@ -0,0 +1,148 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>2.13.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpmime</artifactId>
|
||||
<version>4.5.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.json.bind</groupId>
|
||||
<artifactId>jakarta.json.bind-api</artifactId>
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.spec.javax.ws.rs</groupId>
|
||||
<artifactId>jboss-jaxrs-api_2.1_spec</artifactId>
|
||||
<version>2.0.1.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>jakarta.json</artifactId>
|
||||
<version>1.1.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>2.19.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.19.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.lecturestudio.core</groupId>
|
||||
<artifactId>lect-core</artifactId>
|
||||
<version>4.0.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
<version>5.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.24</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.10.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.microprofile.rest.client</groupId>
|
||||
<artifactId>microprofile-rest-client-api</artifactId>
|
||||
<version>2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-client-microprofile</artifactId>
|
||||
<version>${resteasy.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-json-binding-provider</artifactId>
|
||||
<version>${resteasy.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-multipart-provider</artifactId>
|
||||
<version>${resteasy.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.sun.activation</groupId>
|
||||
<artifactId>jakarta.activation</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<parent>
|
||||
<groupId>moodle.sync</groupId>
|
||||
<artifactId>moodle-sync</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>moodle-sync-core</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<javafx.version>18.0.1</javafx.version>
|
||||
<jackson.version>2.13.3</jackson.version>
|
||||
<log4j.version>2.17.2</log4j.version>
|
||||
<resteasy.version>4.7.5.Final</resteasy.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<targetPath>resources</targetPath>
|
||||
<filtering>false</filtering>
|
||||
<excludes>
|
||||
<exclude>log4j2.xml</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>log4j2.xml</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,24 @@
|
|||
package moodle.sync.core.config;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* This Class offers the possibility to generate a default configuration.
|
||||
*/
|
||||
public class DefaultConfiguration extends MoodleSyncConfiguration {
|
||||
|
||||
public DefaultConfiguration() {
|
||||
setApplicationName("MoodleSync");
|
||||
setLocale(Locale.getDefault());
|
||||
setCheckNewVersion(true);
|
||||
setUIControlSize(9);
|
||||
setStartMaximized(false);
|
||||
setAdvancedUIMode(false);
|
||||
setSyncRootPath(System.getProperty("user.dir"));
|
||||
setFormatsFileserver("");
|
||||
setFormatsMoodle("pdf,png,pptx,docx");
|
||||
setPortFileserver("21");
|
||||
setMoodleUrl("https://localhost");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
package moodle.sync.core.config;
|
||||
|
||||
|
||||
import moodle.sync.core.model.json.Course;
|
||||
import org.lecturestudio.core.beans.BooleanProperty;
|
||||
import moodle.sync.core.model.json.Section;
|
||||
import org.lecturestudio.core.app.configuration.Configuration;
|
||||
import org.lecturestudio.core.beans.ObjectProperty;
|
||||
import org.lecturestudio.core.beans.StringProperty;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* This class represents a configuration containing several settings.
|
||||
*/
|
||||
public class MoodleSyncConfiguration extends Configuration {
|
||||
|
||||
//The path where the synchronized files are stored at.
|
||||
private final StringProperty syncRootPath = new StringProperty();
|
||||
|
||||
//The previously selected Course.
|
||||
private final ObjectProperty<Course> recentCourse = new ObjectProperty<>();
|
||||
|
||||
//The users Moodle-token.
|
||||
private final StringProperty moodleToken = new StringProperty();
|
||||
|
||||
//The previously selected Section.
|
||||
private final ObjectProperty<Section> recentSection = new ObjectProperty<>();
|
||||
|
||||
//The Url of the Moodle-Platform.
|
||||
private final StringProperty moodleUrl = new StringProperty();
|
||||
|
||||
//If a file belongs to this format, it should be synchronized with the Moodle-plattform.
|
||||
private final StringProperty formatsMoodle = new StringProperty();
|
||||
|
||||
//If a file belongs to this format, it should be synchronized with the fileserver.
|
||||
private final StringProperty formatsFileserver = new StringProperty();
|
||||
|
||||
//The Url of the fileserver.
|
||||
private final StringProperty ftpserver = new StringProperty();
|
||||
|
||||
//The users fileserver-username.
|
||||
private final StringProperty ftpuser = new StringProperty();
|
||||
|
||||
//The users fileserver-password.
|
||||
private final StringProperty ftppassword = new StringProperty();
|
||||
|
||||
//The choosen port for the fileserver-communication.
|
||||
private final StringProperty ftpport = new StringProperty();
|
||||
|
||||
//Whether files of unknown fileformat should be displayed.
|
||||
private final BooleanProperty showUnknownFormats = new BooleanProperty();
|
||||
|
||||
//Language
|
||||
private final ObjectProperty<Locale> locale = new ObjectProperty();
|
||||
|
||||
//Delete file property - still in work
|
||||
private final BooleanProperty executeDeletion = new BooleanProperty();
|
||||
|
||||
public String getSyncRootPath() {
|
||||
return syncRootPath.get();
|
||||
}
|
||||
|
||||
public void setSyncRootPath(String path) {
|
||||
this.syncRootPath.set(path);
|
||||
}
|
||||
|
||||
public StringProperty syncRootPathProperty() {
|
||||
return syncRootPath;
|
||||
}
|
||||
|
||||
public Course getRecentCourse() {
|
||||
return recentCourse.get();
|
||||
}
|
||||
|
||||
public void setRecentCourse(Course course) {
|
||||
this.recentCourse.set(course);
|
||||
}
|
||||
|
||||
public ObjectProperty<Course> recentCourseProperty() {
|
||||
return recentCourse;
|
||||
}
|
||||
|
||||
public String getMoodleToken() {
|
||||
return moodleToken.get();
|
||||
}
|
||||
|
||||
public void setMoodleToken(String token) {
|
||||
this.moodleToken.set(token);
|
||||
}
|
||||
|
||||
public StringProperty moodleTokenProperty() {
|
||||
return moodleToken;
|
||||
}
|
||||
|
||||
public Section getRecentSection() {
|
||||
return recentSection.get();
|
||||
}
|
||||
|
||||
public void setRecentSection(Section section) {
|
||||
this.recentSection.set(section);
|
||||
}
|
||||
|
||||
public ObjectProperty<Section> recentSectionProperty() {
|
||||
return recentSection;
|
||||
}
|
||||
|
||||
public String getMoodleUrl() {
|
||||
return moodleUrl.get();
|
||||
}
|
||||
|
||||
public void setMoodleUrl(String url) {
|
||||
this.moodleUrl.set(url);
|
||||
}
|
||||
|
||||
public StringProperty moodleUrlProperty() {
|
||||
return moodleUrl;
|
||||
}
|
||||
|
||||
public String getFormatsMoodle() {
|
||||
return formatsMoodle.get();
|
||||
}
|
||||
|
||||
public void setFormatsMoodle(String formats) {
|
||||
this.formatsMoodle.set(formats);
|
||||
}
|
||||
|
||||
public StringProperty formatsMoodleProperty() {
|
||||
return formatsMoodle;
|
||||
}
|
||||
|
||||
public String getFormatsFileserver() {
|
||||
return formatsFileserver.get();
|
||||
}
|
||||
|
||||
public void setFormatsFileserver(String formats) {
|
||||
this.formatsFileserver.set(formats);
|
||||
}
|
||||
|
||||
public StringProperty formatsFileserverProperty() {
|
||||
return formatsFileserver;
|
||||
}
|
||||
|
||||
public String getFileserver() {
|
||||
return ftpserver.get();
|
||||
}
|
||||
|
||||
public void setFileserver(String fileserver) {
|
||||
this.ftpserver.set(fileserver);
|
||||
}
|
||||
|
||||
public StringProperty FileserverProperty() {
|
||||
return ftpserver;
|
||||
}
|
||||
|
||||
public String getUserFileserver() {
|
||||
return ftpuser.get();
|
||||
}
|
||||
|
||||
public void setUserFileserver(String user) {
|
||||
this.ftpuser.set(user);
|
||||
}
|
||||
|
||||
public StringProperty userFileserverProperty() {
|
||||
return ftpuser;
|
||||
}
|
||||
|
||||
public String getPasswordFileserver() {
|
||||
return ftppassword.get();
|
||||
}
|
||||
|
||||
public void setPasswordFileserver(String formats) {
|
||||
this.ftppassword.set(formats);
|
||||
}
|
||||
|
||||
public StringProperty passwordFileserverProperty() {
|
||||
return ftppassword;
|
||||
}
|
||||
|
||||
public String getPortFileserver() {
|
||||
return ftpport.get();
|
||||
}
|
||||
|
||||
public void setPortFileserver(String port) {
|
||||
this.ftpport.set(port);
|
||||
}
|
||||
|
||||
public StringProperty portFileserverProperty() {
|
||||
return ftpport;
|
||||
}
|
||||
|
||||
public Boolean getShowUnknownFormats() {
|
||||
return showUnknownFormats.get();
|
||||
}
|
||||
|
||||
public void setShowUnknownFormats(Boolean unknownFormats) {
|
||||
this.showUnknownFormats.set(unknownFormats);
|
||||
}
|
||||
|
||||
public BooleanProperty showUnknownFormatsProperty() {
|
||||
return showUnknownFormats;
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return (Locale)this.locale.get();
|
||||
}
|
||||
|
||||
public void setLocale(Locale locale) {
|
||||
this.locale.set(locale);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package moodle.sync.core.context;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.lecturestudio.core.app.AppDataLocator;
|
||||
import org.lecturestudio.core.app.ApplicationContext;
|
||||
import org.lecturestudio.core.app.configuration.Configuration;
|
||||
import org.lecturestudio.core.app.configuration.ConfigurationService;
|
||||
import org.lecturestudio.core.app.configuration.JsonConfigurationService;
|
||||
import org.lecturestudio.core.app.dictionary.Dictionary;
|
||||
import org.lecturestudio.core.bus.EventBus;
|
||||
|
||||
public class MoodleSyncContext extends ApplicationContext {
|
||||
|
||||
private final File configFile;
|
||||
|
||||
|
||||
@Inject
|
||||
public MoodleSyncContext(AppDataLocator dataLocator, File configFile,
|
||||
Configuration config, Dictionary dict, EventBus eventBus,
|
||||
EventBus audioBus) {
|
||||
super(dataLocator, config, dict, eventBus, audioBus);
|
||||
|
||||
this.configFile = configFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveConfiguration() throws Exception {
|
||||
ConfigurationService<Configuration> configService = new JsonConfigurationService<>();
|
||||
configService.save(configFile, getConfiguration());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package moodle.sync.core.fileserver;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface declaring all needed methods for fileserver support.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public interface FileServerClient {
|
||||
|
||||
//Retrieve list of FileServerFiles from dedicated directory
|
||||
List<FileServerFile> getFiles(String pathname) throws Exception;
|
||||
|
||||
// String uploadFile(syncTableElement item, String pathname);
|
||||
|
||||
//Disconnect from fileserver
|
||||
void disconnect();
|
||||
|
||||
//Connect to fileserver
|
||||
void connect();
|
||||
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
package moodle.sync.core.fileserver;
|
||||
|
||||
import moodle.sync.core.config.MoodleSyncConfiguration;
|
||||
|
||||
import org.apache.commons.net.PrintCommandListener;
|
||||
import org.apache.commons.net.ftp.FTP;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.apache.commons.net.ftp.FTPReply;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class implementing the FileServerClient-interface using the ftp-protocol.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class FileServerClientFTP implements FileServerClient {
|
||||
|
||||
//Used FTPClient for communication.
|
||||
private final FTPClient ftpClient;
|
||||
|
||||
//Configuration providing information about url etc.
|
||||
private final MoodleSyncConfiguration config;
|
||||
|
||||
|
||||
public FileServerClientFTP(MoodleSyncConfiguration config) {
|
||||
ftpClient = new FTPClient();
|
||||
ftpClient.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out)));
|
||||
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the connection with a fileserver.
|
||||
*/
|
||||
@Override
|
||||
public void connect() {
|
||||
try {
|
||||
ftpClient.connect(config.getFileserver(), Integer.parseInt(config.getPortFileserver()));
|
||||
int reply = ftpClient.getReplyCode();
|
||||
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||
ftpClient.disconnect();
|
||||
throw new IOException("Exception in connecting to FTP Server");
|
||||
}
|
||||
ftpClient.login(config.getUserFileserver(), config.getPasswordFileserver());
|
||||
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates the connection with a fileserver.
|
||||
*/
|
||||
@Override
|
||||
public void disconnect() {
|
||||
try {
|
||||
ftpClient.disconnect();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to gather information about uploaded files.
|
||||
*
|
||||
* @param pathname Path to the directory at the ftpserver.
|
||||
* @return a list containing elements of FileServerFile.
|
||||
*/
|
||||
@Override
|
||||
public List<FileServerFile> getFiles(String pathname) throws Exception {
|
||||
List<FileServerFile> files = new ArrayList<>();
|
||||
try {
|
||||
FTPFile[] ftpFiles = ftpClient.listFiles(pathname);
|
||||
for (FTPFile item : ftpFiles) {
|
||||
files.add(new FileServerFile(item.getName(), item.getTimestamp().getTimeInMillis()));
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new Exception();
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to upload a file to a ftpserver.
|
||||
*
|
||||
* @param item UploadElement, containing the local path to the file.
|
||||
* @param pathname Dedicated directory at the ftpserver.
|
||||
* @return the url of the uploaded file.
|
||||
*/
|
||||
// @Override
|
||||
// public String uploadFile(syncTableElement item, String pathname) {
|
||||
// //Evtl noch pathname einbringen
|
||||
// String url = null;
|
||||
// try {
|
||||
// InputStream file = Files.newInputStream(Paths.get(item.getExistingFile()));
|
||||
// ftpClient.storeFile("/" /*+ config.getRecentSection().getName() + "/" */ + item.getExistingFileName(), file);
|
||||
// //ToDo add functionality Url
|
||||
// url = config.getFileserver() + "/" /*+ config.getRecentSection().getName() + "/" */ + item.getExistingFileName();
|
||||
// file.close();
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// return url;
|
||||
// }
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package moodle.sync.core.fileserver;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
|
||||
/**
|
||||
* Class representing a file uploaded to a fileserver.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class FileServerFile {
|
||||
|
||||
private String filename;
|
||||
|
||||
private Long lastTimeModified;
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package moodle.sync.core.model.json;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
|
||||
/**
|
||||
* Class representing a course-modules content.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class Content {
|
||||
private String filename;
|
||||
private Long timemodified;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package moodle.sync.core.model.json;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
|
||||
/**
|
||||
* Class representing a Moodle-Course.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class Course {
|
||||
private Integer id;
|
||||
private String shortname;
|
||||
private String displayname;
|
||||
private String idnumber ;
|
||||
private Integer visible;
|
||||
private Integer enddate;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Course course = (Course) o;
|
||||
return Objects.equals(id, course.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return shortname;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package moodle.sync.core.model.json;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.bind.serializer.DeserializationContext;
|
||||
import javax.json.bind.serializer.JsonbDeserializer;
|
||||
import javax.json.stream.JsonParser;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Class which helps deserialize and debug a course
|
||||
*/
|
||||
public class CourseDeserializer implements JsonbDeserializer<Course> {
|
||||
private static final Logger LOG = LogManager.getLogger(CourseDeserializer.class);
|
||||
|
||||
@Override
|
||||
public Course deserialize(JsonParser parser,
|
||||
DeserializationContext deserializationContext, Type type) {
|
||||
JsonObject jsonObj = parser.getObject();
|
||||
|
||||
System.out.println("-----------");
|
||||
System.out.println(jsonObj.toString());
|
||||
LOG.debug(jsonObj.toString());
|
||||
|
||||
Integer id = null;
|
||||
String shortname = null;
|
||||
String displayname = null;
|
||||
String idnumber = null;
|
||||
Integer visible = null;
|
||||
Integer enddate = null;
|
||||
|
||||
if (jsonObj.containsKey("id")) {
|
||||
id = jsonObj.getInt("id");
|
||||
}
|
||||
|
||||
if (jsonObj.containsKey("shortname")) {
|
||||
shortname = jsonObj.getString("shortname");
|
||||
}
|
||||
|
||||
if (jsonObj.containsKey("displayname")) {
|
||||
displayname = jsonObj.getString("displayname");
|
||||
}
|
||||
|
||||
if (jsonObj.containsKey("idnumber")) {
|
||||
idnumber = jsonObj.getString("idnumber");
|
||||
}
|
||||
|
||||
if (jsonObj.containsKey("visible")) {
|
||||
visible = jsonObj.getInt("visible");
|
||||
}
|
||||
|
||||
if (jsonObj.containsKey("enddate")) {
|
||||
try{
|
||||
String temp = jsonObj.getJsonNumber("enddate").toString();
|
||||
System.out.println("Enddate: " + temp);
|
||||
LOG.debug("Enddate: " + temp);
|
||||
enddate = Integer.parseInt(temp);
|
||||
}catch (Throwable e){
|
||||
enddate = 0;
|
||||
System.out.println("Failed to parse enddate");
|
||||
LOG.error("Failed to parse enddate");
|
||||
}
|
||||
} else{
|
||||
enddate = 0;
|
||||
}
|
||||
|
||||
Course course = new Course();
|
||||
course.setId(id);
|
||||
course.setShortname(shortname);
|
||||
course.setDisplayname(displayname);
|
||||
course.setIdnumber(idnumber);
|
||||
course.setVisible(visible);
|
||||
course.setEnddate(enddate);
|
||||
|
||||
System.out.println("-----------");
|
||||
System.out.println(course);
|
||||
return course;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package moodle.sync.core.model.json;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.json.bind.Jsonb;
|
||||
import javax.json.bind.JsonbBuilder;
|
||||
import javax.json.bind.JsonbConfig;
|
||||
import javax.json.bind.config.PropertyVisibilityStrategy;
|
||||
import javax.ws.rs.ext.ContextResolver;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
@Provider
|
||||
public class JsonConfigProvider implements ContextResolver<Jsonb> {
|
||||
|
||||
@Override
|
||||
public Jsonb getContext(Class<?> aClass) {
|
||||
return JsonbBuilder.create(createConfig());
|
||||
}
|
||||
|
||||
public static JsonbConfig createConfig() {
|
||||
return new JsonbConfig()
|
||||
.withNullValues(true)
|
||||
.withDeserializers(new CourseDeserializer())
|
||||
.withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() {
|
||||
|
||||
@Override
|
||||
public boolean isVisible(Method method) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible(Field field) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package moodle.sync.core.model.json;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
|
||||
/**
|
||||
* Class representing a course-module.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class Module {
|
||||
private Integer id;
|
||||
private String url;
|
||||
private String name;
|
||||
private Integer instance;
|
||||
private Integer contextid;
|
||||
private Integer visible;
|
||||
private String modname;
|
||||
private String availability;
|
||||
private List<Content> contents;
|
||||
|
||||
|
||||
public Module(List<Content> contents){
|
||||
this.contents = contents;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package moodle.sync.core.model.json;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonRawValue;
|
||||
import lombok.*;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public class ModuleAvailability {
|
||||
|
||||
@JsonRawValue
|
||||
private TimeDateCondition[] c;
|
||||
@JsonRawValue
|
||||
private Boolean[] showc;
|
||||
|
||||
public TimeDateCondition getTimeDateCondition(){
|
||||
return c != null ? c[0] : null;
|
||||
}
|
||||
|
||||
public Boolean getConditionVisibility(){
|
||||
return showc != null ? showc[0] : null;
|
||||
}
|
||||
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public static class TimeDateCondition {
|
||||
String type;
|
||||
String d; //Duration
|
||||
Long t;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package moodle.sync.core.model.json;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
|
||||
/**
|
||||
* Class representing the response-object of a file upload.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class MoodleUpload {
|
||||
private String filename;
|
||||
private Long itemid;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package moodle.sync.core.model.json;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
|
||||
/**
|
||||
* Class representing a course-section.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class Section {
|
||||
private Integer id;
|
||||
private String name;
|
||||
private Integer visible;
|
||||
private String summary;
|
||||
private Integer summaryformat;
|
||||
private Integer section;
|
||||
private Integer hiddenbynumsections;
|
||||
private Boolean uservisible;
|
||||
private List<Module> modules;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Section section = (Section) o;
|
||||
return Objects.equals(id, section.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package moodle.sync.core.model.json;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
|
||||
/**
|
||||
* Class representing the necessary parameters of the http-response to the api-call: getSiteInfo.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class SiteInfo {
|
||||
private int userid;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package moodle.sync.core.util.FileWatcherService;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.EventObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* Class representing an event regarding a local directory.
|
||||
*
|
||||
*/
|
||||
public class FileEvent extends EventObject {
|
||||
|
||||
public FileEvent(File file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return (File) getSource();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package moodle.sync.core.util.FileWatcherService;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
/**
|
||||
* Interface declaring the possible changes regarding a local directory.
|
||||
*/
|
||||
public interface FileListener extends EventListener {
|
||||
|
||||
default void onCreated(FileEvent event) {
|
||||
System.out.println("Created" + event.getFile().toString());
|
||||
event.getFile();
|
||||
}
|
||||
|
||||
default void onModified(FileEvent event) {
|
||||
System.out.println("Changed");
|
||||
}
|
||||
|
||||
default void onDeleted(FileEvent event) {
|
||||
System.out.println("Deleted");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package moodle.sync.core.util.FileWatcherService;
|
||||
|
||||
import static java.nio.file.StandardWatchEventKinds.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.nio.file.ClosedWatchServiceException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.WatchEvent;
|
||||
import java.nio.file.WatchKey;
|
||||
import java.nio.file.WatchService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class which monitors a directory for changes.
|
||||
*/
|
||||
public class FileWatcher implements Runnable {
|
||||
|
||||
protected List<FileListener> listeners = new ArrayList<>();
|
||||
protected final File folder;
|
||||
protected static final List<WatchService> watchServices = new ArrayList<>();
|
||||
|
||||
|
||||
public FileWatcher(File folder) {
|
||||
this.folder = folder;
|
||||
}
|
||||
|
||||
public void watch() {
|
||||
if (folder.exists()) {
|
||||
Thread thread = new Thread(this);
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
for (int i = 0; i < watchServices.size(); i++) {
|
||||
watchServices.get(i).close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
|
||||
Path path = Paths.get(folder.getAbsolutePath());
|
||||
path.register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
|
||||
watchServices.add(watchService);
|
||||
boolean poll = true;
|
||||
while (poll) {
|
||||
poll = pollEvents(watchService);
|
||||
}
|
||||
} catch (IOException | InterruptedException | ClosedWatchServiceException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean pollEvents(WatchService watchService) throws InterruptedException {
|
||||
WatchKey key = watchService.take();
|
||||
Path path = (Path) key.watchable();
|
||||
for (WatchEvent<?> event : key.pollEvents()) {
|
||||
notifyListeners(event.kind(), path.resolve((Path) event.context()).toFile());
|
||||
}
|
||||
return key.reset();
|
||||
}
|
||||
|
||||
protected void notifyListeners(WatchEvent.Kind<?> kind, File file) {
|
||||
FileEvent event = new FileEvent(file);
|
||||
if (kind == ENTRY_CREATE) {
|
||||
for (FileListener listener : listeners) {
|
||||
listener.onCreated(event);
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
new FileWatcher(file).setListeners(listeners).watch();
|
||||
}
|
||||
} else if (kind == ENTRY_MODIFY) {
|
||||
for (FileListener listener : listeners) {
|
||||
listener.onModified(event);
|
||||
}
|
||||
} else if (kind == ENTRY_DELETE) {
|
||||
for (FileListener listener : listeners) {
|
||||
listener.onDeleted(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FileWatcher addListener(FileListener listener) {
|
||||
listeners.add(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileWatcher removeListener(FileListener listener) {
|
||||
listeners.remove(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<FileListener> getListeners() {
|
||||
return listeners;
|
||||
}
|
||||
|
||||
public FileWatcher setListeners(List<FileListener> listeners) {
|
||||
this.listeners = listeners;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static List<WatchService> getWatchServices() {
|
||||
return Collections.unmodifiableList(watchServices);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package moodle.sync.core.util;
|
||||
|
||||
/**
|
||||
* Enumeration containing several entities describing actions to do with UploadData.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public enum MoodleAction {
|
||||
|
||||
MoodleUpload("Upload file to moodle"),
|
||||
MoodleSynchronize("Update file on moodle"),
|
||||
FTPUpload("Upload file to fileserver"),
|
||||
FTPSynchronize("Update file on fileserver"),
|
||||
FTPLink("Link file to moodle"),
|
||||
NotLocalFile("File not locally saved"),
|
||||
ExistingFile("File is up to date"),
|
||||
DatatypeNotKnown("Data-Format noch specified"),
|
||||
ExistingSection("Exisiting section"),
|
||||
UploadSection("Create a new section");
|
||||
|
||||
public final String message;
|
||||
|
||||
MoodleAction(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
package moodle.sync.core.web.client;
|
||||
|
||||
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.web.filter.LoggingFilter;
|
||||
import org.eclipse.microprofile.rest.client.annotation.RegisterProvider;
|
||||
import org.eclipse.microprofile.rest.client.annotation.RegisterProviders;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface used to define https-calls executed and implemented by classes of the package MicroProfile Rest Client.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
@Path("/webservice/rest/server.php")
|
||||
@RegisterProviders({@RegisterProvider(LoggingFilter.class), @RegisterProvider(JsonConfigProvider.class),})
|
||||
public interface MoodleClient {
|
||||
|
||||
/**
|
||||
* Obtain a users subscribed Moodle-courses.
|
||||
*
|
||||
* @param moodlewsrestformat Used dataformat.
|
||||
* @param token The Moodle-token.
|
||||
* @param function The called Web Service API function.
|
||||
* @param userid The users id.
|
||||
* @return a list of moodle-courses.
|
||||
*/
|
||||
@POST
|
||||
@Path("")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<Course> getCourses(@QueryParam("moodlewsrestformat") String moodlewsrestformat,
|
||||
@QueryParam("wstoken") String token, @QueryParam("wsfunction") String function,
|
||||
@QueryParam("userid") int userid);
|
||||
|
||||
/**
|
||||
* Method used to provide a webservice info, including information about the user.
|
||||
*
|
||||
* @param moodlewsrestformat Used dataformat.
|
||||
* @param token The Moodle-token.
|
||||
* @param function The called Web Service API function.
|
||||
* @return a object containing the userid.
|
||||
*/
|
||||
@GET
|
||||
@Path("")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
SiteInfo getSiteInfo(@QueryParam("moodlewsrestformat") String moodlewsrestformat,
|
||||
@QueryParam("wstoken") String token, @QueryParam("wsfunction") String function);
|
||||
|
||||
/**
|
||||
* Receive a moodle-courses content.
|
||||
*
|
||||
* @param moodlewsrestformat Used dataformat.
|
||||
* @param token The Moodle-token.
|
||||
* @param function The called Web Service API function.
|
||||
* @param courseid The Moodle-courses id.
|
||||
* @return a list of course-sections containing course-modules.
|
||||
*/
|
||||
@GET
|
||||
@Path("")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<Section> getCourseContent(@QueryParam("moodlewsrestformat") String moodlewsrestformat,
|
||||
@QueryParam("wstoken") String token, @QueryParam("wsfunction") String function,
|
||||
@QueryParam("courseid") int courseid);
|
||||
|
||||
/**
|
||||
* Method used to move a course-module to a specific position.
|
||||
*
|
||||
* @param moodlewsrestformat Used dataformat.
|
||||
* @param token The Moodle-token.
|
||||
* @param function The called Web Service API function.
|
||||
* @param cmid The course-modules id.
|
||||
* @param sectionid The course-sections id.
|
||||
* @param beforemod The course-module id of the course-module at the supposed position. If beforemod ist
|
||||
* null, the course-module will be moved to the bottom of the course-section.
|
||||
*/
|
||||
@POST
|
||||
@Path("")
|
||||
void setMoveModule(@QueryParam("moodlewsrestformat") String moodlewsrestformat,
|
||||
@QueryParam("wstoken") String token, @QueryParam("wsfunction") String function,
|
||||
@QueryParam("cmid") int cmid, @QueryParam("sectionid") int sectionid,
|
||||
@QueryParam("beforemod") Integer beforemod);
|
||||
|
||||
/**
|
||||
* Create a course-module of type "url".
|
||||
*
|
||||
* @param moodlewsrestformat Used dataformat.
|
||||
* @param token The Moodle-token.
|
||||
* @param function The called Web Service API function.
|
||||
* @param courseid The Moodle-courses id.
|
||||
* @param sectionnum The moodle-sections number in the moodle-course.
|
||||
* @param urlname The displayname of the course-module.
|
||||
* @param url The course-modules content.
|
||||
* @param beforemod The course-module id of the course-module at the supposed position. If beforemod ist
|
||||
* null, the course-module will be moved to the bottom of the course-section.
|
||||
*/
|
||||
@POST
|
||||
@Path("")
|
||||
void setUrl(@QueryParam("moodlewsrestformat") String moodlewsrestformat, @QueryParam("wstoken") String token,
|
||||
@QueryParam("wsfunction") String function, @QueryParam("courseid") int courseid,
|
||||
@QueryParam("sectionnum") int sectionnum, @QueryParam("urlname") String urlname,
|
||||
@QueryParam("url") String url, @QueryParam("time") Long time, @QueryParam("visible") boolean visible,
|
||||
@QueryParam("beforemod") Integer beforemod);
|
||||
|
||||
/**
|
||||
* Create a course-module of type "resource".
|
||||
*
|
||||
* @param moodlewsrestformat Used dataformat.
|
||||
* @param token The Moodle-token.
|
||||
* @param function The called Web Service API function.
|
||||
* @param courseid The Moodle-courses id.
|
||||
* @param sectionnum The moodle-sections number in the moodle-course.
|
||||
* @param itemid The id of the prior uploaded file, which should be presented by the course-module.
|
||||
* @param displayname The displayname of the course-module.
|
||||
* @param beforemod The course-module id of the course-module at the supposed position. If beforemod ist
|
||||
* null, the course-module will be moved to the bottom of the course-section.
|
||||
*/
|
||||
@POST
|
||||
@Path("")
|
||||
void setResource(@QueryParam("moodlewsrestformat") String moodlewsrestformat, @QueryParam("wstoken") String token,
|
||||
@QueryParam("wsfunction") String function, @QueryParam("courseid") int courseid,
|
||||
@QueryParam("sectionnum") int sectionnum, @QueryParam("itemid") long itemid, @QueryParam("time") Long time,
|
||||
@QueryParam("visible") boolean visible, @QueryParam("displayname") String displayname,
|
||||
@QueryParam("beforemod") Integer beforemod);
|
||||
|
||||
/**
|
||||
* Create a course-module of type "folder".
|
||||
*
|
||||
* @param moodlewsrestformat Used dataformat.
|
||||
* @param token The Moodle-token.
|
||||
* @param function The called Web Service API function.
|
||||
* @param courseid The Moodle-courses id.
|
||||
* @param sectionnum The moodle-sections number in the moodle-course.
|
||||
* @param itemid The id of the prior uploaded files, which should be presented by the course-module.
|
||||
* @param displayname The displayname of the course-module.
|
||||
* @param beforemod The course-module id of the course-module at the supposed position. If beforemod ist
|
||||
* null, the course-module will be moved to the bottom of the course-section.
|
||||
*/
|
||||
@POST
|
||||
@Path("")
|
||||
void setFolder(@QueryParam("moodlewsrestformat") String moodlewsrestformat, @QueryParam("wstoken") String token,
|
||||
@QueryParam("wsfunction") String function, @QueryParam("courseid") int courseid,
|
||||
@QueryParam("sectionnum") int sectionnum, @QueryParam("itemid") long itemid,
|
||||
@QueryParam("displayname") String displayname, @QueryParam("beforemod") Integer beforemod);
|
||||
|
||||
/**
|
||||
* Obtains the course-content of a specific course-section.
|
||||
*
|
||||
* @param moodlewsrestformat Used dataformat.
|
||||
* @param token The Moodle-token.
|
||||
* @param function The called Web Service API function.
|
||||
* @param courseid The Moodle-courses id.
|
||||
* @param s Needed parameter for creating a JSON-Object.
|
||||
* @param sectionid The course-sections id.
|
||||
* @return a list containg one section.
|
||||
*/
|
||||
@POST
|
||||
@Path("")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<Section> getCourseContentSection(@QueryParam("moodlewsrestformat") String moodlewsrestformat,
|
||||
@QueryParam("wstoken") String token, @QueryParam("wsfunction") String function,
|
||||
@QueryParam("courseid") int courseid, @QueryParam("options[0][name]") String s,
|
||||
@QueryParam("options[0][value]") int sectionid);
|
||||
|
||||
/**
|
||||
* Method used to remove a course-module.
|
||||
*
|
||||
* @param moodlewsrestformat Used dataformat.
|
||||
* @param token The Moodle-token.
|
||||
* @param function The called Web Service API function.
|
||||
* @param cmid The course-modules id.
|
||||
*/
|
||||
@POST
|
||||
@Path("")
|
||||
void removeResource(@QueryParam("moodlewsrestformat") String moodlewsrestformat,
|
||||
@QueryParam("wstoken") String token, @QueryParam("wsfunction") String function,
|
||||
@QueryParam("cmids[0]") int cmid);
|
||||
|
||||
/**
|
||||
* Method used to create a new course-section.
|
||||
*
|
||||
* @param moodlewsrestformat Used dataformat.
|
||||
* @param token The Moodle-token.
|
||||
* @param function The called Web Service API function.
|
||||
* @param courseid The Moodle-courses id.
|
||||
* @param sectionname The name of the section.
|
||||
* @param sectionnum The moodle-sections number in the moodle-course.
|
||||
*/
|
||||
@GET
|
||||
@Path("")
|
||||
void setSection(@QueryParam("moodlewsrestformat") String moodlewsrestformat, @QueryParam("wstoken") String token,
|
||||
@QueryParam("wsfunction") String function, @QueryParam("courseid") int courseid,
|
||||
@QueryParam("sectionname") String sectionname, @QueryParam("sectionnum") int sectionnum);
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package moodle.sync.core.web.filter;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import javax.ws.rs.client.ClientRequestContext;
|
||||
import javax.ws.rs.client.ClientRequestFilter;
|
||||
import javax.ws.rs.client.ClientResponseContext;
|
||||
import javax.ws.rs.client.ClientResponseFilter;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
/**
|
||||
* Class which implements an Logging Filter for easy error analysis
|
||||
*/
|
||||
@Provider
|
||||
public class LoggingFilter implements ClientRequestFilter, ClientResponseFilter {
|
||||
|
||||
@Override
|
||||
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
|
||||
StringBuilder builder = new StringBuilder("----- <Response> ------------\n");
|
||||
builder.append(" Status: ").append(responseContext.getStatus()).append("\n");
|
||||
|
||||
if (MediaType.APPLICATION_JSON_TYPE.equals(responseContext.getMediaType())) {
|
||||
byte[] content = responseContext.getEntityStream().readAllBytes();
|
||||
String body = new String(content, StandardCharsets.UTF_8);
|
||||
builder.append(" Body: " + body).append("\n");
|
||||
if(body.contains("exception")){
|
||||
throw new IOException("Fehlende Berechtigung");
|
||||
}
|
||||
responseContext.setEntityStream(new ByteArrayInputStream(content));
|
||||
}
|
||||
|
||||
builder.append("----- </Response> ----------\n");
|
||||
|
||||
System.out.println(builder.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filter(ClientRequestContext requestContext) throws IOException {
|
||||
StringBuilder builder = new StringBuilder("----- <Request> ------------\n");
|
||||
builder.append(" Method: ").append(requestContext.getMethod()).append("\n");
|
||||
builder.append(" URI: ").append(requestContext.getUri()).append("\n");
|
||||
for(var header : requestContext.getHeaders().entrySet()){
|
||||
builder.append(" Header: ").append(header.getKey()).append("\n");
|
||||
builder.append(" Headervalue: ").append(header.getValue()).append("\n");
|
||||
}
|
||||
builder.append(" URI: ").append(requestContext.getEntity()).append("\n");
|
||||
builder.append("----- </Request> ----------\n");
|
||||
|
||||
System.out.println(builder.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
package moodle.sync.core.web.service;
|
||||
|
||||
import java.net.URI;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
|
||||
import moodle.sync.core.model.json.Course;
|
||||
import moodle.sync.core.model.json.Section;
|
||||
import moodle.sync.core.model.json.SiteInfo;
|
||||
import moodle.sync.core.web.client.MoodleClient;
|
||||
|
||||
import org.eclipse.microprofile.rest.client.RestClientBuilder;
|
||||
|
||||
import org.lecturestudio.core.beans.StringProperty;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
/**
|
||||
* Class which contains methods to instantiate the interface MoodleClient and to call methods defined in this interface.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class MoodleService {
|
||||
|
||||
private MoodleClient moodleClient;
|
||||
|
||||
/**
|
||||
* Creates a new MoodleService.
|
||||
*
|
||||
* @param apiUrl The url of the Moodle-platform.
|
||||
*/
|
||||
public MoodleService(StringProperty apiUrl) {
|
||||
setApiUrl(apiUrl.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(MoodleClient.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to get a users userid.
|
||||
*
|
||||
* @param token The Moodle-token.
|
||||
* @return the userid as an int.
|
||||
*/
|
||||
public int getUserId(String token) {
|
||||
SiteInfo info = moodleClient.getSiteInfo("json", token, "core_webservice_get_site_info");
|
||||
return info.getUserid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to provide a users subscribed Moodle-courses.
|
||||
*
|
||||
* @param token The Moodle-token.
|
||||
* @param userid A user's id.
|
||||
* @return list of Moodle-Courses.
|
||||
*/
|
||||
public List<Course> getEnrolledCourses(String token, int userid) {
|
||||
return moodleClient.getCourses("json", token, "core_enrol_get_users_courses", userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a specific Moodle-courses content.
|
||||
*
|
||||
* @param token The Moodle-token.
|
||||
* @param courseid A Moodle-courses id.
|
||||
* @return a list of course-sections, which contains the course-modules.
|
||||
*/
|
||||
public List<Section> getCourseContent(String token, int courseid) {
|
||||
return moodleClient.getCourseContent("json", token, "core_course_get_contents", courseid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the content of a specific Moodle-section.
|
||||
*
|
||||
* @param token The Moodle-token.
|
||||
* @param courseid A Moodle-courses id.
|
||||
* @param sectionid The course-sections id.
|
||||
* @return the course-section, which contains the course-modules.
|
||||
*/
|
||||
public List<Section> getCourseContentSection(String token, int courseid, int sectionid) {
|
||||
return moodleClient.getCourseContentSection("json", token, "core_course_get_contents", courseid, "sectionid",
|
||||
sectionid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a course-module to a specific position in a Moodle-course.
|
||||
*
|
||||
* @param token The Moodle-token.
|
||||
* @param cmid The course-modules id.
|
||||
* @param sectionid The course-sections id.
|
||||
* @param beforemod The course-module id of the course-module at the supposed position. If beforemod ist null,
|
||||
* the course-module will be moved to the bottom of the course-section.
|
||||
*/
|
||||
public void setMoveModule(String token, int cmid, int sectionid, int beforemod) {
|
||||
if (beforemod == -1) {
|
||||
moodleClient.setMoveModule("json", token, "local_course_move_module_to_specific_position", cmid,
|
||||
sectionid, null);
|
||||
}
|
||||
else {
|
||||
moodleClient.setMoveModule("json", token, "local_course_move_module_to_specific_position", cmid,
|
||||
sectionid, beforemod);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a course-module of the type "url".
|
||||
*
|
||||
* @param token The Moodle-token.
|
||||
* @param courseid A Moodle-courses id.
|
||||
* @param section The moodle-sections number in the moodle-course.
|
||||
* @param urlname The displayname of the course-module.
|
||||
* @param url The course-modules content.
|
||||
*/
|
||||
public void setUrl(String token, int courseid, int section, String urlname, String url, Long time,
|
||||
Boolean visible, int beforemod) {
|
||||
if (beforemod == -1) {
|
||||
moodleClient.setUrl("json", token, "local_course_add_new_course_module_url", courseid, section, urlname,
|
||||
url, time, visible, null);
|
||||
}
|
||||
else {
|
||||
moodleClient.setUrl("json", token, "local_course_add_new_course_module_url", courseid, section, urlname,
|
||||
url, time, visible, beforemod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a course-module of the type "resource" at a specific position inside a course-section.
|
||||
*
|
||||
* @param token The Moodle-token.
|
||||
* @param courseid A Moodle-courses id.
|
||||
* @param section The moodle-sections number in the moodle-course.
|
||||
* @param name The displayname of the course-module.
|
||||
* @param itemid The id of the prior uploaded file, which should be presented by the course-module.
|
||||
* @param beforemod The course-module id of the course-module at the supposed position. If beforemod ist null,
|
||||
* the course-module will be moved to the bottom of the course-section.
|
||||
*/
|
||||
public void setResource(String token, int courseid, int section, Long itemid, Long time, Boolean visible,
|
||||
String name, int beforemod) {
|
||||
if (beforemod == -1) {
|
||||
moodleClient.setResource("json", token, "local_course_add_new_course_module_resource", courseid, section,
|
||||
itemid, time, visible, name, null);
|
||||
}
|
||||
else {
|
||||
moodleClient.setResource("json", token, "local_course_add_new_course_module_resource", courseid, section,
|
||||
itemid, time, visible, name, beforemod);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used for deleting an exisiting course-module
|
||||
*
|
||||
* @param token The Moodle-token.
|
||||
* @param cmid The course-modules id.
|
||||
*/
|
||||
|
||||
public void removeResource(String token, int cmid) {
|
||||
moodleClient.removeResource("json", token, "core_course_delete_modules", cmid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a course-module of the type "folder".
|
||||
*
|
||||
* @param token The Moodle-token.
|
||||
* @param courseid A Moodle-courses id.
|
||||
* @param section The moodle-sections number in the moodle-course.
|
||||
* @param itemid The id of the prior uploaded files, which should be presented by the course-module.
|
||||
* @param name The displayname of the course-module.
|
||||
*/
|
||||
public void setFolder(String token, int courseid, int section, Long itemid, String name) {
|
||||
moodleClient.setFolder("json", token, "local_course_add_new_course_module_directory", courseid, section,
|
||||
itemid, name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a course-module of the type "resource" at a specific position inside a course-section.
|
||||
*
|
||||
* @param token The Moodle-token.
|
||||
* @param courseid A Moodle-courses id.
|
||||
* @param section The moodle-sections number in the moodle-course.
|
||||
* @param itemid The id of the prior uploaded files, which should be presented by the course-module.
|
||||
* @param name The displayname of the course-module.
|
||||
* @param beforemod The course-module id of the course-module at the supposed position. If beforemod ist null,
|
||||
* the course-module will be moved to the bottom of the course-section.
|
||||
*/
|
||||
public void setFolder(String token, int courseid, int section, Long itemid, String name, int beforemod) {
|
||||
moodleClient.setFolder("json", token, "local_course_add_new_course_module_directory", courseid, section,
|
||||
itemid, name, beforemod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method used to create a new course section.
|
||||
*
|
||||
* @param token The Moodle-token.
|
||||
* @param courseid A Moodle-courses id.
|
||||
* @param sectionname The name of the new section.
|
||||
* @param sectionnum The moodle-sections number in the moodle-course.
|
||||
*/
|
||||
public void setSection(String token, int courseid, String sectionname, int sectionnum) {
|
||||
moodleClient.setSection("json", token, "local_course_add_new_section", courseid, sectionname, sectionnum);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method user for generating a needed SSLContext for https-communication
|
||||
*
|
||||
* @return SSLContext
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package moodle.sync.core.web.service;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.NoArgsConstructor;
|
||||
import moodle.sync.core.model.json.MoodleUpload;
|
||||
import okhttp3.*;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
import java.io.File;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class implements an alternative way to execute http-requests with the help of the OkHttpClient and is used for file upload.
|
||||
*
|
||||
* @author Daniel Schöter
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
public class MoodleUploadTemp {
|
||||
|
||||
/**
|
||||
* With the help of this method, http-requests to upload a file to Moodle are constructed and executed.
|
||||
*
|
||||
* @param name name of the file to upload
|
||||
* @param pathname path of the file
|
||||
* @param moodleUrl url of the Moodle platform
|
||||
* @param token Moodle-token
|
||||
* @return MoodleUpload: Object which contains the filename and an itemid which is needed to identify the file
|
||||
*/
|
||||
|
||||
public MoodleUpload upload(String name, String pathname, String moodleUrl, String token) {
|
||||
try {
|
||||
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
||||
|
||||
//Usage of https
|
||||
if (moodleUrl.startsWith("https")) {
|
||||
X509TrustManager trustManager;
|
||||
SSLSocketFactory sslSocketFactory;
|
||||
try {
|
||||
trustManager = createTrustManager();
|
||||
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
|
||||
sslContext.init(null, new TrustManager[]{trustManager}, new java.security.SecureRandom());
|
||||
sslSocketFactory = sslContext.getSocketFactory();
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
builder.sslSocketFactory(sslSocketFactory, trustManager);
|
||||
builder.hostnameVerifier((hostname, sslSession) -> hostname
|
||||
.equalsIgnoreCase(sslSession.getPeerHost()));
|
||||
|
||||
}
|
||||
//Execution of the http-request
|
||||
OkHttpClient client = builder.build();
|
||||
RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||
.addFormDataPart(name, pathname,
|
||||
RequestBody.create(MediaType.parse("application/octet-stream"),
|
||||
new File(pathname)))
|
||||
.build();
|
||||
Request request = new Request.Builder()
|
||||
.url(moodleUrl + "/webservice/upload.php?token=" + token)
|
||||
.method("POST", body)
|
||||
.build();
|
||||
ResponseBody response = client.newCall(request).execute().body();
|
||||
String bodystring = response.string();
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
System.out.println("--------------------------------------" + bodystring);
|
||||
List<MoodleUpload> entity = objectMapper.readValue(bodystring, new TypeReference<List<MoodleUpload>>() {
|
||||
});
|
||||
System.out.println(entity);
|
||||
return entity.get(0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method user for generating a needed X509TrustManager for https-communication
|
||||
*
|
||||
* @return X509TrustManager
|
||||
*/
|
||||
private static X509TrustManager createTrustManager() {
|
||||
X509TrustManager tm;
|
||||
try {
|
||||
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 new X509Certificate[0];
|
||||
}
|
||||
};
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return tm;
|
||||
}
|
||||
}
|
BIN
moodle-sync-core/src/main/resources/keystore.jks
Normal file
BIN
moodle-sync-core/src/main/resources/keystore.jks
Normal file
Binary file not shown.
237
moodle-sync-fx/pom.xml
Normal file
237
moodle-sync-fx/pom.xml
Normal file
|
@ -0,0 +1,237 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>moodle.sync</groupId>
|
||||
<artifactId>moodle-sync</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>moodle-sync-fx</artifactId>
|
||||
<version>1.0.0</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<javafx.version>18.0.1</javafx.version>
|
||||
<jackson.version>2.13.3</jackson.version>
|
||||
<log4j.version>2.17.2</log4j.version>
|
||||
<resteasy.version>4.7.5.Final</resteasy.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<classifier>${envClassifier}</classifier>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>moodle.sync.javafx.SyncApplication</mainClass>
|
||||
<addClasspath>true</addClasspath>
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
<classpathLayoutType>custom</classpathLayoutType>
|
||||
<customClasspathLayout>${artifact.artifactId}.${artifact.extension}</customClasspathLayout>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<Class-Path>lib/javafx-controls-win.jar lib/javafx-graphics-win.jar lib/javafx-base-win.jar lib/javafx-fxml-win.jar lib/javafx-swing-win.jar lib/javafx-media-win.jar</Class-Path>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<targetPath>resources</targetPath>
|
||||
<filtering>false</filtering>
|
||||
<excludes>
|
||||
<exclude>log4j2.xml</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>log4j2.xml</include>
|
||||
</includes>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>moodle.sync</groupId>
|
||||
<artifactId>moodle-sync-core</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-text</artifactId>
|
||||
<version>1.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.13.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.24</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-controls</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-fxml</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-swing</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-web</artifactId>
|
||||
<version>17.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>no.tornado</groupId>
|
||||
<artifactId>tornadofx-controls</artifactId>
|
||||
<version>1.0.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.lecturestudio.core</groupId>
|
||||
<artifactId>lect-core</artifactId>
|
||||
<version>4.0.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.lecturestudio.javafx</groupId>
|
||||
<artifactId>lect-javafx</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.lecturestudio.web.api</groupId>
|
||||
<artifactId>lect-web-api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
<version>1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>31.1-jre</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
<artifactId>guice</artifactId>
|
||||
<version>5.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.controlsfx</groupId>
|
||||
<artifactId>controlsfx</artifactId>
|
||||
<version>11.1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.dlsc.gemsfx</groupId>
|
||||
<artifactId>gemsfx</artifactId>
|
||||
<version>1.47.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Used to compare app/release versions -->
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-artifact</artifactId>
|
||||
<version>3.8.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.10.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-client-microprofile</artifactId>
|
||||
<version>${resteasy.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-json-binding-provider</artifactId>
|
||||
<version>${resteasy.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-multipart-provider</artifactId>
|
||||
<version>${resteasy.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.sun.activation</groupId>
|
||||
<artifactId>jakarta.activation</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
32
moodle-sync-fx/src/main/java/moodle/sync/input/Shortcut.java
Normal file
32
moodle-sync-fx/src/main/java/moodle/sync/input/Shortcut.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
package moodle.sync.input;
|
||||
|
||||
import org.lecturestudio.core.input.KeyCode;
|
||||
import org.lecturestudio.core.input.KeyEvent;
|
||||
|
||||
public enum Shortcut {
|
||||
|
||||
APP_CLOSE(KeyCode.Q, KeyEvent.CTRL_MASK),
|
||||
|
||||
CLOSE_VIEW(KeyCode.ESCAPE);
|
||||
|
||||
|
||||
private final KeyEvent keyEvent;
|
||||
|
||||
|
||||
Shortcut(KeyCode code) {
|
||||
this.keyEvent = new KeyEvent(code.getCode());
|
||||
}
|
||||
|
||||
Shortcut(KeyCode code, int modifiers) {
|
||||
this.keyEvent = new KeyEvent(code.getCode(), modifiers);
|
||||
}
|
||||
|
||||
public KeyEvent getKeyEvent() {
|
||||
return keyEvent;
|
||||
}
|
||||
|
||||
public boolean match(KeyEvent event) {
|
||||
return keyEvent.equals(event);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package moodle.sync.javafx;
|
||||
|
||||
import org.lecturestudio.core.app.ApplicationFactory;
|
||||
import org.lecturestudio.javafx.app.JavaFxApplication;
|
||||
|
||||
public class SyncApplication extends JavaFxApplication {
|
||||
|
||||
/**
|
||||
* The entry point of the application. This method calls the static
|
||||
* {@link #launch(String[])} method to fire up the application.
|
||||
*
|
||||
* @param args the main method's arguments.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
SyncApplication.launch(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationFactory createApplicationFactory() {
|
||||
return new SyncFxFactory();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package moodle.sync.javafx;
|
||||
|
||||
import org.lecturestudio.core.app.ApplicationContext;
|
||||
import org.lecturestudio.core.app.ApplicationFactory;
|
||||
import org.lecturestudio.core.inject.GuiceInjector;
|
||||
import org.lecturestudio.core.inject.Injector;
|
||||
|
||||
import moodle.sync.javafx.inject.ApplicationModule;
|
||||
import moodle.sync.javafx.inject.ViewModule;
|
||||
import moodle.sync.presenter.MainPresenter;
|
||||
|
||||
public class SyncFxFactory implements ApplicationFactory {
|
||||
|
||||
private final Injector injector;
|
||||
|
||||
|
||||
public SyncFxFactory() {
|
||||
injector = new GuiceInjector(new ApplicationModule(), new ViewModule());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationContext getApplicationContext() {
|
||||
return injector.getInstance(ApplicationContext.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.lecturestudio.core.presenter.MainPresenter<?> getStartPresenter() {
|
||||
return injector.getInstance(MainPresenter.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.TableCell;
|
||||
import javafx.scene.control.TableColumn;
|
||||
import javafx.util.Callback;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
/**
|
||||
* Class implementing a Checkbox as the content of a TableCell.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class AvailabilityCellFactory implements Callback<TableColumn<syncTableElement, Boolean>, TableCell<syncTableElement, Boolean>> {
|
||||
@Override
|
||||
public TableCell<syncTableElement, Boolean> call(TableColumn<syncTableElement, Boolean> p) {
|
||||
AvailabilityCheckBoxCell <syncTableElement, Boolean> cell = new AvailabilityCheckBoxCell<syncTableElement, Boolean>();
|
||||
cell.setAlignment(Pos.CENTER);
|
||||
cell.setStyle("-fx-alignment: CENTER;");
|
||||
|
||||
return cell;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.control.TableColumn;
|
||||
import javafx.util.Callback;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
/**
|
||||
* Class used for determining the state of a CheckBox inside the "sync-page"-table.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class AvailabilityCellValueFactory implements Callback<TableColumn.CellDataFeatures<syncTableElement,Boolean>, ObservableValue<Boolean>> {
|
||||
@Override
|
||||
public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<syncTableElement, Boolean> param)
|
||||
{
|
||||
syncTableElement elem = param.getValue();
|
||||
//selectedProperty should be used to determine the state.
|
||||
param.getValue().visibleProperty();
|
||||
SimpleBooleanProperty booleanProp= (SimpleBooleanProperty) elem.visibleProperty();
|
||||
booleanProp.addListener(new ChangeListener<Boolean>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue,
|
||||
Boolean newValue) {
|
||||
elem.setVisible(newValue);
|
||||
}
|
||||
});
|
||||
return booleanProp;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.cell.CheckBoxTableCell;
|
||||
import javafx.util.StringConverter;
|
||||
import moodle.sync.core.util.MoodleAction;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
/**
|
||||
* Class used to display the selctedProperty-value inside a CheckBoxTreeTableCell.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class AvailabilityCheckBoxCell<U, B> extends CheckBoxTableCell<syncTableElement, Boolean> {
|
||||
|
||||
private CheckBox checkBox;
|
||||
|
||||
private boolean showLabel;
|
||||
|
||||
private ObservableValue<Boolean> booleanProperty;
|
||||
|
||||
|
||||
@Override
|
||||
public void updateItem(Boolean item, boolean empty) {
|
||||
this.checkBox = new CheckBox();
|
||||
this.checkBox.setAlignment(Pos.CENTER);
|
||||
setAlignment(Pos.CENTER);
|
||||
setGraphic(checkBox);
|
||||
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (booleanProperty instanceof BooleanProperty) {
|
||||
checkBox.selectedProperty().unbindBidirectional((BooleanProperty) booleanProperty);
|
||||
booleanProperty = null;
|
||||
}
|
||||
if (empty) {
|
||||
checkBox.setAlignment(Pos.CENTER);
|
||||
setText(null);
|
||||
setGraphic(null);
|
||||
} else if (getTableRow() != null) {
|
||||
if (getTableRow().getItem() != null && (!getTableRow().getItem().isSelectable() ||
|
||||
getTableRow().getItem().getAction() == MoodleAction.UploadSection)) {
|
||||
checkBox.setAlignment(Pos.CENTER);
|
||||
setDisable(false);
|
||||
setGraphic(null);
|
||||
}
|
||||
} else {
|
||||
StringConverter<Boolean> c = getConverter();
|
||||
|
||||
if (showLabel) {
|
||||
checkBox.setAlignment(Pos.CENTER);
|
||||
setText(c.toString(item));
|
||||
}
|
||||
setGraphic(checkBox);
|
||||
|
||||
ObservableValue<?> obsValue = getSelectedProperty();
|
||||
if (obsValue instanceof BooleanProperty) {
|
||||
booleanProperty = (ObservableValue<Boolean>) obsValue;
|
||||
checkBox.selectedProperty().bindBidirectional((BooleanProperty) booleanProperty);
|
||||
}
|
||||
|
||||
checkBox.disableProperty().bind(Bindings.not(getTableView().
|
||||
editableProperty().and(getTableColumn().editableProperty()).and(editableProperty())));
|
||||
}
|
||||
|
||||
checkBox.setAlignment(Pos.CENTER);
|
||||
setAlignment(Pos.CENTER);
|
||||
}
|
||||
|
||||
|
||||
private ObservableValue<?> getSelectedProperty() {
|
||||
return getSelectedStateCallback() != null ? getSelectedStateCallback().call(getIndex()) :
|
||||
getTableColumn().getCellObservableValue(getIndex());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.TableCell;
|
||||
import javafx.scene.control.TableColumn;
|
||||
import javafx.util.Callback;
|
||||
import moodle.sync.javafx.model.TimeDateElement;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
/**
|
||||
* Class used for determining the date and time of a upload inside the "sync-page"-table.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class AvailableDateTimeTableCellFactory implements Callback<TableColumn<syncTableElement, TimeDateElement>, TableCell<syncTableElement, TimeDateElement>> {
|
||||
@Override
|
||||
public TableCell<syncTableElement, TimeDateElement> call(TableColumn<syncTableElement, TimeDateElement> p) {
|
||||
LocalDateTimeCell<syncTableElement, TimeDateElement> cell = new LocalDateTimeCell<syncTableElement, TimeDateElement>();
|
||||
cell.setAlignment(Pos.CENTER);
|
||||
cell.setStyle("-fx-alignment: CENTER;");
|
||||
|
||||
return cell;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.TableCell;
|
||||
import javafx.scene.control.TableColumn;
|
||||
import javafx.util.Callback;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
/**
|
||||
* Class implementing a Checkbox as the content of a TableCell.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class CheckBoxTableCellFactory implements Callback<TableColumn<syncTableElement, Boolean>, TableCell<syncTableElement, Boolean>> {
|
||||
@Override
|
||||
public TableCell<syncTableElement, Boolean> call(TableColumn<syncTableElement, Boolean> p) {
|
||||
UploadCheckBoxCell<syncTableElement, Boolean> cell = new UploadCheckBoxCell<syncTableElement, Boolean>();
|
||||
cell.setAlignment(Pos.CENTER);
|
||||
cell.setStyle("-fx-alignment: CENTER;");
|
||||
|
||||
return cell;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.util.Callback;
|
||||
import moodle.sync.core.model.json.Course;
|
||||
|
||||
/**
|
||||
* Class implementing Courses as the content of a ComboBox.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
|
||||
public class CourseCellFactory implements Callback<ListView<Course>, ListCell<Course>> {
|
||||
|
||||
@Override
|
||||
public ListCell<Course> call(ListView<Course> param) {
|
||||
return new CourseListCell();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.scene.control.ListCell;
|
||||
import moodle.sync.core.model.json.Course;
|
||||
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
|
||||
/**
|
||||
* Class used to display the name of a Course inside a ListCell.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class CourseListCell extends ListCell<Course> {
|
||||
|
||||
@Override
|
||||
protected void updateItem(Course item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
setGraphic(null);
|
||||
|
||||
if (isNull(item) || empty) {
|
||||
setText("");
|
||||
} else {
|
||||
setText(item.getShortname());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.scene.control.TableRow;
|
||||
import javafx.scene.control.TableView;
|
||||
import javafx.scene.input.ClipboardContent;
|
||||
import javafx.scene.input.DataFormat;
|
||||
import javafx.scene.input.Dragboard;
|
||||
import javafx.scene.input.TransferMode;
|
||||
import javafx.util.Callback;
|
||||
|
||||
import moodle.sync.core.util.MoodleAction;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
/**
|
||||
* Class executing the drag and drop process within the sync-table
|
||||
*/
|
||||
public class DragAndDropRowFactory implements Callback<TableView<syncTableElement>, TableRow<syncTableElement>> {
|
||||
|
||||
private static final DataFormat SERIALIZED_MIME_TYPE = new DataFormat("application/x-java-serialized-object");
|
||||
|
||||
@Override
|
||||
public TableRow<syncTableElement> call(TableView<syncTableElement> tableView) {
|
||||
|
||||
|
||||
final TableRow<syncTableElement> row;
|
||||
|
||||
row = new TableRow<>();
|
||||
|
||||
row.setOnDragDetected(event -> {
|
||||
/* drag was detected, start drag-and-drop gesture*/
|
||||
if (!row.isEmpty()) {
|
||||
Integer index = row.getIndex();
|
||||
|
||||
Dragboard db = row.startDragAndDrop(TransferMode.MOVE);
|
||||
db.setDragView(row.snapshot(null, null));
|
||||
ClipboardContent cc = new ClipboardContent();
|
||||
cc.put(SERIALIZED_MIME_TYPE, index);
|
||||
db.setContent(cc);
|
||||
event.consume();
|
||||
}
|
||||
});
|
||||
|
||||
row.setOnDragOver(event -> {
|
||||
/* data is dragged over the target */
|
||||
Dragboard db = event.getDragboard();
|
||||
if (db.hasContent(SERIALIZED_MIME_TYPE)) {
|
||||
if (row.getIndex() != ((Integer) db.getContent(SERIALIZED_MIME_TYPE)).intValue()) {
|
||||
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
|
||||
event.consume();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
row.setOnDragDropped(event -> {
|
||||
Dragboard db = event.getDragboard();
|
||||
if (db.hasContent(SERIALIZED_MIME_TYPE)) {
|
||||
int draggedIndex = (Integer) db.getContent(SERIALIZED_MIME_TYPE);
|
||||
syncTableElement draggedElement = tableView.getItems().remove(draggedIndex);
|
||||
|
||||
int dropIndex;
|
||||
|
||||
|
||||
if (row.isEmpty()) {
|
||||
dropIndex = tableView.getItems().size();
|
||||
} else {
|
||||
dropIndex = row.getIndex();
|
||||
}
|
||||
|
||||
if (draggedElement.getAction() == MoodleAction.UploadSection) {
|
||||
if (tableView.getItems().get(dropIndex).getAction() == MoodleAction.ExistingSection) {
|
||||
draggedElement.setSection(tableView.getItems().get(dropIndex).getSection());
|
||||
draggedElement.setBeforemod(tableView.getItems().get(dropIndex).getCmid());
|
||||
tableView.getItems().add(dropIndex, draggedElement);
|
||||
|
||||
event.setDropCompleted(true);
|
||||
tableView.getSelectionModel().select(dropIndex);
|
||||
tableView.refresh();
|
||||
} else {
|
||||
tableView.getItems().add(draggedIndex, draggedElement);
|
||||
|
||||
event.setDropCompleted(true);
|
||||
tableView.getSelectionModel().select(draggedIndex);
|
||||
tableView.refresh();
|
||||
}
|
||||
} else if (draggedElement.getAction() == MoodleAction.ExistingSection) {
|
||||
tableView.getItems().add(draggedIndex, draggedElement);
|
||||
|
||||
event.setDropCompleted(true);
|
||||
tableView.getSelectionModel().select(draggedIndex);
|
||||
tableView.refresh();
|
||||
} else {
|
||||
if (tableView.getItems().get(dropIndex).getAction() == MoodleAction.ExistingSection) {
|
||||
draggedElement.setBeforemod(-1);
|
||||
} else {
|
||||
if (dropIndex != draggedElement.getOldPos()) {
|
||||
draggedElement.setBeforemod(tableView.getItems().get(dropIndex).getCmid());
|
||||
}
|
||||
}
|
||||
if (dropIndex != draggedElement.getOldPos() || draggedElement.getAction() == MoodleAction.MoodleUpload || draggedElement.getAction() == MoodleAction.FTPUpload) {
|
||||
draggedElement.setSectionId(tableView.getItems().get(dropIndex - 1).getSectionId());
|
||||
draggedElement.setSelectable(true);
|
||||
} else {
|
||||
draggedElement.setSelectable(false);
|
||||
}
|
||||
|
||||
tableView.getItems().add(dropIndex, draggedElement);
|
||||
|
||||
event.setDropCompleted(true);
|
||||
tableView.refresh();
|
||||
tableView.getSelectionModel().select(dropIndex);
|
||||
}
|
||||
event.consume();
|
||||
}
|
||||
});
|
||||
|
||||
return row;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.TableCell;
|
||||
import javafx.scene.control.TableColumn;
|
||||
import javafx.util.Callback;
|
||||
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
/**
|
||||
* Class implementing a text field with different styles as the content of a TableCell.
|
||||
*/
|
||||
public class HighlightSectionCellFactory implements Callback<TableColumn<syncTableElement, String>, TableCell<syncTableElement, String>> {
|
||||
@Override
|
||||
public TableCell<syncTableElement, String> call(TableColumn<syncTableElement, String> p) {
|
||||
UploadHighlightTableCell<syncTableElement, String> cell = new UploadHighlightTableCell<syncTableElement, String>();
|
||||
cell.setAlignment(Pos.CENTER);
|
||||
return cell;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import com.dlsc.gemsfx.TimePicker;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.DatePicker;
|
||||
import javafx.scene.control.TableCell;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
import moodle.sync.core.util.MoodleAction;
|
||||
|
||||
import moodle.sync.javafx.model.TimeDateElement;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
/**
|
||||
* Class used to display a time/date setter inside a TableCell.
|
||||
*/
|
||||
public class LocalDateTimeCell<S, U> extends TableCell<syncTableElement, TimeDateElement> {
|
||||
|
||||
private DatePicker datePicker;
|
||||
private TimePicker timePicker;
|
||||
|
||||
@Override
|
||||
public void updateItem(TimeDateElement item, boolean empty) {
|
||||
this.datePicker = new DatePicker();
|
||||
this.timePicker = new TimePicker();
|
||||
setAlignment(Pos.CENTER);
|
||||
datePicker.setMaxWidth(100);
|
||||
timePicker.setMaxWidth(100);
|
||||
HBox hbox = new HBox(datePicker, timePicker);
|
||||
hbox.setAlignment(Pos.CENTER);
|
||||
hbox.setSpacing(10);
|
||||
setGraphic(hbox);
|
||||
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (empty) {
|
||||
setText(null);
|
||||
setGraphic(null);
|
||||
} else if (getTableRow() != null && getTableRow().getItem() != null) {
|
||||
datePicker.valueProperty().unbindBidirectional(getTableRow().getItem().availabilityDateTimeProperty().get().LocalDateProperty());
|
||||
timePicker.timeProperty().unbindBidirectional(getTableRow().getItem().availabilityDateTimeProperty().get().LocalTimeProperty());
|
||||
if (getTableRow().getItem() != null && (!getTableRow().getItem().isSelectable() ||
|
||||
getTableRow().getItem().getAction() == MoodleAction.UploadSection)) {
|
||||
setDisable(false);
|
||||
setGraphic(null);
|
||||
}
|
||||
else {
|
||||
if (getTableRow().getItem() != null && getTableRow().getItem().isSelectable()) {
|
||||
datePicker.valueProperty().bindBidirectional(getTableRow().getItem().availabilityDateTimeProperty().get().LocalDateProperty());
|
||||
timePicker.timeProperty().bindBidirectional(getTableRow().getItem().availabilityDateTimeProperty().get().LocalTimeProperty());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.util.Callback;
|
||||
import moodle.sync.core.model.json.Section;
|
||||
|
||||
/**
|
||||
* Class implementing Sections as the content of a ComboBox.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class SectionCellFactory implements Callback<ListView<Section>, SectionListCell> {
|
||||
|
||||
@Override
|
||||
public SectionListCell call(ListView<Section> param) {
|
||||
return new SectionListCell();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.scene.control.ListCell;
|
||||
import moodle.sync.core.model.json.Section;
|
||||
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
|
||||
/**
|
||||
* Class used to display the name of a Section inside a ListCell.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class SectionListCell extends ListCell<Section> {
|
||||
|
||||
@Override
|
||||
protected void updateItem(Section item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
setGraphic(null);
|
||||
|
||||
if (isNull(item) || empty) {
|
||||
setText("");
|
||||
} else {
|
||||
setText(item.getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.TableCell;
|
||||
import javafx.scene.control.TableColumn;
|
||||
import javafx.util.Callback;
|
||||
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
/**
|
||||
* Class used to change the background color of a TableCell.
|
||||
*/
|
||||
public class StatusCellFactory implements Callback<TableColumn<syncTableElement, String>, TableCell<syncTableElement, String>> {
|
||||
@Override
|
||||
public TableCell<syncTableElement, String> call(TableColumn<syncTableElement, String> p){
|
||||
StatusTableCell<syncTableElement, String> cell = new StatusTableCell<>();
|
||||
cell.setAlignment(Pos.CENTER);
|
||||
return cell;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.scene.control.TableCell;
|
||||
|
||||
import moodle.sync.core.util.MoodleAction;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
import org.lecturestudio.javafx.control.SvgIcon;
|
||||
|
||||
/**
|
||||
* Class used to change the background color of a TableCell.
|
||||
*/
|
||||
public class StatusTableCell <U, B> extends TableCell<syncTableElement, String> {
|
||||
|
||||
@Override
|
||||
protected void updateItem(String item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
setGraphic(null);
|
||||
|
||||
if (empty || item == null || getTableRow() == null || getTableRow().getItem() == null) {
|
||||
setText(null);
|
||||
setStyle("-fx-background-color: TRANSPARENT");
|
||||
} else {
|
||||
if(getTableRow().getItem().getAction() == MoodleAction.MoodleSynchronize){
|
||||
setText(item);
|
||||
setStyle("-fx-background-color: PALEGREEN");
|
||||
}
|
||||
else if(getTableRow().getItem().getAction() == MoodleAction.MoodleUpload){
|
||||
setText(item);
|
||||
setStyle("-fx-background-color: SKYBLUE");
|
||||
}
|
||||
else if(getTableRow().getItem().getAction() == MoodleAction.FTPUpload){
|
||||
SvgIcon icon = new SvgIcon();
|
||||
icon.getStyleClass().add("ftp-icon");
|
||||
setGraphic(icon);
|
||||
setText(item);
|
||||
setStyle("-fx-background-color: ORANGE");
|
||||
}
|
||||
else if(getTableRow().getItem().getAction() == MoodleAction.UploadSection){
|
||||
setText(item);
|
||||
setStyle("-fx-font-weight: bold");
|
||||
}
|
||||
else if(getTableRow().getItem().getAction() == MoodleAction.DatatypeNotKnown){
|
||||
setText(item);
|
||||
setStyle("-fx-font-weight: bold");
|
||||
}
|
||||
else{
|
||||
setText(item);
|
||||
setStyle("-fx-background-color: TRANSPARENT");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.cell.CheckBoxTableCell;
|
||||
import javafx.util.StringConverter;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
/**
|
||||
* Class used to display the selctedProperty-value inside a CheckBoxTreeTableCell.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class UploadCheckBoxCell<U, B> extends CheckBoxTableCell<syncTableElement, Boolean> {
|
||||
|
||||
private CheckBox checkBox;
|
||||
|
||||
private boolean showLabel;
|
||||
|
||||
private ObservableValue<Boolean> booleanProperty;
|
||||
|
||||
|
||||
@Override
|
||||
public void updateItem(Boolean item, boolean empty) {
|
||||
this.checkBox = new CheckBox();
|
||||
this.checkBox.setAlignment(Pos.CENTER);
|
||||
setAlignment(Pos.CENTER);
|
||||
setGraphic(checkBox);
|
||||
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (booleanProperty instanceof BooleanProperty) {
|
||||
checkBox.selectedProperty().unbindBidirectional((BooleanProperty) booleanProperty);
|
||||
booleanProperty = null;
|
||||
}
|
||||
if (empty) {
|
||||
checkBox.setAlignment(Pos.CENTER);
|
||||
setText(null);
|
||||
setGraphic(null);
|
||||
} else if (getTableRow() != null) {
|
||||
if(getTableRow().getItem() != null && !getTableRow().getItem().isSelectable()) {
|
||||
checkBox.setAlignment(Pos.CENTER);
|
||||
setDisable(false);
|
||||
setGraphic(null);
|
||||
}
|
||||
} else {
|
||||
StringConverter<Boolean> c = getConverter();
|
||||
|
||||
if (showLabel) {
|
||||
checkBox.setAlignment(Pos.CENTER);
|
||||
setText(c.toString(item));
|
||||
}
|
||||
setGraphic(checkBox);
|
||||
|
||||
ObservableValue<?> obsValue = getSelectedProperty();
|
||||
if (obsValue instanceof BooleanProperty) {
|
||||
booleanProperty = (ObservableValue<Boolean>) obsValue;
|
||||
checkBox.selectedProperty().bindBidirectional((BooleanProperty) booleanProperty);
|
||||
}
|
||||
|
||||
checkBox.disableProperty().bind(Bindings.not(
|
||||
getTableView().editableProperty().and(
|
||||
getTableColumn().editableProperty()).and(
|
||||
editableProperty())
|
||||
));
|
||||
|
||||
|
||||
}
|
||||
|
||||
checkBox.setAlignment(Pos.CENTER);
|
||||
setAlignment(Pos.CENTER);
|
||||
}
|
||||
|
||||
|
||||
private ObservableValue<?> getSelectedProperty() {
|
||||
return getSelectedStateCallback() != null ?
|
||||
getSelectedStateCallback().call(getIndex()) :
|
||||
getTableColumn().getCellObservableValue(getIndex());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.control.TableColumn;
|
||||
import javafx.util.Callback;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
/**
|
||||
* Class used for determining the state of a CheckBox inside the "sync-page"-table.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class UploadElementCellValueFactory implements Callback<TableColumn.CellDataFeatures<syncTableElement,Boolean>, ObservableValue<Boolean>> {
|
||||
@Override
|
||||
public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<syncTableElement, Boolean> param)
|
||||
{
|
||||
syncTableElement elem = param.getValue();
|
||||
//selectedProperty should be used to determine the state.
|
||||
param.getValue().selectedProperty();
|
||||
SimpleBooleanProperty booleanProp= (SimpleBooleanProperty) elem.selectedProperty();
|
||||
booleanProp.addListener(new ChangeListener<Boolean>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue,
|
||||
Boolean newValue) {
|
||||
elem.setSelected(newValue);
|
||||
}
|
||||
});
|
||||
return booleanProp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
package moodle.sync.javafx.custom;
|
||||
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.VBox;
|
||||
import moodle.sync.core.util.MoodleAction;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
import org.controlsfx.control.PopOver;
|
||||
import org.lecturestudio.javafx.control.SvgIcon;
|
||||
|
||||
/**
|
||||
* Class used to display the Name of a Section/ Module including different styles/background colors inside a cell.
|
||||
*/
|
||||
public class UploadHighlightTableCell <U, B> extends TableCell<syncTableElement, String> {
|
||||
|
||||
private Listener listener = new Listener();
|
||||
private PopOver popOver;
|
||||
|
||||
@Override
|
||||
public void updateItem(String item, boolean empty) {
|
||||
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if(getTableRow().getItem() != null) getTableRow().getItem().selectedProperty().removeListener(listener);
|
||||
|
||||
if(popOver != null){
|
||||
popOver = null;
|
||||
this.setOnMouseEntered(mouseEvent -> {
|
||||
});
|
||||
|
||||
this.setOnMouseExited(mouseEvent -> {
|
||||
});
|
||||
}
|
||||
|
||||
setGraphic(null);
|
||||
setVisible(true);
|
||||
getTableRow().getStyleClass().remove("headerstyle");
|
||||
|
||||
if (empty || item == null || getTableRow() == null || getTableRow().getItem() == null) {
|
||||
setText(null);
|
||||
setEditable(false);
|
||||
} else if(getTableRow().getItem().getAction() == MoodleAction.ExistingSection ){
|
||||
if(!(getTableRow().getItem().getModuleType().replaceAll("\\<.*?>", "")).isEmpty()){
|
||||
Label textArea = new Label();
|
||||
textArea.setText(getTableRow().getItem().getModuleType().replaceAll("\\<.*?>", ""));
|
||||
textArea.setWrapText(true);
|
||||
textArea.setMaxWidth(200);
|
||||
textArea.setStyle("-fx-font-weight: normal");
|
||||
textArea.getStyleClass().add("popUpTextArea");
|
||||
VBox vBox = new VBox(textArea);
|
||||
vBox.setPadding(new Insets(5));
|
||||
popOver = new PopOver(vBox);
|
||||
popOver.setArrowLocation(PopOver.ArrowLocation.LEFT_CENTER);
|
||||
|
||||
this.setOnMouseEntered(mouseEvent -> {
|
||||
//Show PopOver when mouse enters label
|
||||
popOver.show(this);
|
||||
});
|
||||
this.setOnMouseExited(mouseEvent -> {
|
||||
//Hide PopOver when mouse exits label
|
||||
popOver.hide();
|
||||
});
|
||||
}
|
||||
setText(getTableRow().getItem().getModuleName());
|
||||
setStyle("-fx-font-weight: bold");
|
||||
|
||||
|
||||
getTableRow().getStyleClass().add("headerstyle");
|
||||
|
||||
}
|
||||
else if(getTableRow().getItem().getAction() == MoodleAction.MoodleUpload ||
|
||||
getTableRow().getItem().getAction() == MoodleAction.FTPUpload ||
|
||||
getTableRow().getItem().getAction() == MoodleAction.UploadSection ||
|
||||
getTableRow().getItem().getAction() == MoodleAction.DatatypeNotKnown) {
|
||||
if((getTableRow().getItem().getAction() == MoodleAction.MoodleUpload && getTableRow().getItem() != null) ||
|
||||
(getTableRow().getItem().getAction() == MoodleAction.FTPUpload && getTableRow().getItem() != null)) {
|
||||
if(!getTableRow().getItem().selectedProperty().get()){
|
||||
setText(null);
|
||||
}
|
||||
getTableRow().getItem().selectedProperty().addListener(listener);
|
||||
}
|
||||
else {
|
||||
setText(null);
|
||||
}
|
||||
}
|
||||
else{
|
||||
setEditable(false);
|
||||
SvgIcon icon = new SvgIcon();
|
||||
setStyle("-fx-font-weight: normal");
|
||||
switch (getTableRow().getItem().getModuleType()) {
|
||||
case "section" -> setStyle("-fx-font-weight: bold");
|
||||
case "resource" -> icon.getStyleClass().add("file-icon");
|
||||
case "forum" -> icon.getStyleClass().add("forum-icon");
|
||||
case "folder" -> icon.getStyleClass().add("folder-icon");
|
||||
case "label" -> icon.getStyleClass().add("label-icon");
|
||||
case "quiz" -> icon.getStyleClass().add("quiz-icon");
|
||||
case "assign" -> icon.getStyleClass().add("assignment-icon");
|
||||
case "chat" -> icon.getStyleClass().add("chat-icon");
|
||||
case "feedback" -> icon.getStyleClass().add("feedback-icon");
|
||||
case "url" -> icon.getStyleClass().add("url-icon");
|
||||
case "survey" -> icon.getStyleClass().add("survey-icon");
|
||||
default -> icon.getStyleClass().add("other-icon");
|
||||
}
|
||||
setGraphic(icon);
|
||||
setText(item.replaceAll("\\u00a0\\n| \\r\\n", ""));
|
||||
}
|
||||
}
|
||||
|
||||
public class Listener implements ChangeListener {
|
||||
@Override
|
||||
public void changed(ObservableValue observableValue, Object o, Object t1) {
|
||||
setEditable(getTableRow().getItem().selectedProperty().get());
|
||||
if(getTableRow().getItem().selectedProperty().get()) setText(getTableRow().getItem().getExistingFileName());
|
||||
setVisible(getTableRow().getItem().selectedProperty().get());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
package moodle.sync.javafx.inject;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.name.Names;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import moodle.sync.core.web.service.MoodleService;
|
||||
import org.lecturestudio.core.app.AppDataLocator;
|
||||
import org.lecturestudio.core.app.ApplicationContext;
|
||||
import org.lecturestudio.core.app.LocaleProvider;
|
||||
import org.lecturestudio.core.app.configuration.Configuration;
|
||||
import org.lecturestudio.core.app.configuration.ConfigurationService;
|
||||
import org.lecturestudio.core.app.configuration.JsonConfigurationService;
|
||||
import org.lecturestudio.core.app.dictionary.Dictionary;
|
||||
import org.lecturestudio.core.audio.bus.AudioBus;
|
||||
import org.lecturestudio.core.bus.ApplicationBus;
|
||||
import org.lecturestudio.core.bus.EventBus;
|
||||
import org.lecturestudio.core.util.AggregateBundle;
|
||||
import org.lecturestudio.core.util.DirUtils;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import moodle.sync.core.config.DefaultConfiguration;
|
||||
import moodle.sync.core.config.MoodleSyncConfiguration;
|
||||
import moodle.sync.core.context.MoodleSyncContext;
|
||||
|
||||
public class ApplicationModule extends AbstractModule {
|
||||
|
||||
private final static Logger LOG = LogManager.getLogger(ApplicationModule.class);
|
||||
|
||||
private static final AppDataLocator LOCATOR = new AppDataLocator("MoodleSync");
|
||||
|
||||
private static final File CONFIG_FILE = new File(LOCATOR.toAppDataPath("config.json"));
|
||||
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ApplicationContext.class).to(MoodleSyncContext.class);
|
||||
|
||||
Properties streamProps = new Properties();
|
||||
|
||||
try {
|
||||
streamProps.load(getClass().getClassLoader()
|
||||
.getResourceAsStream("resources/moodle-api.properties"));
|
||||
|
||||
Names.bindProperties(binder(), streamProps);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error("Load stream properties failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ResourceBundle createResourceBundle(Configuration config) throws Exception {
|
||||
LocaleProvider localeProvider = new LocaleProvider();
|
||||
Locale locale = localeProvider.getBestSupported(config.getLocale());
|
||||
|
||||
return new AggregateBundle(locale, "resources.i18n.core", "resources.i18n.dict");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
AggregateBundle createAggregateBundle(ResourceBundle resourceBundle) {
|
||||
return (AggregateBundle) resourceBundle;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
Dictionary provideDictionary(ResourceBundle resourceBundle) {
|
||||
return new Dictionary() {
|
||||
|
||||
@Override
|
||||
public String get(String key) throws NullPointerException {
|
||||
return resourceBundle.getString(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String key) {
|
||||
return resourceBundle.containsKey(key);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
MoodleService createMoodleService(Configuration config){
|
||||
MoodleSyncConfiguration syncConfig = (MoodleSyncConfiguration) config;
|
||||
return new MoodleService(syncConfig.moodleUrlProperty());
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
MoodleSyncContext createApplicationContext(Configuration config,
|
||||
Dictionary dict) throws Exception {
|
||||
EventBus eventBus = ApplicationBus.get();
|
||||
EventBus audioBus = AudioBus.get();
|
||||
|
||||
return new MoodleSyncContext(LOCATOR, CONFIG_FILE, config, dict,
|
||||
eventBus, audioBus);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ConfigurationService<MoodleSyncConfiguration> provideConfigurationService() {
|
||||
ConfigurationService<MoodleSyncConfiguration> configService = null;
|
||||
|
||||
try {
|
||||
configService = new JsonConfigurationService<>();
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error("Create configuration service failed.", e);
|
||||
}
|
||||
|
||||
return configService;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
Configuration provideConfiguration(
|
||||
ConfigurationService<MoodleSyncConfiguration> configService) {
|
||||
MoodleSyncConfiguration configuration = null;
|
||||
|
||||
try {
|
||||
DirUtils.createIfNotExists(Paths.get(LOCATOR.getAppDataPath()));
|
||||
|
||||
if (!CONFIG_FILE.exists()) {
|
||||
// Create configuration with default values.
|
||||
configuration = new DefaultConfiguration();
|
||||
|
||||
configService.save(CONFIG_FILE, configuration);
|
||||
}
|
||||
else {
|
||||
configuration = configService.load(CONFIG_FILE,
|
||||
MoodleSyncConfiguration.class);
|
||||
}
|
||||
|
||||
// Set system default locale.
|
||||
LocaleProvider localeProvider = new LocaleProvider();
|
||||
configuration.setLocale(localeProvider.getBestSupported(
|
||||
configuration.getLocale()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error("Create configuration failed", e);
|
||||
}
|
||||
|
||||
return configuration;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package moodle.sync.javafx.inject;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.TypeLiteral;
|
||||
import com.google.inject.spi.TypeEncounter;
|
||||
import com.google.inject.spi.TypeListener;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import javafx.util.BuilderFactory;
|
||||
|
||||
import moodle.sync.javafx.view.*;
|
||||
import moodle.sync.view.*;
|
||||
|
||||
import org.lecturestudio.core.inject.DIViewContextFactory;
|
||||
import org.lecturestudio.core.util.AggregateBundle;
|
||||
import org.lecturestudio.core.view.*;
|
||||
import org.lecturestudio.javafx.guice.FxmlViewLoader;
|
||||
import org.lecturestudio.javafx.guice.FxmlViewMatcher;
|
||||
import org.lecturestudio.javafx.view.*;
|
||||
import org.lecturestudio.javafx.view.builder.DIBuilderFactory;
|
||||
|
||||
public class ViewModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(BuilderFactory.class).to(DIBuilderFactory.class);
|
||||
bind(ViewContextFactory.class).to(DIViewContextFactory.class);
|
||||
|
||||
bind(DirectoryChooserView.class).to(FxDirectoryChooserView.class);
|
||||
bind(FileChooserView.class).to(FxFileChooserView.class);
|
||||
bind(NewVersionView.class).to(FxNewVersionView.class);
|
||||
bind(NotificationView.class).to(FxNotificationView.class);
|
||||
bind(NotificationPopupView.class).to(FxNotificationPopupView.class);
|
||||
bind(NotificationPopupManager.class).to(FxNotificationPopupManager.class);
|
||||
bind(ProgressView.class).to(FxProgressView.class);
|
||||
bind(ProgressDialogView.class).to(FxProgressDialogView.class);
|
||||
|
||||
bind(MainView.class).to(FxMainView.class);
|
||||
bind(StartView.class).to(FxStartView.class);
|
||||
bind(SettingsView.class).to(FxSettingsView.class);
|
||||
|
||||
Provider<AggregateBundle> resourceProvider = getProvider(AggregateBundle.class);
|
||||
Provider<BuilderFactory> builderProvider = getProvider(BuilderFactory.class);
|
||||
|
||||
bindListener(new FxmlViewMatcher(), new TypeListener() {
|
||||
|
||||
@Override
|
||||
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
|
||||
encounter.register(FxmlViewLoader.getInstance(resourceProvider, builderProvider));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package moodle.sync.javafx.log;
|
||||
|
||||
import org.lecturestudio.core.app.AppDataLocator;
|
||||
import org.lecturestudio.core.log.Log4jXMLConfigurationFactory;
|
||||
import org.lecturestudio.core.model.VersionInfo;
|
||||
|
||||
import org.apache.logging.log4j.core.config.Order;
|
||||
import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
|
||||
@Plugin(name = "Log4jConfigurationFactory", category = "ConfigurationFactory")
|
||||
@Order(10)
|
||||
public class Log4jConfigurationFactory extends Log4jXMLConfigurationFactory {
|
||||
|
||||
public Log4jConfigurationFactory() {
|
||||
AppDataLocator dataLocator = new AppDataLocator("MoodleSync");
|
||||
|
||||
System.setProperty("logAppVersion", VersionInfo.getAppVersion());
|
||||
System.setProperty("logFilePath", dataLocator.getAppDataPath());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package moodle.sync.javafx.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class ReturnValue {
|
||||
|
||||
private List<Path> fileList;
|
||||
|
||||
private syncTableElement element;
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package moodle.sync.javafx.model;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
|
||||
public class TimeDateElement {
|
||||
private ObjectProperty<LocalDate> localDate;
|
||||
private ObjectProperty<LocalTime> localTime;
|
||||
|
||||
public TimeDateElement(LocalDate localDate, LocalTime localTime) {
|
||||
this.localDate = new SimpleObjectProperty(localDate);
|
||||
this.localTime = new SimpleObjectProperty(localTime);
|
||||
}
|
||||
|
||||
public void setLocalDate(LocalDate localDate) {
|
||||
this.localDate.set(localDate);
|
||||
}
|
||||
|
||||
public void setLocalTime(LocalTime localTime) {
|
||||
this.localTime.set(localTime);
|
||||
}
|
||||
|
||||
public LocalDate getLocalDate() {
|
||||
return localDate.get();
|
||||
}
|
||||
|
||||
public LocalTime getLocalTime() {
|
||||
if(isNull(localTime.get())){
|
||||
return LocalTime.of(0, 0);
|
||||
}
|
||||
return localTime.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<LocalDate> LocalDateProperty() {
|
||||
return localDate;
|
||||
}
|
||||
|
||||
public ObjectProperty<LocalTime> LocalTimeProperty() {
|
||||
return localTime;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,523 @@
|
|||
package moodle.sync.javafx.model;
|
||||
|
||||
import javafx.beans.property.*;
|
||||
import moodle.sync.core.util.MoodleAction;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
|
||||
public class syncTableElement {
|
||||
|
||||
private StringProperty moduleName;
|
||||
|
||||
private IntegerProperty cmid;
|
||||
|
||||
private IntegerProperty section;
|
||||
|
||||
private IntegerProperty sectionId;
|
||||
|
||||
private IntegerProperty oldPos;
|
||||
|
||||
private StringProperty moduleType;
|
||||
|
||||
private StringProperty existingFile;
|
||||
|
||||
private StringProperty existingFileName;
|
||||
|
||||
private BooleanProperty selectable;
|
||||
|
||||
private BooleanProperty selected;
|
||||
|
||||
private MoodleAction action;
|
||||
|
||||
private IntegerProperty beforemod;
|
||||
|
||||
private BooleanProperty visible;
|
||||
|
||||
private ObjectProperty<TimeDateElement> availabilityDateTime;
|
||||
|
||||
private BooleanProperty delete = new SimpleBooleanProperty(false);
|
||||
|
||||
public syncTableElement(String moduleName, Integer cmid, Integer section, Integer sectionId, Integer oldPos, String moduleType, Path existingFile, Boolean selectable, Boolean selected, MoodleAction action, Boolean visible){
|
||||
this.moduleName = new SimpleStringProperty(moduleName);
|
||||
this.cmid = new SimpleIntegerProperty(cmid);
|
||||
this.section = new SimpleIntegerProperty(section);
|
||||
this.sectionId = new SimpleIntegerProperty(sectionId);
|
||||
this.oldPos = new SimpleIntegerProperty(oldPos);
|
||||
this.moduleType = new SimpleStringProperty(moduleType);
|
||||
this.existingFile = new SimpleStringProperty(existingFile.toString());
|
||||
this.existingFileName = new SimpleStringProperty(existingFile.getFileName().toString());
|
||||
this.selectable = new SimpleBooleanProperty(selectable);
|
||||
this.selected = new SimpleBooleanProperty(selected);
|
||||
this.action = action;
|
||||
this.beforemod = new SimpleIntegerProperty(-1);
|
||||
this.visible = new SimpleBooleanProperty(visible);
|
||||
this.availabilityDateTime = new SimpleObjectProperty(new TimeDateElement(null, null));
|
||||
}
|
||||
|
||||
public syncTableElement(String moduleName, Integer cmid, Integer section, Integer sectionId, Integer oldPos, String moduleType, Path existingFile, Boolean selectable, Boolean selected, MoodleAction action, Boolean visible, Integer beforemod){
|
||||
this.moduleName = new SimpleStringProperty(moduleName);
|
||||
this.cmid = new SimpleIntegerProperty(cmid);
|
||||
this.section = new SimpleIntegerProperty(section);
|
||||
this.sectionId = new SimpleIntegerProperty(sectionId);
|
||||
this.oldPos = new SimpleIntegerProperty(oldPos);
|
||||
this.moduleType = new SimpleStringProperty(moduleType);
|
||||
this.existingFile = new SimpleStringProperty(existingFile.toString());
|
||||
this.existingFileName = new SimpleStringProperty(existingFile.getFileName().toString());
|
||||
this.selectable = new SimpleBooleanProperty(selectable);
|
||||
this.selected = new SimpleBooleanProperty(selected);
|
||||
this.action = action;
|
||||
this.beforemod = new SimpleIntegerProperty(beforemod);
|
||||
this.visible = new SimpleBooleanProperty(visible);
|
||||
this.availabilityDateTime = new SimpleObjectProperty(new TimeDateElement(null, null));
|
||||
}
|
||||
|
||||
public syncTableElement(String moduleName, Integer cmid, Integer section, Integer sectionId, Integer oldPos,String moduleType, Path existingFile, Boolean selectable, Boolean selected, MoodleAction action, Boolean visible, TimeDateElement availabilityDateTime){
|
||||
this.moduleName = new SimpleStringProperty(moduleName);
|
||||
this.cmid = new SimpleIntegerProperty(cmid);
|
||||
this.section = new SimpleIntegerProperty(section);
|
||||
this.sectionId = new SimpleIntegerProperty(sectionId);
|
||||
this.oldPos = new SimpleIntegerProperty(oldPos);
|
||||
this.moduleType = new SimpleStringProperty(moduleType);
|
||||
this.existingFile = new SimpleStringProperty(existingFile.toString());
|
||||
this.existingFileName = new SimpleStringProperty(existingFile.getFileName().toString());
|
||||
this.selectable = new SimpleBooleanProperty(selectable);
|
||||
this.selected = new SimpleBooleanProperty(selected);
|
||||
this.action = action;
|
||||
this.beforemod = new SimpleIntegerProperty(-1);
|
||||
this.visible = new SimpleBooleanProperty(visible);
|
||||
this.availabilityDateTime = new SimpleObjectProperty(availabilityDateTime);
|
||||
}
|
||||
|
||||
public syncTableElement(String moduleName, Integer cmid, Integer section, Integer sectionId, Integer oldPos, String moduleType, Path existingFile, Boolean selectable, Boolean selected, MoodleAction action, Boolean visible, TimeDateElement availabilityDateTime, Integer beforemod){
|
||||
this.moduleName = new SimpleStringProperty(moduleName);
|
||||
this.cmid = new SimpleIntegerProperty(cmid);
|
||||
this.section = new SimpleIntegerProperty(section);
|
||||
this.sectionId = new SimpleIntegerProperty(sectionId);
|
||||
this.oldPos = new SimpleIntegerProperty(oldPos);
|
||||
this.moduleType = new SimpleStringProperty(moduleType);
|
||||
this.existingFile = new SimpleStringProperty(existingFile.toString());
|
||||
this.existingFileName = new SimpleStringProperty(existingFile.getFileName().toString());
|
||||
this.selectable = new SimpleBooleanProperty(selectable);
|
||||
this.selected = new SimpleBooleanProperty(selected);
|
||||
this.action = action;
|
||||
this.beforemod = new SimpleIntegerProperty(beforemod);
|
||||
this.visible = new SimpleBooleanProperty(visible);
|
||||
this.availabilityDateTime = new SimpleObjectProperty(availabilityDateTime);
|
||||
}
|
||||
|
||||
public syncTableElement(String moduleName, Integer cmid, Integer section, Integer sectionId, Integer oldPos, String moduleType, Boolean selectable, Boolean selected, MoodleAction action, Boolean visible){
|
||||
this.moduleName = new SimpleStringProperty(moduleName);
|
||||
this.cmid = new SimpleIntegerProperty(cmid);
|
||||
this.section = new SimpleIntegerProperty(section);
|
||||
this.sectionId = new SimpleIntegerProperty(sectionId);
|
||||
this.oldPos = new SimpleIntegerProperty(oldPos);
|
||||
this.moduleType = new SimpleStringProperty(moduleType);
|
||||
this.existingFile = null;
|
||||
this.existingFileName = null;
|
||||
this.selectable = new SimpleBooleanProperty(selectable);
|
||||
this.selected = new SimpleBooleanProperty(selected);
|
||||
this.action = action;
|
||||
this.beforemod = new SimpleIntegerProperty(-1);
|
||||
this.visible = new SimpleBooleanProperty(visible);
|
||||
this.availabilityDateTime = new SimpleObjectProperty(new TimeDateElement(null, null));
|
||||
}
|
||||
|
||||
public syncTableElement(String moduleName, Integer cmid, Integer section, Integer sectionId, Integer oldPos, String moduleType, Path existingFile, Boolean selectable, Boolean selected, MoodleAction action, Integer beforemod, Boolean visible){
|
||||
this.moduleName = new SimpleStringProperty(moduleName);
|
||||
this.cmid = new SimpleIntegerProperty(cmid);
|
||||
this.section = new SimpleIntegerProperty(section);
|
||||
this.sectionId = new SimpleIntegerProperty(sectionId);
|
||||
this.oldPos = new SimpleIntegerProperty(oldPos);
|
||||
this.moduleType = new SimpleStringProperty(moduleType);
|
||||
this.existingFile = new SimpleStringProperty(existingFile.toString());
|
||||
this.existingFileName = new SimpleStringProperty(existingFile.getFileName().toString());
|
||||
this.selectable = new SimpleBooleanProperty(selectable);
|
||||
this.selected = new SimpleBooleanProperty(selected);
|
||||
this.action = action;
|
||||
this.beforemod = new SimpleIntegerProperty(beforemod);
|
||||
this.visible = new SimpleBooleanProperty(visible);
|
||||
this.availabilityDateTime = new SimpleObjectProperty(new TimeDateElement(null, null));
|
||||
}
|
||||
|
||||
public long getUnixTimeStamp(){
|
||||
LocalDateTime time = null;
|
||||
try{
|
||||
time = availabilityDateTime.get().getLocalTime().atDate(availabilityDateTime.get().getLocalDate());
|
||||
} catch (Exception e) {
|
||||
return 1659776301;
|
||||
}
|
||||
return time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()/1000L;
|
||||
}
|
||||
|
||||
public MoodleAction getAction(){ return action;}
|
||||
|
||||
/**
|
||||
* Providing the messageProperty.
|
||||
*
|
||||
* @return the messageProprty.
|
||||
*/
|
||||
public StringProperty moduleNameProperty() {
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the files message as a String.
|
||||
*
|
||||
* @return the files message as a String.
|
||||
*/
|
||||
public String getModuleName() {
|
||||
return this.moduleName.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new message.
|
||||
*
|
||||
* @param value the new message.
|
||||
*/
|
||||
public void setModuleName(String value) {
|
||||
this.moduleName.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the messageProperty.
|
||||
*
|
||||
* @return the messageProprty.
|
||||
*/
|
||||
public IntegerProperty cmidProperty() {
|
||||
return cmid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the files message as a String.
|
||||
*
|
||||
* @return the files message as a String.
|
||||
*/
|
||||
public Integer getCmid() {
|
||||
return this.cmid.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new message.
|
||||
*
|
||||
* @param value the new message.
|
||||
*/
|
||||
public void setCmid(Integer value) { this.cmid.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the messageProperty.
|
||||
*
|
||||
* @return the messageProprty.
|
||||
*/
|
||||
public IntegerProperty sectionProperty() {
|
||||
return section;
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the files message as a String.
|
||||
*
|
||||
* @return the files message as a String.
|
||||
*/
|
||||
public Integer getSection() {
|
||||
return this.section.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new message.
|
||||
*
|
||||
* @param value the new message.
|
||||
*/
|
||||
public void setSection(Integer value) { this.section.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the messageProperty.
|
||||
*
|
||||
* @return the messageProprty.
|
||||
*/
|
||||
public IntegerProperty sectionIdProperty() {
|
||||
return sectionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the files message as a String.
|
||||
*
|
||||
* @return the files message as a String.
|
||||
*/
|
||||
public Integer getSectionId() {
|
||||
return this.sectionId.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new message.
|
||||
*
|
||||
* @param value the new message.
|
||||
*/
|
||||
public void setSectionId(Integer value) { this.sectionId.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the messageProperty.
|
||||
*
|
||||
* @return the messageProprty.
|
||||
*/
|
||||
public IntegerProperty oldPosProperty() {
|
||||
return oldPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the files message as a String.
|
||||
*
|
||||
* @return the files message as a String.
|
||||
*/
|
||||
public Integer getOldPos() {
|
||||
return this.oldPos.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new message.
|
||||
*
|
||||
* @param value the new message.
|
||||
*/
|
||||
public void setOldPos(Integer value) { this.oldPos.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the messageProperty.
|
||||
*
|
||||
* @return the messageProprty.
|
||||
*/
|
||||
public StringProperty existingFileProperty() {
|
||||
return existingFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the files message as a String.
|
||||
*
|
||||
* @return the files message as a String.
|
||||
*/
|
||||
public String getExistingFile() {
|
||||
return this.existingFile.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new message.
|
||||
*
|
||||
* @param value the new message.
|
||||
*/
|
||||
public void setExistingFile(String value) {
|
||||
this.existingFile.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the messageProperty.
|
||||
*
|
||||
* @return the messageProprty.
|
||||
*/
|
||||
public StringProperty existingFileNameProperty() {
|
||||
return existingFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the files message as a String.
|
||||
*
|
||||
* @return the files message as a String.
|
||||
*/
|
||||
public String getExistingFileName() {
|
||||
return this.existingFileName.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new message.
|
||||
*
|
||||
* @param value the new message.
|
||||
*/
|
||||
public void setExistingFileName(String value) {
|
||||
this.existingFileName.set(value);
|
||||
}
|
||||
/**
|
||||
* Providing the messageProperty.
|
||||
*
|
||||
* @return the messageProprty.
|
||||
*/
|
||||
public StringProperty moduleTypeProperty() {
|
||||
return moduleType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the files message as a String.
|
||||
*
|
||||
* @return the files message as a String.
|
||||
*/
|
||||
public String getModuleType() {
|
||||
return this.moduleType.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new message.
|
||||
*
|
||||
* @param value the new message.
|
||||
*/
|
||||
public void setModuleType(String value) {
|
||||
this.moduleType.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the selectedProperty.
|
||||
*
|
||||
* @return the selectedProperty.
|
||||
*/
|
||||
public BooleanProperty selectableProperty() {
|
||||
return selectable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proving whether the element is selected or not.
|
||||
*
|
||||
* @return True if the element is selected.
|
||||
*/
|
||||
public boolean isSelectable() {
|
||||
return this.selectable.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selectedProperty.
|
||||
*
|
||||
* @param value if the object is selected.
|
||||
*/
|
||||
public void setSelectable(boolean value) {
|
||||
this.selectable.set(value);
|
||||
}
|
||||
/**
|
||||
* Providing the selectedProperty.
|
||||
*
|
||||
* @return the selectedProperty.
|
||||
*/
|
||||
public BooleanProperty selectedProperty() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proving whether the element is selected or not.
|
||||
*
|
||||
* @return True if the element is selected.
|
||||
*/
|
||||
public boolean isSelected() {
|
||||
return this.selected.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selectedProperty.
|
||||
*
|
||||
* @param value if the object is selected.
|
||||
*/
|
||||
public void setSelected(boolean value) {
|
||||
this.selected.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the messageProperty.
|
||||
*
|
||||
* @return the messageProprty.
|
||||
*/
|
||||
public IntegerProperty beforemodProperty() {
|
||||
return beforemod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the files message as a String.
|
||||
*
|
||||
* @return the files message as a String.
|
||||
*/
|
||||
public Integer getBeforemod() {
|
||||
return this.beforemod.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new message.
|
||||
*
|
||||
* @param value the new message.
|
||||
*/
|
||||
public void setBeforemod(Integer value) { this.beforemod.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the messageProperty.
|
||||
*
|
||||
* @return the messageProprty.
|
||||
*/
|
||||
public BooleanProperty visibleProperty() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the files message as a String.
|
||||
*
|
||||
* @return the files message as a String.
|
||||
*/
|
||||
public boolean getVisible() {
|
||||
return this.visible.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new message.
|
||||
*
|
||||
* @param value the new message.
|
||||
*/
|
||||
public void setVisible(boolean value) { this.visible.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the messageProperty.
|
||||
*
|
||||
* @return the messageProprty.
|
||||
*/
|
||||
public ObjectProperty<TimeDateElement> availabilityDateTimeProperty() {
|
||||
return availabilityDateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the files message as a String.
|
||||
*
|
||||
* @return the files message as a String.
|
||||
*/
|
||||
public TimeDateElement getTimeDateElement() {
|
||||
return availabilityDateTime.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new message.
|
||||
*
|
||||
* @param value the new message.
|
||||
*/
|
||||
public void setTimeDateElement(TimeDateElement value) { this.availabilityDateTime.set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the messageProperty.
|
||||
*
|
||||
* @return the messageProprty.
|
||||
*/
|
||||
public BooleanProperty deleteProperty() {
|
||||
return delete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the files message as a String.
|
||||
*
|
||||
* @return the files message as a String.
|
||||
*/
|
||||
public boolean getDelete() {
|
||||
return this.delete.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new message.
|
||||
*
|
||||
* @param value the new message.
|
||||
*/
|
||||
public void setDelete(boolean value) { this.delete.set(value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,285 @@
|
|||
package moodle.sync.javafx.view;
|
||||
|
||||
import static java.util.Objects.nonNull;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.lecturestudio.core.app.ApplicationContext;
|
||||
import org.lecturestudio.core.app.Theme;
|
||||
import org.lecturestudio.core.app.configuration.Configuration;
|
||||
import org.lecturestudio.core.geometry.Rectangle2D;
|
||||
import org.lecturestudio.core.view.Action;
|
||||
import org.lecturestudio.core.view.View;
|
||||
import org.lecturestudio.core.view.ViewLayer;
|
||||
import org.lecturestudio.javafx.beans.converter.KeyEventConverter;
|
||||
import org.lecturestudio.javafx.util.FxUtils;
|
||||
import org.lecturestudio.javafx.view.FxmlView;
|
||||
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.Window;
|
||||
import javafx.stage.WindowEvent;
|
||||
|
||||
import moodle.sync.view.MainView;
|
||||
|
||||
/**
|
||||
* Class implementing the functions of the "main-window".
|
||||
*/
|
||||
@FxmlView(name = "main-window")
|
||||
public class FxMainView extends StackPane implements MainView {
|
||||
|
||||
private final ApplicationContext context;
|
||||
|
||||
private final Deque<Node> viewStack;
|
||||
|
||||
private Predicate<org.lecturestudio.core.input.KeyEvent> keyAction;
|
||||
|
||||
private Action shownAction;
|
||||
|
||||
private Action closeAction;
|
||||
|
||||
@FXML
|
||||
private BorderPane contentPane;
|
||||
|
||||
|
||||
@Inject
|
||||
public FxMainView(ApplicationContext context) {
|
||||
super();
|
||||
|
||||
this.context = context;
|
||||
this.viewStack = new ArrayDeque<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D getBounds() {
|
||||
Window window = getScene().getWindow();
|
||||
|
||||
return new Rectangle2D(window.getX(), window.getY(), window.getWidth(),
|
||||
window.getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// Fire close request in order to shut down appropriately.
|
||||
Window window = getScene().getWindow();
|
||||
window.fireEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSE_REQUEST));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
FxUtils.invoke(() -> {
|
||||
Window window = getScene().getWindow();
|
||||
window.hide();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeView(View view, ViewLayer layer) {
|
||||
if (layer == ViewLayer.Window) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkNodeView(view);
|
||||
|
||||
Node nodeView = (Node) view;
|
||||
|
||||
removeNode(nodeView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showView(View view, ViewLayer layer) {
|
||||
if (layer == ViewLayer.Window) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkNodeView(view);
|
||||
|
||||
Node nodeView = (Node) view;
|
||||
|
||||
switch (layer) {
|
||||
case Content:
|
||||
showNode(nodeView, true);
|
||||
break;
|
||||
|
||||
case Dialog:
|
||||
case Notification:
|
||||
showNodeOnTop(nodeView);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFullscreen(boolean fullscreen) {
|
||||
FxUtils.invoke(() -> {
|
||||
Stage stage = (Stage) getScene().getWindow();
|
||||
stage.setFullScreen(fullscreen);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnKeyEvent(Predicate<org.lecturestudio.core.input.KeyEvent> action) {
|
||||
this.keyAction = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnShown(Action action) {
|
||||
this.shownAction = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnClose(Action action) {
|
||||
this.closeAction = action;
|
||||
}
|
||||
|
||||
private void onKeyEvent(KeyEvent event) {
|
||||
if (event.getEventType() == KeyEvent.KEY_PRESSED) {
|
||||
org.lecturestudio.core.input.KeyEvent keyEvent = KeyEventConverter.INSTANCE.from(event);
|
||||
|
||||
if (nonNull(keyAction) && keyAction.test(keyEvent)) {
|
||||
event.consume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeNode(Node nodeView) {
|
||||
FxUtils.invoke(() -> {
|
||||
boolean removed = getChildren().remove(nodeView);
|
||||
|
||||
if (!removed) {
|
||||
showNode(nodeView, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showNode(Node nodeView, boolean show) {
|
||||
Node currentView = contentPane.getCenter();
|
||||
boolean isSame = currentView == nodeView;
|
||||
|
||||
if (show) {
|
||||
if (!isSame) {
|
||||
viewStack.push(nodeView);
|
||||
|
||||
FxUtils.invoke(() -> {
|
||||
contentPane.setCenter(nodeView);
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (isSame) {
|
||||
Node lastView = viewStack.pop();
|
||||
|
||||
if (!viewStack.isEmpty()) {
|
||||
lastView = viewStack.pop();
|
||||
}
|
||||
|
||||
showNode(lastView, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void showNodeOnTop(Node nodeView) {
|
||||
FxUtils.invoke(() -> {
|
||||
getChildren().add(nodeView);
|
||||
});
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
Configuration config = context.getConfiguration();
|
||||
|
||||
// Set application wide font size.
|
||||
setStyle(String.format(Locale.US, "-fx-font-size: %.2fpt;", config.getUIControlSize()));
|
||||
|
||||
addEventFilter(KeyEvent.KEY_PRESSED, this::onKeyEvent);
|
||||
|
||||
config.themeProperty().addListener((observable, oldTheme, newTheme) -> {
|
||||
// Load new theme.
|
||||
loadTheme(newTheme);
|
||||
// Unload old theme.
|
||||
unloadTheme(oldTheme);
|
||||
});
|
||||
|
||||
// Init view-stack with default node.
|
||||
viewStack.push(contentPane.getCenter());
|
||||
|
||||
sceneProperty().addListener(new ChangeListener<>() {
|
||||
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Scene> observableValue,
|
||||
Scene oldScene, Scene newScene) {
|
||||
if (nonNull(newScene)) {
|
||||
sceneProperty().removeListener(this);
|
||||
|
||||
onSceneSet(newScene);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void loadTheme(Theme theme) {
|
||||
if (nonNull(theme) && nonNull(theme.getFile())) {
|
||||
String themeUrl = getClass().getResource(theme.getFile()).toExternalForm();
|
||||
getScene().getStylesheets().add(themeUrl);
|
||||
}
|
||||
}
|
||||
|
||||
private void unloadTheme(Theme theme) {
|
||||
if (nonNull(theme.getFile())) {
|
||||
String themeUrl = getClass().getResource(theme.getFile()).toExternalForm();
|
||||
getScene().getStylesheets().remove(themeUrl);
|
||||
}
|
||||
}
|
||||
|
||||
private void onSceneSet(Scene scene) {
|
||||
scene.windowProperty().addListener(new ChangeListener<>() {
|
||||
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Window> observable,
|
||||
Window oldWindow, Window newWindow) {
|
||||
if (nonNull(newWindow)) {
|
||||
scene.windowProperty().removeListener(this);
|
||||
|
||||
onStageSet((Stage) newWindow);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onStageSet(Stage stage) {
|
||||
// It's imperative to load fxml-defined stylesheets prior to the user-defined theme
|
||||
// stylesheet, so the theme can override initial styles.
|
||||
getStylesheets().forEach(file -> {
|
||||
loadTheme(new Theme("defined", file));
|
||||
});
|
||||
// Remove loaded stylesheets to avoid additional loading by the scene itself.
|
||||
getStylesheets().clear();
|
||||
|
||||
loadTheme(context.getConfiguration().getTheme());
|
||||
|
||||
stage.setOnShown(event -> {
|
||||
executeAction(shownAction);
|
||||
});
|
||||
stage.setOnCloseRequest(event -> {
|
||||
// Consume event. Don't close the window yet.
|
||||
event.consume();
|
||||
|
||||
executeAction(closeAction);
|
||||
});
|
||||
}
|
||||
|
||||
private void checkNodeView(View view) {
|
||||
if (!Node.class.isAssignableFrom(view.getClass())) {
|
||||
throw new IllegalArgumentException("View expected to be a JavaFX Node");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,233 @@
|
|||
package moodle.sync.javafx.view;
|
||||
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.util.converter.IntegerStringConverter;
|
||||
import moodle.sync.presenter.SettingsPresenter;
|
||||
import moodle.sync.util.UserInputValidations;
|
||||
import moodle.sync.view.SettingsView;
|
||||
import org.controlsfx.control.PopOver;
|
||||
import org.lecturestudio.core.beans.BooleanProperty;
|
||||
import org.lecturestudio.core.beans.ObjectProperty;
|
||||
import org.lecturestudio.core.beans.StringProperty;
|
||||
import org.lecturestudio.core.view.Action;
|
||||
import org.lecturestudio.javafx.beans.LectBooleanProperty;
|
||||
import org.lecturestudio.javafx.beans.LectObjectProperty;
|
||||
import org.lecturestudio.javafx.beans.LectStringProperty;
|
||||
import org.lecturestudio.javafx.util.FxUtils;
|
||||
import org.lecturestudio.javafx.view.FxView;
|
||||
import org.lecturestudio.javafx.view.FxmlView;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Class implementing the functions of the "settings-page".
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
@FxmlView(name = "main-settings", presenter = SettingsPresenter.class)
|
||||
public class FxSettingsView extends VBox implements SettingsView, FxView {
|
||||
|
||||
@FXML
|
||||
private Button closesettingsButton;
|
||||
|
||||
@FXML
|
||||
private ComboBox<Locale> languageCombo;
|
||||
|
||||
@FXML
|
||||
private TextField tokenField;
|
||||
|
||||
@FXML
|
||||
private TextField syncRootPath;
|
||||
|
||||
@FXML
|
||||
private TextField ftpField;
|
||||
|
||||
@FXML
|
||||
private TextField ftpUser;
|
||||
|
||||
@FXML
|
||||
private TextField ftpPassword;
|
||||
|
||||
@FXML
|
||||
private TextField moodleField;
|
||||
|
||||
@FXML
|
||||
private TextArea formatsMoodle;
|
||||
|
||||
@FXML
|
||||
private TextField ftpPort;
|
||||
|
||||
@FXML
|
||||
private TextArea formatsFileserver;
|
||||
|
||||
@FXML
|
||||
private Button syncRootPathButton;
|
||||
|
||||
@FXML
|
||||
private CheckBox showUnknownFormats;
|
||||
|
||||
public FxSettingsView() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exiting the "settings-page".
|
||||
*
|
||||
* @param action Users action.
|
||||
*/
|
||||
@Override
|
||||
public void setOnExit(Action action) {
|
||||
FxUtils.bindAction(closesettingsButton, action);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setLocale(ObjectProperty<Locale> locale) {
|
||||
languageCombo.valueProperty().bindBidirectional(new LectObjectProperty<>(locale));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocales(List<Locale> locales) {
|
||||
FxUtils.invoke(() -> languageCombo.getItems().setAll(locales));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMoodleField(StringProperty moodleURL) {
|
||||
moodleField.textProperty().bindBidirectional(new LectStringProperty(moodleURL));
|
||||
moodleField.textProperty().addListener(event -> {
|
||||
moodleField.pseudoClassStateChanged(
|
||||
PseudoClass.getPseudoClass("error"),
|
||||
!moodleField.getText().matches("^(https?)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]")
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Input of the Moodle-token.
|
||||
*
|
||||
* @param moodleToken User input.
|
||||
*/
|
||||
@Override
|
||||
public void setMoodleToken(StringProperty moodleToken) {
|
||||
tokenField.textProperty().bindBidirectional(new LectStringProperty(moodleToken));
|
||||
tokenField.textProperty().addListener(event -> {
|
||||
tokenField.pseudoClassStateChanged(
|
||||
PseudoClass.getPseudoClass("error"),
|
||||
(tokenField.getText().isEmpty())
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Input and inputvalidation of the fileserver url.
|
||||
*
|
||||
* @param ftpURL User input.
|
||||
*/
|
||||
@Override
|
||||
public void setFtpField(StringProperty ftpURL) {
|
||||
ftpField.textProperty().bindBidirectional(new LectStringProperty(ftpURL));
|
||||
ftpField.textProperty().addListener(event -> {
|
||||
ftpField.pseudoClassStateChanged(
|
||||
PseudoClass.getPseudoClass("error"),
|
||||
(!ftpField.getText().isEmpty() &&
|
||||
!ftpField.getText().matches("^(((https?|ftp)://)|(ftp\\.))[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Input and inputvalidation of the used port.
|
||||
*
|
||||
* @param ftpport User input.
|
||||
*/
|
||||
@Override
|
||||
public void setFtpPort(StringProperty ftpport) {
|
||||
ftpPort.setTextFormatter(new TextFormatter<Integer>(new IntegerStringConverter(), 0, UserInputValidations.numberValidationFormatter));
|
||||
ftpPort.textProperty().bindBidirectional(new LectStringProperty(ftpport));
|
||||
}
|
||||
|
||||
/**
|
||||
* Input of the fileserver username.
|
||||
*
|
||||
* @param ftpuser User input.
|
||||
*/
|
||||
@Override
|
||||
public void setFtpUser(StringProperty ftpuser) {
|
||||
ftpUser.textProperty().bindBidirectional(new LectStringProperty(ftpuser));
|
||||
}
|
||||
|
||||
/**
|
||||
* Input of the fileserver password.
|
||||
*
|
||||
* @param ftppassword User input.
|
||||
*/
|
||||
@Override
|
||||
public void setFtpPassword(StringProperty ftppassword) {
|
||||
ftpPassword.textProperty().bindBidirectional(new LectStringProperty(ftppassword));
|
||||
}
|
||||
|
||||
/**
|
||||
* Input of formats used to upload to the Moodle-Platform.
|
||||
*
|
||||
* @param moodleformats User input.
|
||||
*/
|
||||
@Override
|
||||
public void setFormatsMoodle(StringProperty moodleformats) {
|
||||
formatsMoodle.textProperty().bindBidirectional(new LectStringProperty(moodleformats));
|
||||
}
|
||||
|
||||
/**
|
||||
* Input of formats used to upload to the fileserver.
|
||||
*
|
||||
* @param fileserverformats User input.
|
||||
*/
|
||||
@Override
|
||||
public void setFormatsFileserver(StringProperty fileserverformats) {
|
||||
formatsFileserver.textProperty().bindBidirectional(new LectStringProperty(fileserverformats));
|
||||
}
|
||||
|
||||
/**
|
||||
* Input of the Root-Directory.
|
||||
*
|
||||
* @param path User input.
|
||||
*/
|
||||
@Override
|
||||
public void setSyncRootPath(StringProperty path) {
|
||||
syncRootPath.textProperty().bindBidirectional(new LectStringProperty(path));
|
||||
syncRootPath.textProperty().addListener(event -> {
|
||||
syncRootPath.pseudoClassStateChanged(
|
||||
PseudoClass.getPseudoClass("error"),
|
||||
!syncRootPath.getText().isEmpty() &&
|
||||
!Files.isDirectory(Paths.get(syncRootPath.getText()))
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the explorer.
|
||||
*
|
||||
* @param action User needs to click a button.
|
||||
*/
|
||||
@Override
|
||||
public void setSelectSyncRootPath(Action action) {
|
||||
FxUtils.bindAction(syncRootPathButton, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* User can set if files with unknownFormats should be shown in the "sync-table".
|
||||
*
|
||||
* @param unknownFormats User Input CheckBox.
|
||||
*/
|
||||
@Override
|
||||
public void setShowUnknownFormats(BooleanProperty unknownFormats) {
|
||||
showUnknownFormats.selectedProperty().bindBidirectional(new LectBooleanProperty(unknownFormats));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
package moodle.sync.javafx.view;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.*;
|
||||
import moodle.sync.core.model.json.Course;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
import moodle.sync.core.model.json.Section;
|
||||
import org.lecturestudio.core.beans.BooleanProperty;
|
||||
import org.lecturestudio.core.beans.ObjectProperty;
|
||||
import org.lecturestudio.core.view.Action;
|
||||
import org.lecturestudio.core.view.ConsumerAction;
|
||||
import org.lecturestudio.javafx.beans.LectBooleanProperty;
|
||||
import org.lecturestudio.javafx.beans.LectObjectProperty;
|
||||
import org.lecturestudio.javafx.util.FxUtils;
|
||||
import org.lecturestudio.javafx.view.FxView;
|
||||
import org.lecturestudio.javafx.view.FxmlView;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import moodle.sync.presenter.StartPresenter;
|
||||
import moodle.sync.view.StartView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class implementing the functions of the "start-page".
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
@FxmlView(name = "main-start", presenter = StartPresenter.class)
|
||||
public class FxStartView extends VBox implements StartView, FxView {
|
||||
|
||||
@FXML
|
||||
private Button syncButton;
|
||||
|
||||
@FXML
|
||||
private Button settingsButton;
|
||||
|
||||
@FXML
|
||||
private Button updateButton;
|
||||
|
||||
@FXML
|
||||
private Button folderButton;
|
||||
|
||||
@FXML
|
||||
private CheckBox allSelected;
|
||||
|
||||
@FXML
|
||||
private ComboBox<Course> courseCombo;
|
||||
|
||||
@FXML
|
||||
private ComboBox<Section> sectionCombo;
|
||||
|
||||
@FXML
|
||||
private TableView<syncTableElement> syncTable;
|
||||
|
||||
|
||||
public FxStartView() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setData(ObservableList<syncTableElement> data) {
|
||||
FxUtils.invoke(() -> {
|
||||
syncTable.getItems().clear();
|
||||
syncTable.setItems(data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the interface
|
||||
*
|
||||
* @param action User presses button.
|
||||
*/
|
||||
@Override
|
||||
public void setOnUpdate(Action action) {
|
||||
FxUtils.bindAction(updateButton, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the synchronisation process.
|
||||
*
|
||||
* @param action User presses button.
|
||||
*/
|
||||
@Override
|
||||
public void setOnSync(Action action) {
|
||||
FxUtils.bindAction(syncButton, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* User opens the "settings-page".
|
||||
*
|
||||
* @param action User presses button.
|
||||
*/
|
||||
@Override
|
||||
public void setOnSettings(Action action) {
|
||||
FxUtils.bindAction(settingsButton, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectAll(BooleanProperty selectAll) {
|
||||
allSelected.selectedProperty().bindBidirectional(new LectBooleanProperty(selectAll));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnFolder(Action action) {
|
||||
FxUtils.bindAction(folderButton, action);
|
||||
}
|
||||
/**
|
||||
* Method to set the elements of the Course-Combobox.
|
||||
*
|
||||
* @param courses Moodle-Courses to display.
|
||||
*/
|
||||
@Override
|
||||
public void setCourses(List<Course> courses) {
|
||||
FxUtils.invoke(() -> courseCombo.getItems().setAll(courses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Choosen Moodle-course.
|
||||
*
|
||||
* @param course choosen Moodle-course.
|
||||
*/
|
||||
@Override
|
||||
public void setCourse(ObjectProperty<Course> course) {
|
||||
courseCombo.valueProperty().bindBidirectional(new LectObjectProperty<>(course));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to set the elements of the Section-Combobox.
|
||||
*
|
||||
* @param sections Course-Sections to display.
|
||||
*/
|
||||
@Override
|
||||
public void setSections(List<Section> sections) {
|
||||
FxUtils.invoke(() -> sectionCombo.getItems().setAll(sections));
|
||||
sectionCombo.getSelectionModel().selectFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Choosen course-section.
|
||||
*
|
||||
* @param section choosen course-section.
|
||||
*/
|
||||
@Override
|
||||
public void setSection(ObjectProperty<Section> section) {
|
||||
sectionCombo.valueProperty().bindBidirectional(new LectObjectProperty<>(section));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to initiate the display of the sections of a choosen Course.
|
||||
*
|
||||
* @param action User chooses Course.
|
||||
*/
|
||||
@Override
|
||||
public void setOnCourseChanged(ConsumerAction<Course> action) {
|
||||
courseCombo.valueProperty().addListener((observable, oldCourse, newCourse) -> {
|
||||
executeAction(action, newCourse);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,451 @@
|
|||
package moodle.sync.presenter;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
import static java.util.Objects.nonNull;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.lecturestudio.core.app.ApplicationContext;
|
||||
import org.lecturestudio.core.app.configuration.Configuration;
|
||||
import org.lecturestudio.core.beans.BooleanProperty;
|
||||
import org.lecturestudio.core.bus.event.ViewVisibleEvent;
|
||||
import org.lecturestudio.core.input.KeyEvent;
|
||||
import org.lecturestudio.core.model.VersionInfo;
|
||||
import org.lecturestudio.core.presenter.NewVersionPresenter;
|
||||
import org.lecturestudio.core.presenter.NotificationPresenter;
|
||||
import org.lecturestudio.core.presenter.Presenter;
|
||||
import org.lecturestudio.core.presenter.command.CloseApplicationCommand;
|
||||
import org.lecturestudio.core.presenter.command.ClosePresenterCommand;
|
||||
import org.lecturestudio.core.presenter.command.NewVersionCommand;
|
||||
import org.lecturestudio.core.presenter.command.ShowPresenterCommand;
|
||||
import org.lecturestudio.core.util.ObservableHashMap;
|
||||
import org.lecturestudio.core.util.ObservableMap;
|
||||
import org.lecturestudio.core.util.ShutdownHandler;
|
||||
import org.lecturestudio.core.view.NotificationPopupManager;
|
||||
import org.lecturestudio.core.view.NotificationPopupView;
|
||||
import org.lecturestudio.core.view.NotificationType;
|
||||
import org.lecturestudio.core.view.View;
|
||||
import org.lecturestudio.core.view.ViewContextFactory;
|
||||
import org.lecturestudio.core.view.ViewHandler;
|
||||
import org.lecturestudio.core.view.ViewLayer;
|
||||
import org.lecturestudio.web.api.model.GitHubRelease;
|
||||
import org.lecturestudio.web.api.service.VersionChecker;
|
||||
|
||||
import moodle.sync.input.Shortcut;
|
||||
import moodle.sync.view.MainView;
|
||||
|
||||
/**
|
||||
* Class defining the logic of the "main-window".
|
||||
*/
|
||||
public class MainPresenter extends org.lecturestudio.core.presenter.MainPresenter<MainView> implements ViewHandler {
|
||||
|
||||
private final ObservableMap<Class<? extends View>, BooleanProperty> viewMap;
|
||||
|
||||
private final Map<KeyEvent, Predicate<KeyEvent>> shortcutMap;
|
||||
|
||||
private final List<ShutdownHandler> shutdownHandlers;
|
||||
|
||||
private final List<Presenter<?>> contexts;
|
||||
|
||||
private final NotificationPopupManager popupManager;
|
||||
|
||||
private final ViewContextFactory contextFactory;
|
||||
|
||||
/** The waiting notification. */
|
||||
private NotificationPresenter notificationPresenter;
|
||||
|
||||
|
||||
@Inject
|
||||
MainPresenter(ApplicationContext context, MainView view,
|
||||
NotificationPopupManager popupManager,
|
||||
ViewContextFactory contextFactory) {
|
||||
super(context, view);
|
||||
|
||||
this.popupManager = popupManager;
|
||||
this.contextFactory = contextFactory;
|
||||
this.viewMap = new ObservableHashMap<>();
|
||||
this.shortcutMap = new HashMap<>();
|
||||
this.contexts = new ArrayList<>();
|
||||
this.shutdownHandlers = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openFile(File file) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArgs(String[] args) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
registerShortcut(Shortcut.CLOSE_VIEW, this::closeView);
|
||||
|
||||
addShutdownHandler(new SaveConfigurationHandler(context));
|
||||
addShutdownHandler(new ShutdownHandler() {
|
||||
|
||||
@Override
|
||||
public boolean execute() {
|
||||
if (nonNull(closeAction)) {
|
||||
closeAction.execute();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Configuration config = context.getConfiguration();
|
||||
|
||||
context.setFullscreen(config.getStartFullscreen());
|
||||
context.fullscreenProperty().addListener((observable, oldValue, newValue) -> {
|
||||
setFullscreen(newValue);
|
||||
});
|
||||
|
||||
view.setOnClose(this::closeWindow);
|
||||
view.setOnShown(this::onViewShown);
|
||||
view.setOnKeyEvent(this::keyEvent);
|
||||
|
||||
context.getEventBus().register(this);
|
||||
|
||||
if (config.getCheckNewVersion()) {
|
||||
// Check for a new version.
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
VersionChecker versionChecker = new VersionChecker();
|
||||
|
||||
if (versionChecker.newVersionAvailable()) {
|
||||
GitHubRelease release = versionChecker.getLatestRelease();
|
||||
|
||||
VersionInfo version = new VersionInfo();
|
||||
version.downloadUrl = versionChecker.getMatchingAssetUrl();
|
||||
version.htmlUrl = release.getUrl();
|
||||
version.published = release.getPublishedAt();
|
||||
version.version = release.getTagName();
|
||||
|
||||
context.getEventBus().post(new NewVersionCommand(
|
||||
NewVersionPresenter.class, version));
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
})
|
||||
.exceptionally(throwable -> {
|
||||
logException(throwable, "Check for new version failed");
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
if (shutdownHandlers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Runnable shutdownLoop = () -> {
|
||||
for (ShutdownHandler handler : shutdownHandlers) {
|
||||
try {
|
||||
if (!handler.execute()) {
|
||||
// Abort shutdown process.
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logException(e, "Execute shutdown handler failed");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Thread thread = new Thread(shutdownLoop, "ShutdownHandler-Thread");
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onCommand(final CloseApplicationCommand command) {
|
||||
closeWindow();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onCommand(final ClosePresenterCommand command) {
|
||||
destroyHandler(command.getPresenterClass());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public <T extends Presenter<?>> void onCommand(final ShowPresenterCommand<T> command) {
|
||||
T presenter = createPresenter(command.getPresenterClass());
|
||||
|
||||
try {
|
||||
command.execute(presenter);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logException(e, "Execute command failed");
|
||||
}
|
||||
|
||||
display(presenter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addShutdownHandler(ShutdownHandler handler) {
|
||||
requireNonNull(handler, "ShutdownHandler must not be null.");
|
||||
|
||||
if (!shutdownHandlers.contains(handler)) {
|
||||
shutdownHandlers.add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeShutdownHandler(ShutdownHandler handler) {
|
||||
requireNonNull(handler, "ShutdownHandler must not be null.");
|
||||
|
||||
shutdownHandlers.remove(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showView(View childView, ViewLayer layer) {
|
||||
if (layer == ViewLayer.NotificationPopup) {
|
||||
popupManager.show(view, (NotificationPopupView) childView);
|
||||
}
|
||||
else {
|
||||
view.showView(childView, layer);
|
||||
|
||||
setViewShown(getViewInterface(childView.getClass()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(Presenter<?> presenter) {
|
||||
requireNonNull(presenter);
|
||||
|
||||
Presenter<?> cachedPresenter = findCachedContext(presenter);
|
||||
|
||||
try {
|
||||
if (nonNull(cachedPresenter)) {
|
||||
View view = cachedPresenter.getView();
|
||||
|
||||
if (nonNull(view)) {
|
||||
BooleanProperty property = getViewVisibleProperty(getViewInterface(view.getClass()));
|
||||
|
||||
if (property.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
showView(view, cachedPresenter.getViewLayer());
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (presenter.getClass().equals(NotificationPresenter.class) &&
|
||||
nonNull(notificationPresenter) &&
|
||||
!notificationPresenter.equals(presenter)) {
|
||||
hideWaitingNotification();
|
||||
}
|
||||
|
||||
presenter.initialize();
|
||||
|
||||
View view = presenter.getView();
|
||||
|
||||
if (nonNull(view)) {
|
||||
BooleanProperty property = getViewVisibleProperty(getViewInterface(view.getClass()));
|
||||
|
||||
if (property.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
presenter.setOnClose(() -> destroy(presenter));
|
||||
|
||||
showView(view, presenter.getViewLayer());
|
||||
|
||||
addContext(presenter);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
handleException(e, "Show view failed", "error", "generic.error");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(Presenter<?> presenter) {
|
||||
requireNonNull(presenter);
|
||||
|
||||
View childView = presenter.getView();
|
||||
|
||||
try {
|
||||
view.removeView(childView, presenter.getViewLayer());
|
||||
|
||||
setViewHidden(getViewInterface(childView.getClass()));
|
||||
|
||||
if (!presenter.cache()) {
|
||||
presenter.destroy();
|
||||
|
||||
removeContext(presenter);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
handleException(e, "Destroy view failed", "error", "generic.error");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeWindow() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFullscreen(boolean enable) {
|
||||
view.setFullscreen(enable);
|
||||
}
|
||||
|
||||
private void addContext(Presenter<?> presenter) {
|
||||
requireNonNull(context);
|
||||
|
||||
if (!contexts.contains(presenter)) {
|
||||
contexts.add(presenter);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeContext(Presenter<?> presenter) {
|
||||
requireNonNull(presenter);
|
||||
|
||||
contexts.remove(presenter);
|
||||
}
|
||||
|
||||
private Presenter<?> findCachedContext(Presenter<?> presenter) {
|
||||
requireNonNull(presenter);
|
||||
|
||||
for (Presenter<?> p : contexts) {
|
||||
if ((presenter.equals(p) || presenter.getClass() == p.getClass()) && p.cache()) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean keyEvent(KeyEvent event) {
|
||||
Predicate<KeyEvent> action = shortcutMap.get(event);
|
||||
|
||||
if (nonNull(action)) {
|
||||
return action.test(event);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private BooleanProperty getViewVisibleProperty(Class<? extends View> viewClass) {
|
||||
BooleanProperty property = viewMap.get(viewClass);
|
||||
|
||||
if (isNull(property)) {
|
||||
property = new BooleanProperty(false);
|
||||
property.addListener((observable, oldValue, newValue) -> {
|
||||
context.getEventBus().post(new ViewVisibleEvent(viewClass, newValue));
|
||||
});
|
||||
|
||||
viewMap.put(viewClass, property);
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
private void destroyHandler(Class<? extends Presenter<?>> presenterClass) {
|
||||
for (Presenter<?> presenter : contexts) {
|
||||
if (getViewInterface(presenter.getClass()) == presenterClass) {
|
||||
destroy(presenter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onViewShown() {
|
||||
|
||||
}
|
||||
|
||||
private boolean closeView(KeyEvent event) {
|
||||
if (!contexts.isEmpty()) {
|
||||
Presenter<?> presenter = contexts.get(contexts.size() - 1);
|
||||
View view = presenter.getView();
|
||||
|
||||
BooleanProperty property = getViewVisibleProperty(getViewInterface(view.getClass()));
|
||||
|
||||
if (property.get()) {
|
||||
presenter.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void registerShortcut(Shortcut shortcut, Predicate<KeyEvent> action) {
|
||||
shortcutMap.put(shortcut.getKeyEvent(), action);
|
||||
}
|
||||
|
||||
private void showWaitingNotification(String title, String message) {
|
||||
if (context.getDictionary().contains(title)) {
|
||||
title = context.getDictionary().get(title);
|
||||
}
|
||||
if (context.getDictionary().contains(message)) {
|
||||
message = context.getDictionary().get(message);
|
||||
}
|
||||
|
||||
notificationPresenter = createPresenter(NotificationPresenter.class);
|
||||
notificationPresenter.setMessage(message);
|
||||
notificationPresenter.setNotificationType(NotificationType.WAITING);
|
||||
notificationPresenter.setTitle(title);
|
||||
|
||||
display(notificationPresenter);
|
||||
}
|
||||
|
||||
private void hideWaitingNotification() {
|
||||
if (nonNull(notificationPresenter)) {
|
||||
destroy(notificationPresenter);
|
||||
notificationPresenter = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void setViewHidden(Class<? extends View> viewClass) {
|
||||
BooleanProperty property = getViewVisibleProperty(viewClass);
|
||||
property.set(false);
|
||||
}
|
||||
|
||||
private void setViewShown(Class<? extends View> viewClass) {
|
||||
BooleanProperty property = getViewVisibleProperty(viewClass);
|
||||
property.set(true);
|
||||
}
|
||||
|
||||
private <T extends Presenter<?>> T createPresenter(Class<T> pClass) {
|
||||
return contextFactory.getInstance(pClass);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<? extends View> getViewInterface(Class<?> cls) {
|
||||
while (nonNull(cls)) {
|
||||
final Class<?>[] interfaces = cls.getInterfaces();
|
||||
|
||||
for (final Class<?> i : interfaces) {
|
||||
if (i == View.class) {
|
||||
return (Class<? extends View>) cls;
|
||||
}
|
||||
|
||||
return getViewInterface(i);
|
||||
}
|
||||
|
||||
cls = cls.getSuperclass();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package moodle.sync.presenter;
|
||||
|
||||
import org.lecturestudio.core.app.ApplicationContext;
|
||||
import org.lecturestudio.core.util.ShutdownHandler;
|
||||
|
||||
public class SaveConfigurationHandler extends ShutdownHandler {
|
||||
|
||||
private final ApplicationContext context;
|
||||
|
||||
|
||||
public SaveConfigurationHandler(ApplicationContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute() {
|
||||
try {
|
||||
context.saveConfiguration();
|
||||
}
|
||||
catch (Exception e) {
|
||||
logException(e, "Save configuration failed");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package moodle.sync.presenter;
|
||||
|
||||
import moodle.sync.core.config.DefaultConfiguration;
|
||||
import moodle.sync.core.config.MoodleSyncConfiguration;
|
||||
import moodle.sync.view.SettingsView;
|
||||
import moodle.sync.core.web.service.MoodleService;
|
||||
|
||||
import org.lecturestudio.core.app.ApplicationContext;
|
||||
import org.lecturestudio.core.app.LocaleProvider;
|
||||
import org.lecturestudio.core.presenter.Presenter;
|
||||
import org.lecturestudio.core.view.DirectoryChooserView;
|
||||
import org.lecturestudio.core.view.ViewContextFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
|
||||
import static java.util.Objects.nonNull;
|
||||
|
||||
/**
|
||||
* Class defining the logic of the "settings-page".
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class SettingsPresenter extends Presenter<SettingsView> {
|
||||
|
||||
private final ViewContextFactory viewFactory;
|
||||
|
||||
//Used MoodleService for executing Web Service API-Calls.
|
||||
private final MoodleService moodleService;
|
||||
|
||||
@Inject
|
||||
SettingsPresenter(ApplicationContext context, SettingsView view,
|
||||
ViewContextFactory viewFactory, MoodleService moodleService) {
|
||||
super(context, view);
|
||||
|
||||
this.moodleService = moodleService;
|
||||
this.viewFactory = viewFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() throws Exception{
|
||||
MoodleSyncConfiguration config = (MoodleSyncConfiguration) context.getConfiguration();
|
||||
LocaleProvider localeProvider = new LocaleProvider();
|
||||
|
||||
//Initialising all functions of the "settings-page" with the help of the configuration.
|
||||
view.setOnExit(this::close);
|
||||
view.setLocales(localeProvider.getLocales());
|
||||
view.setLocale(config.localeProperty());
|
||||
view.setMoodleField(config.moodleUrlProperty());
|
||||
view.setFormatsMoodle(config.formatsMoodleProperty());
|
||||
view.setFormatsFileserver(config.formatsFileserverProperty());
|
||||
view.setMoodleToken(config.moodleTokenProperty());
|
||||
view.setSyncRootPath(config.syncRootPathProperty());
|
||||
view.setSelectSyncRootPath(this::selectSyncPath);
|
||||
view.setFtpField(config.FileserverProperty());
|
||||
view.setFtpPort(config.portFileserverProperty());
|
||||
view.setFtpUser(config.userFileserverProperty());
|
||||
view.setFtpPassword(config.passwordFileserverProperty());
|
||||
view.setShowUnknownFormats(config.showUnknownFormatsProperty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to close the "settings-page".
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
MoodleSyncConfiguration config = (MoodleSyncConfiguration) context.getConfiguration();
|
||||
//Reconstruct the MoodleService with the new settings.
|
||||
moodleService.setApiUrl(config.getMoodleUrl());
|
||||
super.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing the functionality to choose a Root-Directory.
|
||||
*/
|
||||
private void selectSyncPath() {
|
||||
MoodleSyncConfiguration config = (MoodleSyncConfiguration) context.getConfiguration();
|
||||
String syncPath = config.getSyncRootPath();
|
||||
//Check whether a default path should be used to prevent unwanted behavior.
|
||||
if (syncPath == null || syncPath.isEmpty() || syncPath.isBlank()) {
|
||||
DefaultConfiguration defaultConfiguration = new DefaultConfiguration();
|
||||
syncPath = defaultConfiguration.getSyncRootPath();
|
||||
}
|
||||
File initDirectory = new File(syncPath);
|
||||
DirectoryChooserView dirChooser = viewFactory.createDirectoryChooserView();
|
||||
dirChooser.setInitialDirectory(initDirectory);
|
||||
File selectedFile = dirChooser.show(view);
|
||||
|
||||
if (nonNull(selectedFile)) {
|
||||
config.setSyncRootPath(selectedFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,598 @@
|
|||
package moodle.sync.presenter;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import moodle.sync.core.config.DefaultConfiguration;
|
||||
import moodle.sync.core.config.MoodleSyncConfiguration;
|
||||
import moodle.sync.core.model.json.*;
|
||||
import moodle.sync.core.fileserver.FileServerClientFTP;
|
||||
import moodle.sync.core.fileserver.FileServerFile;
|
||||
import moodle.sync.core.model.json.Module;
|
||||
import moodle.sync.util.VerifyDataService;
|
||||
import moodle.sync.javafx.model.ReturnValue;
|
||||
import moodle.sync.presenter.command.ShowSettingsCommand;
|
||||
import moodle.sync.util.FileService;
|
||||
import moodle.sync.core.util.FileWatcherService.FileEvent;
|
||||
import moodle.sync.core.util.FileWatcherService.FileListener;
|
||||
import moodle.sync.core.util.FileWatcherService.FileWatcher;
|
||||
import moodle.sync.core.util.MoodleAction;
|
||||
import moodle.sync.javafx.model.TimeDateElement;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
|
||||
import moodle.sync.util.SetModuleService;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.lecturestudio.core.app.ApplicationContext;
|
||||
import org.lecturestudio.core.beans.BooleanProperty;
|
||||
import org.lecturestudio.core.presenter.Presenter;
|
||||
|
||||
import org.lecturestudio.core.view.NotificationType;
|
||||
import org.lecturestudio.core.view.ViewContextFactory;
|
||||
|
||||
import moodle.sync.view.StartView;
|
||||
import moodle.sync.core.web.service.MoodleService;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
|
||||
/**
|
||||
* Class defining the logic of the "start-page".
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class StartPresenter extends Presenter<StartView> implements FileListener {
|
||||
|
||||
private final ViewContextFactory viewFactory;
|
||||
|
||||
//Used MoodleService for executing Web Service API-Calls.
|
||||
private final MoodleService moodleService;
|
||||
|
||||
//Configuration providing the settings.
|
||||
private final MoodleSyncConfiguration config;
|
||||
|
||||
//Providing the content of a course. Used for the section-combobox.
|
||||
private List<Section> courseContent;
|
||||
|
||||
//List representing the actual courseData with the planned/possible changes.
|
||||
private ObservableList<syncTableElement> courseData;
|
||||
|
||||
//FileWatcher for the current course's directory.
|
||||
private FileWatcher watcher;
|
||||
|
||||
//Saves if the fileserver is required.
|
||||
private boolean fileServerRequired;
|
||||
|
||||
//Used fileServerClient implementation.
|
||||
private FileServerClientFTP fileClient;
|
||||
|
||||
//Select all possible changes.
|
||||
private BooleanProperty selectAll;
|
||||
|
||||
//User's moodle token.
|
||||
private String token;
|
||||
|
||||
//The moodle plattforms url.
|
||||
private String url;
|
||||
|
||||
//Selected moodle course.
|
||||
private Course course;
|
||||
|
||||
//Selected moodle section.
|
||||
private Section section;
|
||||
|
||||
|
||||
@Inject
|
||||
StartPresenter(ApplicationContext context, StartView view, ViewContextFactory viewFactory,
|
||||
MoodleService moodleService) {
|
||||
super(context, view);
|
||||
this.viewFactory = viewFactory;
|
||||
this.moodleService = moodleService;
|
||||
this.config = (MoodleSyncConfiguration) context.getConfiguration();
|
||||
this.selectAll = new BooleanProperty(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
//Initialising all functions of the "start-page" with the help of the configuration.
|
||||
String syncPath = config.getSyncRootPath();
|
||||
//Check whether a default path should be used to prevent unwanted behavior.
|
||||
if (!VerifyDataService.validateString(syncPath)) {
|
||||
DefaultConfiguration defaultConfiguration = new DefaultConfiguration();
|
||||
config.setSyncRootPath(defaultConfiguration.getSyncRootPath());
|
||||
}
|
||||
|
||||
view.setOnUpdate(this::updateCourses);
|
||||
view.setOnSync(this::onSync);
|
||||
view.setOnSettings(this::onSettings);
|
||||
view.setCourse(config.recentCourseProperty());
|
||||
view.setCourses(courses());
|
||||
view.setSection(config.recentSectionProperty());
|
||||
view.setSections(sections());
|
||||
view.setOnCourseChanged(this::onCourseChanged);
|
||||
view.setData(setData());
|
||||
view.setOnFolder(this::openCourseDirectory);
|
||||
view.setSelectAll(selectAll);
|
||||
|
||||
//Display the course-sections after Moodle-course is choosen.
|
||||
config.recentCourseProperty().addListener((observable, oldCourse, newCourse) -> {
|
||||
course = config.getRecentCourse(); //Todo hier schauen ob das ausreicht!
|
||||
config.setRecentSection(null);
|
||||
view.setData(setData());
|
||||
});
|
||||
|
||||
config.recentSectionProperty().addListener((observable, oldSection, newSection) -> {
|
||||
section = config.getRecentSection();
|
||||
view.setData(setData());
|
||||
});
|
||||
|
||||
config.moodleUrlProperty().addListener((observable, oldUrl, newUrl) -> {
|
||||
config.setRecentCourse(null);
|
||||
config.setRecentSection(null);
|
||||
course = null;
|
||||
section = null;
|
||||
});
|
||||
|
||||
selectAll.addListener((observable, oldUrl, newUrl) -> {
|
||||
if (newUrl) {
|
||||
for (syncTableElement elem : courseData) {
|
||||
if (elem.isSelectable()) {
|
||||
elem.selectedProperty().setValue(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (syncTableElement elem : courseData) {
|
||||
if (elem.isSelectable()) {
|
||||
elem.selectedProperty().setValue(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onCourseChanged(Course course) {
|
||||
view.setSections(sections());
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an API-call to get users Moodle-courses.
|
||||
*
|
||||
* @return list containing users Moodle-courses.
|
||||
*/
|
||||
private List<Course> courses() {
|
||||
url = config.getMoodleUrl();
|
||||
//Security checks to prevent unwanted behaviour.
|
||||
//Todo überprüfen neu
|
||||
if (!VerifyDataService.validateString(url) || !VerifyDataService.validateString(token)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<Course> courses = List.of();
|
||||
try {
|
||||
courses = moodleService.getEnrolledCourses(token, moodleService.getUserId(token));
|
||||
}
|
||||
catch (Exception e) {
|
||||
logException(e, "Sync failed");
|
||||
showNotification(NotificationType.ERROR, "start.sync.error.title", "start.sync.error.invalidurl.message");
|
||||
config.setRecentCourse(null);
|
||||
course = null;
|
||||
}
|
||||
|
||||
//Do not show Moodle-courses which are already over.
|
||||
if (!courses.isEmpty()) {
|
||||
courses.removeIf(item -> (item.getEnddate() != 0 && (item.getEnddate() < System.currentTimeMillis()
|
||||
/1000)));
|
||||
}
|
||||
//Sort Courses if Possible
|
||||
/*if(courses.get(0).getShortname().contains("SoSe") || courses.get(0).getShortname().contains("WiSe")){
|
||||
if(courses.get(0).getShortname().contains("20")){
|
||||
|
||||
}
|
||||
}*/
|
||||
return courses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an API-call to get a choosen Moodle-courses course-sections.
|
||||
*
|
||||
* @return list containing course-sections.
|
||||
*/
|
||||
private List<Section> sections() {
|
||||
if (course == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
List<Section> content = moodleService.getCourseContent(token, course.getId());
|
||||
content.add(0, new Section(-2, this.context.getDictionary().get("start.sync.showall"), 1, "all", -1, -1,
|
||||
-1, true, null));
|
||||
courseContent = content;
|
||||
return content;
|
||||
}
|
||||
catch (Exception e) {
|
||||
logException(e, "Sync failed");
|
||||
showNotification(NotificationType.ERROR, "start.sync.error.title", "start.sync.error.invalidurl.message");
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
private void onSettings() {
|
||||
context.getEventBus().post(new ShowSettingsCommand(this::refreshCourseList));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts the sync-process.
|
||||
*/
|
||||
private void onSync() {
|
||||
//Serveral security checks to prevent unwanted behaviour.
|
||||
if (config.getRecentCourse() == null) {
|
||||
showNotification(NotificationType.ERROR, "start.sync.error.title", "start.sync.error.course.message");
|
||||
return;
|
||||
}
|
||||
if (!Files.isDirectory(Paths.get(config.getSyncRootPath()))) {
|
||||
showNotification(NotificationType.ERROR, "start.sync.error.title", "start.sync.error.path.message");
|
||||
return;
|
||||
}
|
||||
for (syncTableElement courseData : courseData) {
|
||||
try {
|
||||
if (courseData.isSelected()) {
|
||||
if (courseData.getModuleType().equals("resource")) {
|
||||
SetModuleService.publishResource(moodleService, courseData, course, url, token);
|
||||
}
|
||||
else if (courseData.getAction() == MoodleAction.FTPUpload) {
|
||||
SetModuleService.publishFileserverResource(moodleService, courseData, course, token);
|
||||
}
|
||||
else if (courseData.getAction() != MoodleAction.UploadSection) {
|
||||
SetModuleService.moveResource(moodleService, courseData, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logException(e, "Sync failed");
|
||||
|
||||
showNotification(NotificationType.ERROR, "start.sync.error.title",
|
||||
MessageFormat.format(context.getDictionary().get("start.sync.error.upload.message"),
|
||||
courseData.getModuleName()));
|
||||
}
|
||||
}
|
||||
//Adding of new sections at the end of the sync-process to prevent new section-numbers
|
||||
for (syncTableElement courseData : courseData) {
|
||||
if (courseData.getAction() == MoodleAction.UploadSection && courseData.isSelected()) {
|
||||
//Logic for Section-Upload
|
||||
try {
|
||||
SetModuleService.createSection(moodleService, courseData, course, token);
|
||||
} catch (Exception e) {
|
||||
logException(e, "Sync failed");
|
||||
|
||||
showNotification(NotificationType.ERROR, "start.sync.error.title", "start.sync.error.upload" +
|
||||
".message");
|
||||
}
|
||||
}
|
||||
}
|
||||
updateCourses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to update the displayed Moodle-Courses.
|
||||
*/
|
||||
private void updateCourses() {
|
||||
view.setData(setData());
|
||||
}
|
||||
|
||||
private void refreshCourseList() {
|
||||
view.setCourses(courses());
|
||||
}
|
||||
|
||||
private ObservableList<syncTableElement> setData() {
|
||||
token = config.getMoodleToken();
|
||||
if (isNull(courseContent) || isNull(course))
|
||||
return FXCollections.observableArrayList();
|
||||
|
||||
try {
|
||||
if (watcher != null)
|
||||
watcher.close();
|
||||
} catch (Exception e) {
|
||||
logException(e, "Sync failed");
|
||||
showNotification(NotificationType.ERROR, "start.sync.error.title", "start.sync.error.message");
|
||||
}
|
||||
|
||||
List<Path> sectionList = List.of();
|
||||
|
||||
try {
|
||||
//If no section is selected, or "all" are selected, directories are checked and coursecontent is set.
|
||||
if (isNull(section) || section.getId() == -2) {
|
||||
//Check if course-folder exists, otherwise create one.
|
||||
Path courseDirectory =
|
||||
Paths.get(config.getSyncRootPath() + "/" + course.getDisplayname());
|
||||
FileService.directoryManager(courseDirectory);
|
||||
sectionList = FileService.getPathsInDirectory(courseDirectory);
|
||||
courseContent = sections();
|
||||
} //Handling if a specific section is chosen.
|
||||
else{
|
||||
courseContent.clear();
|
||||
courseContent.add(moodleService.getCourseContentSection(token, course.getId(),
|
||||
section.getId()).get(0));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logException(e, "Sync failed");
|
||||
showNotification(NotificationType.ERROR, "start.sync.error.title", "start.sync.error.message");
|
||||
}
|
||||
|
||||
ObservableList<syncTableElement> data = FXCollections.observableArrayList();
|
||||
|
||||
for (Section section : courseContent) {
|
||||
try {
|
||||
if (section.getId() != -2) {
|
||||
String sectionName = section.getName();
|
||||
int sectionNum = section.getSection();
|
||||
int sectionId = section.getId();
|
||||
|
||||
data.add(new syncTableElement(sectionName, sectionId, sectionNum, sectionId, data.size(),
|
||||
section.getSummary(), false, false, MoodleAction.ExistingSection,
|
||||
section.getVisible() == 1));
|
||||
|
||||
sectionList = FileService.formatSectionFolder(sectionList, section);
|
||||
|
||||
//Section-directory is eventually created.
|
||||
Path execute =
|
||||
Paths.get(config.getSyncRootPath() + "/" + course.getDisplayname() + "/" + section.getSection() + "_" + sectionName);
|
||||
FileService.directoryManager(execute);
|
||||
|
||||
//Initialize the fileServerRequired variable.
|
||||
fileServerRequired = false;
|
||||
List<FileServerFile> files = List.of();
|
||||
|
||||
//Watcher is added to choosen course-/ or section-directory.
|
||||
watcher = new FileWatcher(new File(execute.toString()));
|
||||
watcher.addListener(this);
|
||||
watcher.watch();
|
||||
|
||||
//Categorize Moodle-, FileServer- and local-files.
|
||||
try {
|
||||
List<Path> fileList = FileService.getPathsInDirectory(execute);
|
||||
for (Module module : section.getModules()) {
|
||||
if (module.getModname().equals("resource")) {
|
||||
ReturnValue resource = FileService.findModuleInFiles(fileList, module, sectionNum,
|
||||
sectionId, data.size());
|
||||
data.add(resource.getElement());
|
||||
fileList = resource.getFileList();
|
||||
}
|
||||
else if (module.getModname().equals("url") && !config.getFormatsFileserver().isEmpty()) {
|
||||
boolean found = false;
|
||||
for (Path file : fileList) {
|
||||
if (module.getName().equals(file.getFileName().toString())) {
|
||||
found = true;
|
||||
//File is found in the moodle-course
|
||||
//Initialize the fileServer files.
|
||||
if (!fileServerRequired) {
|
||||
files = provideFileserverFiles(/*config.getRecentSection().getName()
|
||||
*/ ""); //ToDo -> If there should be support for different upload-sections.
|
||||
}
|
||||
for (FileServerFile fileServerFile : files) {
|
||||
if (fileServerFile.getFilename().equals(file.getFileName().toString())) {
|
||||
//File additionally uploaded to fileserver
|
||||
if (fileServerFile.getLastTimeModified() < Files.getLastModifiedTime(file).toMillis()) {
|
||||
//File not up-to-date at fileserver
|
||||
if (module.getAvailability() != null) {
|
||||
var JsonB = new JsonConfigProvider().getContext(null);
|
||||
JsonB.fromJson(module.getAvailability(),
|
||||
ModuleAvailability.class);
|
||||
LocalDateTime time =
|
||||
LocalDateTime.ofInstant(Instant.ofEpochMilli(JsonB.fromJson(module.getAvailability().
|
||||
replaceAll("\\\\", ""), ModuleAvailability.class).getTimeDateCondition().getT() * 1000L),
|
||||
ZoneId.systemDefault());
|
||||
data.add(new syncTableElement(module.getName(),
|
||||
module.getId(), sectionNum, sectionId, data.size(),
|
||||
module.getModname(), file, true, false,
|
||||
MoodleAction.FTPSynchronize,
|
||||
getPriorityVisibility(module.getVisible() == 1,
|
||||
JsonB.fromJson(module.getAvailability().replaceAll("\\\\", ""),
|
||||
ModuleAvailability.class).getConditionVisibility()),
|
||||
new TimeDateElement(time.toLocalDate(), time.toLocalTime()), module.getId()));
|
||||
} else {
|
||||
data.add(new syncTableElement(module.getName(),
|
||||
module.getId(), sectionNum, sectionId, data.size(),
|
||||
module.getModname(), file, true, false,
|
||||
MoodleAction.FTPSynchronize, module.getVisible() == 1));
|
||||
}
|
||||
fileList.remove(file);
|
||||
break;
|
||||
} else {
|
||||
//File up to date at the fileserver
|
||||
if (module.getAvailability() != null) {
|
||||
var JsonB = new JsonConfigProvider().getContext(null);
|
||||
JsonB.fromJson(module.getAvailability(),
|
||||
ModuleAvailability.class);
|
||||
LocalDateTime time =
|
||||
LocalDateTime.ofInstant(Instant.ofEpochMilli(JsonB.fromJson(module.getAvailability().replaceAll("\\\\", ""),
|
||||
ModuleAvailability.class).getTimeDateCondition().getT() * 1000L), ZoneId.systemDefault());
|
||||
data.add(new syncTableElement(module.getName(),
|
||||
module.getId(), sectionNum, sectionId, data.size(),
|
||||
module.getModname(), file, false, false,
|
||||
MoodleAction.ExistingFile,
|
||||
getPriorityVisibility(module.getVisible() == 1,
|
||||
JsonB.fromJson(module.getAvailability().replaceAll("\\\\", ""), ModuleAvailability.class)
|
||||
.getConditionVisibility()), new TimeDateElement(time.toLocalDate(), time.toLocalTime())));
|
||||
} else {
|
||||
data.add(new syncTableElement(module.getName(),
|
||||
module.getId(), sectionNum, sectionId, data.size(),
|
||||
module.getModname(), file, false, false,
|
||||
MoodleAction.ExistingFile, module.getVisible() == 1));
|
||||
}
|
||||
fileList.remove(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
data.add(new syncTableElement(module.getName(), module.getId(), sectionNum,
|
||||
sectionId, data.size(), module.getModname(), false, false,
|
||||
MoodleAction.NotLocalFile, module.getVisible() == 1));
|
||||
}
|
||||
} else {
|
||||
//Other modules which are not "url" or "resource".
|
||||
data.add(new syncTableElement(module.getName(), module.getId(), sectionNum, sectionId
|
||||
, data.size(), module.getModname(), false, false, MoodleAction.NotLocalFile,
|
||||
module.getVisible() == 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (fileList.size() != 0) {
|
||||
//All files inside here should be uploaded, if DataType is known.
|
||||
for (Path path : fileList) {
|
||||
if (contains(config.getFormatsMoodle().split(","),
|
||||
FilenameUtils.getExtension(path.getFileName().toString()))) {
|
||||
data.add(new syncTableElement(path.getFileName().toString(), -1, sectionNum,
|
||||
sectionId, data.size(), "resource", path, true, false,
|
||||
MoodleAction.MoodleUpload, true));
|
||||
}
|
||||
//More Complicated: all Files for the Fileserver-Upload (if new upload oder update)
|
||||
// are found here:
|
||||
else if ((contains(config.getFormatsFileserver().split(","),
|
||||
FilenameUtils.getExtension(path.getFileName().toString()))) && !config.getFormatsFileserver().isBlank()) {
|
||||
//Local files which are not uploaded to moodle.
|
||||
if (!fileServerRequired) {
|
||||
files = provideFileserverFiles(/*config.getRecentSection().getName()*/ "");
|
||||
//ToDo -> If there should be support for different upload-sections.
|
||||
}
|
||||
//Array files containing all files uploaded to the fileserver (name and date).
|
||||
for (FileServerFile fileServerFile : files) {
|
||||
if (fileServerFile.getFilename().equals(path.getFileName())) {
|
||||
if (fileServerFile.getLastTimeModified() < Files.getLastModifiedTime(path).toMillis()) {
|
||||
//File to up-to-date
|
||||
data.add(new syncTableElement(path.getFileName().toString(), -1,
|
||||
sectionNum, sectionId, data.size(), "url", path, true, false,
|
||||
MoodleAction.FTPUpload, true));
|
||||
} else {
|
||||
data.add(new syncTableElement(path.getFileName().toString(), -1,
|
||||
sectionNum, sectionId, data.size(), "url", path, true, false,
|
||||
MoodleAction.FTPLink, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
//File not on moodle nor on fileserver
|
||||
data.add(new syncTableElement(path.getFileName().toString(), -1, sectionNum,
|
||||
sectionId, data.size(), "url", path, true, false, MoodleAction.FTPUpload,
|
||||
true));
|
||||
|
||||
}
|
||||
else {
|
||||
data.add(new syncTableElement(path.getFileName().toString(), -1, sectionNum,
|
||||
sectionId, data.size(), "resource", path, false, false,
|
||||
MoodleAction.DatatypeNotKnown, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
logException(e, "Sync failed");
|
||||
showNotification(NotificationType.ERROR, "start.sync.error.title", "start.sync.error.message");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logException(e, "Sync failed");
|
||||
showNotification(NotificationType.ERROR, "start.sync.error.title", "start.sync.error.message");
|
||||
}
|
||||
}
|
||||
|
||||
if (!sectionList.isEmpty()) {
|
||||
for (Path elem : sectionList) {
|
||||
data.add(new syncTableElement(elem.getFileName().toString(), -1, -1, -1, data.size(), "section", elem
|
||||
, true, false, MoodleAction.UploadSection, true));
|
||||
}
|
||||
}
|
||||
|
||||
courseData = data;
|
||||
|
||||
//Add FileWatcher
|
||||
watcher = new FileWatcher(new File(config.getSyncRootPath() + "/" + course.getDisplayname()));
|
||||
watcher.addListener(this);
|
||||
watcher.watch();
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
/*private void deleteModule(syncTableElement element){
|
||||
if(element.getAction() == MoodleAction.ExistingFile || element.getAction() == MoodleAction.NotLocalFile ||
|
||||
element.getAction() == MoodleAction.MoodleSynchronize || element.getAction() == MoodleAction.FTPSynchronize){
|
||||
if(element.getDelete()){
|
||||
//Hier nochmal Nutzerdialog
|
||||
context.getEventBus().post(new ShowPresenterCommand<>(ConfirmDeleteModulePresenter.class));
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void onCreated(FileEvent event) {
|
||||
view.setData(setData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(FileEvent event) {
|
||||
view.setData(setData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleted(FileEvent event) {
|
||||
view.setData(setData());
|
||||
}
|
||||
|
||||
private Boolean getPriorityVisibility(Boolean visible, Boolean availability) {
|
||||
if (!visible || !availability) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void openCourseDirectory() {
|
||||
Desktop desktop = Desktop.getDesktop();
|
||||
try {
|
||||
File dirToOpen = new File(config.getSyncRootPath() + "/" + course.getDisplayname());
|
||||
desktop.open(dirToOpen);
|
||||
} catch (Throwable e) {
|
||||
logException(e, "Sync failed");
|
||||
showNotification(NotificationType.ERROR, "start.sync.error.title", "start.sync.error.path.unknown.message");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean contains(final String[] arr, final String key) {
|
||||
return Arrays.asList(arr).contains(key);
|
||||
}
|
||||
|
||||
private List<FileServerFile> provideFileserverFiles(String pathname) throws Exception {
|
||||
List<FileServerFile> files = List.of();
|
||||
if (!VerifyDataService.validateString(config.getFileserver()) || !VerifyDataService.validateString(config.getUserFileserver()) ||
|
||||
!VerifyDataService.validateString(config.getPasswordFileserver())) {
|
||||
showNotification(NotificationType.ERROR, "start.sync.error.title", "start.sync.error.fileserver1.message");
|
||||
} else {
|
||||
try {
|
||||
fileClient = new FileServerClientFTP(config);
|
||||
fileClient.connect();
|
||||
files = fileClient.getFiles(/*config.getRecentSection().getName()*/ ""); //ToDo -> If there should be
|
||||
// support for different upload-sections.
|
||||
fileClient.disconnect();
|
||||
} catch (Exception e) {
|
||||
logException(e, "Sync failed");
|
||||
showNotification(NotificationType.ERROR, "start.sync.error.title", "start.sync.error.fileserver2.message");
|
||||
}
|
||||
}
|
||||
fileServerRequired = true;
|
||||
|
||||
return files;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package moodle.sync.presenter.command;
|
||||
|
||||
import moodle.sync.presenter.SettingsPresenter;
|
||||
import org.lecturestudio.core.presenter.command.ShowPresenterCommand;
|
||||
import org.lecturestudio.core.view.Action;
|
||||
|
||||
public class ShowSettingsCommand extends ShowPresenterCommand<SettingsPresenter> {
|
||||
private final Action closeAction;
|
||||
|
||||
|
||||
public ShowSettingsCommand(Action closeAction) {
|
||||
super(SettingsPresenter.class);
|
||||
|
||||
this.closeAction = closeAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(SettingsPresenter presenter) {
|
||||
presenter.setOnClose(closeAction);
|
||||
}
|
||||
}
|
188
moodle-sync-fx/src/main/java/moodle/sync/util/FileService.java
Normal file
188
moodle-sync-fx/src/main/java/moodle/sync/util/FileService.java
Normal file
|
@ -0,0 +1,188 @@
|
|||
package moodle.sync.util;
|
||||
|
||||
import moodle.sync.javafx.model.ReturnValue;
|
||||
import moodle.sync.javafx.model.TimeDateElement;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
import moodle.sync.core.model.json.JsonConfigProvider;
|
||||
import moodle.sync.core.model.json.Module;
|
||||
import moodle.sync.core.model.json.ModuleAvailability;
|
||||
import moodle.sync.core.model.json.Section;
|
||||
import moodle.sync.core.util.MoodleAction;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryIteratorException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class implementing several methods in terms of file handling and comparison.
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public class FileService {
|
||||
|
||||
|
||||
/**
|
||||
* Secures that a directory given in a given path exists. Therefore the directory could be created.
|
||||
*
|
||||
* @param p Path of the directory.
|
||||
*/
|
||||
public static void directoryManager(Path p) throws Exception {
|
||||
Files.createDirectories(p);
|
||||
}
|
||||
|
||||
|
||||
public static List<Path> formatSectionFolder(List<Path> sectionList, Section section) {
|
||||
int remove = -1;
|
||||
for (int i = 0; i < sectionList.size(); i++) {
|
||||
String[] sectionFolder = sectionList.get(i).getFileName().toString().split("_", 2);
|
||||
if (sectionFolder[sectionFolder.length - 1].equals(section.getName())) {
|
||||
File temp = new File(sectionList.get(i).toString());
|
||||
temp.renameTo(new File((sectionList.get(i).getParent().toString() + "/" + section.getSection() + "_" + section.getName())));
|
||||
remove = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (remove != -1) {
|
||||
sectionList.remove(remove);
|
||||
}
|
||||
return sectionList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtaining a list containing all paths inside a directory.
|
||||
*
|
||||
* @param p Path of the directory.
|
||||
* @return list of all paths inside the directory.
|
||||
*/
|
||||
public static List<Path> fileServerRequired(Path p) throws IOException {
|
||||
List<Path> result = new ArrayList<>();
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(p)) {
|
||||
for (Path entry : stream) {
|
||||
result.add(entry);
|
||||
}
|
||||
} catch (DirectoryIteratorException ex) {
|
||||
// I/O error encountered during the iteration, the cause is an IOException.
|
||||
throw ex.getCause();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static List<Path> getPathsInDirectory(Path p) throws IOException {
|
||||
List<Path> result = new ArrayList<>();
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(p)) {
|
||||
for (Path entry : stream) {
|
||||
//When an element is a directory, a recursive-call of this method is made.
|
||||
result.add(entry);
|
||||
}
|
||||
} catch (DirectoryIteratorException ex) {
|
||||
// I/O error encounted during the iteration, the cause is an IOException
|
||||
throw ex.getCause();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//Returns List with all Moodle-Moduls of a choosen Module-type
|
||||
|
||||
/**
|
||||
* Method to sort the Modules inside a Section by Module-type.
|
||||
*
|
||||
* @param type1 Module-type searched for.
|
||||
* @param section Section containing a list of Modules.
|
||||
* @return a list of Modules of the searched Module-type.
|
||||
*/
|
||||
public static List<Module> getModulesByType(String type1, Section section) {
|
||||
List<Module> modules = new ArrayList<>();
|
||||
for (int i = 0; i < section.getModules().size(); i++) {
|
||||
if (section.getModules().get(i).getModname().equals(type1)) {
|
||||
modules.add(section.getModules().get(i));
|
||||
}
|
||||
}
|
||||
return modules;
|
||||
}
|
||||
|
||||
public static ReturnValue findModuleInFiles(List<Path> fileList, Module module, int sectionNum, int sectionId,
|
||||
int position /* Substitute data.size()*/) throws Exception {
|
||||
syncTableElement element = null;
|
||||
boolean found = false;
|
||||
for (int i = 0; i < fileList.size(); i++) {
|
||||
if (fileList.get(i).getFileName().toString().equals(module.getContents().get(0).getFilename())) {
|
||||
long onlinemodified = module.getContents().get(0).getTimemodified() * 1000;
|
||||
long filemodified = Files.getLastModifiedTime(fileList.get(i)).toMillis();
|
||||
//Check if local file is newer.
|
||||
if (filemodified > onlinemodified) {
|
||||
found = true;
|
||||
if (module.getAvailability() != null) {
|
||||
var JsonB = new JsonConfigProvider().getContext(null);
|
||||
JsonB.fromJson(module.getAvailability(), ModuleAvailability.class);
|
||||
LocalDateTime time =
|
||||
LocalDateTime.ofInstant(Instant.ofEpochMilli(JsonB.fromJson(module.getAvailability().replaceAll("\\\\", ""),
|
||||
ModuleAvailability.class).getTimeDateCondition().getT() * 1000L), ZoneId.systemDefault());
|
||||
element = new syncTableElement(module.getName(), module.getId(), sectionNum, sectionId,
|
||||
position, module.getModname(), fileList.get(i), true, false,
|
||||
MoodleAction.MoodleSynchronize, getPriorityVisibility(module.getVisible() == 1,
|
||||
JsonB.fromJson(module.getAvailability().replaceAll("\\\\", ""),
|
||||
ModuleAvailability.class).getConditionVisibility()),
|
||||
new TimeDateElement(time.toLocalDate(), time.toLocalTime()), module.getId());
|
||||
} else {
|
||||
element = new syncTableElement(module.getName(), module.getId(), sectionNum, sectionId,
|
||||
position, module.getModname(), fileList.get(i), true, false,
|
||||
MoodleAction.MoodleSynchronize, module.getVisible() == 1, module.getId());
|
||||
}
|
||||
fileList.remove(i);
|
||||
break;
|
||||
} else {
|
||||
found = true;
|
||||
if (module.getAvailability() != null) {
|
||||
var JsonB = new JsonConfigProvider().getContext(null);
|
||||
JsonB.fromJson(module.getAvailability(), ModuleAvailability.class);
|
||||
LocalDateTime time =
|
||||
LocalDateTime.ofInstant(Instant.ofEpochMilli(JsonB.fromJson(module.getAvailability().replaceAll("\\\\", ""),
|
||||
ModuleAvailability.class).getTimeDateCondition().getT() * 1000L), ZoneId.systemDefault());
|
||||
element = new syncTableElement(module.getName(), module.getId(), sectionNum, sectionId,
|
||||
position, module.getModname(), fileList.get(i), false, false,
|
||||
MoodleAction.ExistingFile, getPriorityVisibility(module.getVisible() == 1,
|
||||
JsonB.fromJson(module.getAvailability().replaceAll("\\\\", ""),
|
||||
ModuleAvailability.class).getConditionVisibility()),
|
||||
new TimeDateElement(time.toLocalDate(), time.toLocalTime()));
|
||||
}
|
||||
else {
|
||||
element = new syncTableElement(module.getName(), module.getId(), sectionNum, sectionId,
|
||||
position, module.getModname(), fileList.get(i), false, false,
|
||||
MoodleAction.ExistingFile, module.getVisible() == 1);
|
||||
}
|
||||
fileList.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
element = new syncTableElement(module.getName(), module.getId(), sectionNum, sectionId, position,
|
||||
module.getModname(), false, false, MoodleAction.NotLocalFile, module.getVisible() == 1);
|
||||
}
|
||||
|
||||
return new ReturnValue(fileList, element);
|
||||
}
|
||||
|
||||
|
||||
private static Boolean getPriorityVisibility(Boolean visible, Boolean availability) {
|
||||
if (!visible || !availability) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean contains(final String[] arr, final String key) {
|
||||
return Arrays.asList(arr).contains(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package moodle.sync.util;
|
||||
|
||||
import moodle.sync.core.model.json.Course;
|
||||
import moodle.sync.core.model.json.MoodleUpload;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
import moodle.sync.core.util.MoodleAction;
|
||||
import moodle.sync.core.web.service.MoodleService;
|
||||
import moodle.sync.core.web.service.MoodleUploadTemp;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
|
||||
public final class SetModuleService {
|
||||
|
||||
public static void publishResource(MoodleService moodleService, syncTableElement courseData, Course course,
|
||||
String url, String token) throws Exception {
|
||||
if (courseData.getAction() == MoodleAction.MoodleUpload) {
|
||||
MoodleUploadTemp uploader = new MoodleUploadTemp();
|
||||
//Upload of the file to the Moodle-platform.
|
||||
MoodleUpload upload = uploader.upload(courseData.getExistingFileName(), courseData.getExistingFile(), url
|
||||
, token);
|
||||
//Publish it in the Moodle-course.
|
||||
if (courseData.getUnixTimeStamp() > System.currentTimeMillis() / 1000L) {
|
||||
//Time in future
|
||||
moodleService.setResource(token, course.getId(), courseData.getSection(), upload.getItemid(),
|
||||
courseData.getUnixTimeStamp(), courseData.getVisible(), courseData.getModuleName(),
|
||||
courseData.getBeforemod());
|
||||
}
|
||||
else {
|
||||
//Time not modified, time should be null
|
||||
moodleService.setResource(token, course.getId(), courseData.getSection(), upload.getItemid(), null,
|
||||
courseData.getVisible(), courseData.getModuleName(), courseData.getBeforemod());
|
||||
}
|
||||
}
|
||||
else if (courseData.getAction() == MoodleAction.MoodleSynchronize) {
|
||||
MoodleUploadTemp uploader = new MoodleUploadTemp();
|
||||
//Upload of the new file to the Moodle-platform.
|
||||
MoodleUpload upload = uploader.upload(courseData.getExistingFileName(), courseData.getExistingFile(), url
|
||||
, token);
|
||||
//Publish it in the Moodle-course above the old course-module containing the old file.
|
||||
if (courseData.getUnixTimeStamp() > System.currentTimeMillis() / 1000L) {
|
||||
moodleService.setResource(token, course.getId(), courseData.getSection(), upload.getItemid(),
|
||||
courseData.getUnixTimeStamp(), courseData.getVisible(), courseData.getModuleName(),
|
||||
courseData.getBeforemod());
|
||||
}
|
||||
else {
|
||||
moodleService.setResource(token, course.getId(), courseData.getSection(), upload.getItemid(), null,
|
||||
courseData.getVisible(), courseData.getModuleName(), courseData.getBeforemod());
|
||||
}
|
||||
//Removal of the old course-module.
|
||||
moodleService.removeResource(token, courseData.getCmid());
|
||||
} else {
|
||||
moodleService.setMoveModule(token, courseData.getCmid(), courseData.getSectionId(),
|
||||
courseData.getBeforemod());
|
||||
}
|
||||
}
|
||||
|
||||
public static void publishFileserverResource(MoodleService moodleService, syncTableElement courseData,
|
||||
Course course, String token) throws Exception {
|
||||
//TODO: konkreter fileserver upload hinzufügen
|
||||
// url = fileservice.....
|
||||
String url = "https://wikipedia.org";
|
||||
if (courseData.getUnixTimeStamp() > System.currentTimeMillis() / 1000L) {
|
||||
moodleService.setUrl(token, course.getId(), courseData.getSection(), courseData.getModuleName(), url,
|
||||
courseData.getUnixTimeStamp(), courseData.getVisible(), courseData.getBeforemod());
|
||||
} else {
|
||||
moodleService.setUrl(token, course.getId(), courseData.getSection(), courseData.getModuleName(), url,
|
||||
null, courseData.getVisible(), courseData.getBeforemod());
|
||||
}
|
||||
}
|
||||
|
||||
public static void moveResource(MoodleService moodleService, syncTableElement courseData, String token) throws Exception {
|
||||
moodleService.setMoveModule(token, courseData.getCmid(), courseData.getSectionId(), courseData.getBeforemod());
|
||||
}
|
||||
|
||||
public static void createSection(MoodleService moodleService, syncTableElement courseData, Course course,
|
||||
String token) throws Exception {
|
||||
moodleService.setSection(token, course.getId(), courseData.getModuleName(), courseData.getSection());
|
||||
if (!courseData.getExistingFileName().split("_", 2)[0].matches("\\d+")) {
|
||||
File temp = new File(courseData.getExistingFile());
|
||||
temp.renameTo(new File(Path.of(courseData.getExistingFile()).getParent().toString() + "/" + courseData.getSection() + "_" + courseData.getExistingFileName()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package moodle.sync.util;
|
||||
|
||||
import javafx.scene.control.TextFormatter;
|
||||
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
/**
|
||||
* Class implementing a method used for input validation.
|
||||
*
|
||||
* @authod Daniel Schröter
|
||||
*/
|
||||
public class UserInputValidations {
|
||||
|
||||
/**
|
||||
* Providing operation to check whether an input is a number. If not, the input is not printed.
|
||||
*
|
||||
* returns if the input is a number, the number otherwise an empty string.
|
||||
*/
|
||||
public static UnaryOperator<TextFormatter.Change> numberValidationFormatter = change -> {
|
||||
//If change is not a number, change input to an empty string.
|
||||
if (!change.getControlNewText().matches("\\d+")) {
|
||||
change.setText("");
|
||||
}
|
||||
return change;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package moodle.sync.util;
|
||||
|
||||
/**
|
||||
* Class used to implement Methods to verify data.
|
||||
*/
|
||||
public final class VerifyDataService {
|
||||
|
||||
public static boolean validateString(String string){
|
||||
if(string == null || string.isEmpty() || string.isBlank()) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
34
moodle-sync-fx/src/main/java/moodle/sync/view/MainView.java
Normal file
34
moodle-sync-fx/src/main/java/moodle/sync/view/MainView.java
Normal file
|
@ -0,0 +1,34 @@
|
|||
package moodle.sync.view;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.lecturestudio.core.geometry.Rectangle2D;
|
||||
import org.lecturestudio.core.input.KeyEvent;
|
||||
import org.lecturestudio.core.view.Action;
|
||||
import org.lecturestudio.core.view.View;
|
||||
import org.lecturestudio.core.view.ViewLayer;
|
||||
|
||||
/**
|
||||
* Interface defining the functions of the "main-window".
|
||||
*/
|
||||
public interface MainView extends View {
|
||||
|
||||
Rectangle2D getBounds();
|
||||
|
||||
void close();
|
||||
|
||||
void hide();
|
||||
|
||||
void removeView(View view, ViewLayer layer);
|
||||
|
||||
void showView(View view, ViewLayer layer);
|
||||
|
||||
void setFullscreen(boolean fullscreen);
|
||||
|
||||
void setOnKeyEvent(Predicate<KeyEvent> action);
|
||||
|
||||
void setOnShown(Action action);
|
||||
|
||||
void setOnClose(Action action);
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package moodle.sync.view;
|
||||
|
||||
import org.lecturestudio.core.beans.BooleanProperty;
|
||||
import org.lecturestudio.core.beans.ObjectProperty;
|
||||
import org.lecturestudio.core.beans.StringProperty;
|
||||
import org.lecturestudio.core.view.Action;
|
||||
import org.lecturestudio.core.view.View;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Interface defining the functions of the "settings-page".
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public interface SettingsView extends View {
|
||||
|
||||
void setOnExit(Action action);
|
||||
|
||||
void setLocale(ObjectProperty<Locale> locale);
|
||||
|
||||
void setLocales(List<Locale> locales);
|
||||
|
||||
void setMoodleField(StringProperty moodleURL);
|
||||
|
||||
void setFormatsMoodle(StringProperty moodleformats);
|
||||
|
||||
void setFormatsFileserver(StringProperty fileserverformats);
|
||||
|
||||
void setFtpField(StringProperty ftpURL);
|
||||
|
||||
void setFtpPort(StringProperty ftpPort);
|
||||
|
||||
void setFtpUser(StringProperty ftpUser);
|
||||
|
||||
void setFtpPassword(StringProperty ftpPassword);
|
||||
|
||||
void setMoodleToken(StringProperty moodleToken);
|
||||
|
||||
void setSyncRootPath(StringProperty path);
|
||||
|
||||
void setSelectSyncRootPath(Action action);
|
||||
|
||||
void setShowUnknownFormats(BooleanProperty unknownFormats);
|
||||
}
|
43
moodle-sync-fx/src/main/java/moodle/sync/view/StartView.java
Normal file
43
moodle-sync-fx/src/main/java/moodle/sync/view/StartView.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
package moodle.sync.view;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
import moodle.sync.core.model.json.Course;
|
||||
import moodle.sync.javafx.model.syncTableElement;
|
||||
import moodle.sync.core.model.json.Section;
|
||||
import org.lecturestudio.core.beans.BooleanProperty;
|
||||
import org.lecturestudio.core.beans.ObjectProperty;
|
||||
import org.lecturestudio.core.view.Action;
|
||||
import org.lecturestudio.core.view.ConsumerAction;
|
||||
import org.lecturestudio.core.view.View;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Interface defining the functions of the "start-page".
|
||||
*
|
||||
* @author Daniel Schröter
|
||||
*/
|
||||
public interface StartView extends View {
|
||||
|
||||
void setOnUpdate(Action action);
|
||||
|
||||
void setOnSync(Action action);
|
||||
|
||||
void setOnSettings(Action action);
|
||||
|
||||
void setOnFolder(Action action);
|
||||
|
||||
void setSelectAll(BooleanProperty selectAll);
|
||||
|
||||
void setCourses(List<Course> courses);
|
||||
|
||||
void setCourse(ObjectProperty<Course> course);
|
||||
|
||||
void setSections(List<Section> sections);
|
||||
|
||||
void setSection(ObjectProperty<Section> section);
|
||||
|
||||
void setOnCourseChanged(ConsumerAction<Course> action);
|
||||
|
||||
void setData(ObservableList<syncTableElement> data);
|
||||
}
|
42
moodle-sync-fx/src/main/resources/log4j2.xml
Normal file
42
moodle-sync-fx/src/main/resources/log4j2.xml
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration>
|
||||
|
||||
<Properties>
|
||||
<Property name="filename">moodle-sync</Property>
|
||||
</Properties>
|
||||
|
||||
<Appenders>
|
||||
<Console name="STDOUT" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d %-5p %c{1}:%L - %m%n" />
|
||||
</Console>
|
||||
<RollingFile
|
||||
name="FILE"
|
||||
fileName="${sys:logFilePath}/${filename}-${sys:logAppVersion}.log"
|
||||
filePattern="${sys:logFilePath}/${filename}-${sys:logAppVersion}-%d{MM-dd-yyyy}-%i.log"
|
||||
immediateFlush="true">
|
||||
<PatternLayout pattern="%d %-5p %c{1}:%L - %m%n" />
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
|
||||
<SizeBasedTriggeringPolicy size="10 MB"/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy>
|
||||
<Delete basePath="${sys:logFilePath}" maxDepth="1">
|
||||
<IfFileName glob="${filename}-*.log"/>
|
||||
<IfAccumulatedFileCount exceeds="3"/>
|
||||
</Delete>
|
||||
</DefaultRolloverStrategy>
|
||||
</RollingFile>
|
||||
</Appenders>
|
||||
|
||||
<Loggers>
|
||||
<Logger name="moodle.sync.core.web.json" level="debug" />
|
||||
<Logger name="org.lecturestudio" level="error" />
|
||||
<Logger name="org.jboss" level="error" />
|
||||
|
||||
<Root level="info">
|
||||
<AppenderRef ref="STDOUT" />
|
||||
<AppenderRef ref="FILE" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
|
||||
</Configuration>
|
2
moodle-sync-fx/src/main/resources/moodle-api.properties
Normal file
2
moodle-sync-fx/src/main/resources/moodle-api.properties
Normal file
|
@ -0,0 +1,2 @@
|
|||
moodle.api.url = http://localhost/webservice/rest/
|
||||
moodle.upload.url = http://localhost/webservice/
|
|
@ -0,0 +1,5 @@
|
|||
.text-input:error {
|
||||
-fx-border-insets: 0;
|
||||
-fx-border-width: 2px;
|
||||
-fx-border-color: red;
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
|
||||
<!--
|
||||
In this document, the layout of the "settings-page" is defined
|
||||
@author Daniel Schröter
|
||||
-->
|
||||
|
||||
<?import org.lecturestudio.javafx.factory.LocaleListCell?>
|
||||
<?import org.lecturestudio.javafx.factory.LocaleCellFactory?>
|
||||
<fx:root alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" type="VBox" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<children>
|
||||
<Label alignment="TOP_CENTER" text="%settings.settings">
|
||||
<padding>
|
||||
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||
</padding></Label>
|
||||
<GridPane alignment="CENTER" vgap="15.0">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="150.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="300.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label prefHeight="17.0" prefWidth="156.0" text="%settings.language" />
|
||||
<Label prefHeight="17.0" prefWidth="156.0" text="%settings.root" GridPane.rowIndex="1" />
|
||||
<TextField fx:id="syncRootPath" prefHeight="25.0" prefWidth="300.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||
<Button fx:id="syncRootPathButton" alignment="CENTER" contentDisplay="CENTER" mnemonicParsing="false" text="%settings.search" GridPane.columnIndex="2" GridPane.rowIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets left="5.0" />
|
||||
</GridPane.margin></Button>
|
||||
<ComboBox fx:id="languageCombo" prefWidth="150.0" GridPane.columnIndex="1">
|
||||
<buttonCell>
|
||||
<LocaleListCell/>
|
||||
</buttonCell>
|
||||
<cellFactory>
|
||||
<LocaleCellFactory/>
|
||||
</cellFactory>
|
||||
</ComboBox>
|
||||
</children>
|
||||
</GridPane>
|
||||
<Label prefWidth="612.0" styleClass="text-head" text="%settings.lmslabel">
|
||||
<padding>
|
||||
<Insets bottom="5.0" right="5.0" top="10.0" />
|
||||
</padding>
|
||||
<VBox.margin>
|
||||
<Insets top="5.0" />
|
||||
</VBox.margin>
|
||||
</Label>
|
||||
<GridPane alignment="CENTER" vgap="15.0">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="LEFT" hgrow="SOMETIMES" minWidth="10.0" prefWidth="150.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="300.0" />
|
||||
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label text="%settings.token" GridPane.rowIndex="1" />
|
||||
<TextField fx:id="tokenField" prefHeight="25.0" prefWidth="300.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||
<Label text="%settings.lms" />
|
||||
<TextField fx:id="moodleField" prefHeight="25.0" prefWidth="300.0" GridPane.columnIndex="1" />
|
||||
<Label text="%settings.dataformatsmoodle" GridPane.rowIndex="2" />
|
||||
<TextArea fx:id="formatsMoodle" GridPane.columnIndex="1" GridPane.rowIndex="2">
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" top="12.0" />
|
||||
</GridPane.margin>
|
||||
</TextArea>
|
||||
</children>
|
||||
</GridPane>
|
||||
<Label prefWidth="612.0" styleClass="text-head" text="%settings.ftplabel">
|
||||
<VBox.margin>
|
||||
<Insets top="10.0" />
|
||||
</VBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" right="5.0" top="10.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<GridPane alignment="CENTER" vgap="10.0">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="LEFT" hgrow="SOMETIMES" minWidth="10.0" prefWidth="150.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="300.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label text="%settings.ftp" />
|
||||
<Label text="%settings.ftpusername" GridPane.rowIndex="1" />
|
||||
<Label text="%settings.ftppassword" GridPane.rowIndex="2" />
|
||||
<TextField fx:id="ftpUser" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||
<TextField fx:id="ftpPassword" GridPane.columnIndex="1" GridPane.rowIndex="2" />
|
||||
<Label text="%settings.dataformatsfilesverer" GridPane.rowIndex="3" />
|
||||
<TextArea fx:id="formatsFileserver" GridPane.columnIndex="1" GridPane.rowIndex="3">
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" top="15.0" />
|
||||
</GridPane.margin>
|
||||
</TextArea>
|
||||
<TextField fx:id="ftpPort" prefHeight="25.0" prefWidth="100.0" GridPane.columnIndex="2">
|
||||
<GridPane.margin>
|
||||
<Insets left="5.0" />
|
||||
</GridPane.margin>
|
||||
</TextField>
|
||||
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="300.0" GridPane.columnIndex="1">
|
||||
<children>
|
||||
<TextField fx:id="ftpField" prefHeight="25.0" prefWidth="240.0" />
|
||||
<Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefHeight="25.0" prefWidth="60.0" text="%settings.port" />
|
||||
</children>
|
||||
</HBox>
|
||||
</children>
|
||||
</GridPane>
|
||||
<GridPane prefHeight="45.0" prefWidth="612.0">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<CheckBox fx:id="showUnknownFormats" mnemonicParsing="false" text="%settings.showUnknownFormats">
|
||||
<GridPane.margin>
|
||||
<Insets top="5.0" />
|
||||
</GridPane.margin>
|
||||
</CheckBox>
|
||||
</children>
|
||||
</GridPane>
|
||||
<HBox alignment="CENTER" spacing="50.0">
|
||||
<children>
|
||||
<Button fx:id="closesettingsButton" mnemonicParsing="false" text="%settings.close">
|
||||
<HBox.margin>
|
||||
<Insets top="2.0" />
|
||||
</HBox.margin></Button>
|
||||
</children>
|
||||
<padding>
|
||||
<Insets top="15.0" />
|
||||
</padding>
|
||||
</HBox>
|
||||
</children>
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<padding>
|
||||
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||
</padding>
|
||||
</fx:root>
|
|
@ -0,0 +1,16 @@
|
|||
settings.search = Durchsuchen
|
||||
settings.close = Schlie\u00DFen
|
||||
settings.root = Rootverzeichnis
|
||||
settings.token = Token
|
||||
settings.lms = URL
|
||||
settings.ftp = URL
|
||||
settings.ftpusername = Benutzername
|
||||
settings.ftppassword = Passwort
|
||||
settings.dataformatsmoodle = Dateiformate
|
||||
settings.dataformatsfilesverer = Dateiformate
|
||||
settings.settings = Einstellungen
|
||||
settings.lmslabel = Moodle-Plattform
|
||||
settings.ftplabel = Fileserver
|
||||
settings.port = Port
|
||||
settings.showUnknownFormats = Dateien mit unbekannten Dateiformaten anzeigen
|
||||
settings.language = Sprache
|
|
@ -0,0 +1,16 @@
|
|||
settings.search = Search Directory
|
||||
settings.close = Close
|
||||
settings.root = Root directory
|
||||
settings.token = Token
|
||||
settings.lms = URL
|
||||
settings.ftp = URL
|
||||
settings.ftpusername = Username
|
||||
settings.ftppassword = Password
|
||||
settings.dataformatsmoodle = Fileformats
|
||||
settings.dataformatsfilesverer = Fileformats
|
||||
settings.settings = Settings
|
||||
settings.lmslabel = Moodle-Platform
|
||||
settings.ftplabel = Fileserver
|
||||
settings.port = Port
|
||||
settings.showUnknownFormats = Show Files with unknown Fileformats
|
||||
settings.language = Language
|
|
@ -0,0 +1,93 @@
|
|||
.main-start .container {
|
||||
-fx-spacing: 10;
|
||||
-fx-padding: 30;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.folderButton{
|
||||
-fx-icon-content: "m.5 3 .04.87a1.99 1.99 0 0 0-.342 1.311l.637 7A2 2 0 0 0 2.826 14H9v-1H2.826a1 1 0 0 1-.995-.91l-.637-7A1 1 0 0 1 2.19 4h11.62a1 1 0 0 1 .996 1.09L14.54 8h1.005l.256-2.819A2 2 0 0 0 13.81 3H9.828a2 2 0 0 1-1.414-.586l-.828-.828A2 2 0 0 0 6.172 1H2.5a2 2 0 0 0-2 2zm5.672-1a1 1 0 0 1 .707.293L7.586 3H2.19c-.24 0-.47.042-.683.12L1.5 2.98a1 1 0 0 1 1-.98h3.672z M13.5 10a.5.5 0 0 1 .5.5V12h1.5a.5.5 0 1 1 0 1H14v1.5a.5.5 0 1 1-1 0V13h-1.5a.5.5 0 0 1 0-1H13v-1.5a.5.5 0 0 1 .5-.5z";
|
||||
}
|
||||
|
||||
.unavailable-icon{
|
||||
-fx-icon-content: "M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z";
|
||||
}
|
||||
|
||||
.assignment-icon{
|
||||
-fx-icon-content: "M8 11a.5.5 0 0 0 .5-.5V6.707l1.146 1.147a.5.5 0 0 0 .708-.708l-2-2a.5.5 0 0 0-.708 0l-2 2a.5.5 0 1 0 .708.708L7.5 6.707V10.5a.5.5 0 0 0 .5.5z M4 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H4zm0 1h8a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1z";
|
||||
}
|
||||
.chat-icon{
|
||||
-fx-icon-content: "M11.176 14.429c-2.665 0-4.826-1.8-4.826-4.018 0-2.22 2.159-4.02 4.824-4.02S16 8.191 16 10.411c0 1.21-.65 2.301-1.666 3.036a.324.324 0 0 0-.12.366l.218.81a.616.616 0 0 1 .029.117.166.166 0 0 1-.162.162.177.177 0 0 1-.092-.03l-1.057-.61a.519.519 0 0 0-.256-.074.509.509 0 0 0-.142.021 5.668 5.668 0 0 1-1.576.22ZM9.064 9.542a.647.647 0 1 0 .557-1 .645.645 0 0 0-.646.647.615.615 0 0 0 .09.353Zm3.232.001a.646.646 0 1 0 .546-1 .645.645 0 0 0-.644.644.627.627 0 0 0 .098.356Z M0 6.826c0 1.455.781 2.765 2.001 3.656a.385.385 0 0 1 .143.439l-.161.6-.1.373a.499.499 0 0 0-.032.14.192.192 0 0 0 .193.193c.039 0 .077-.01.111-.029l1.268-.733a.622.622 0 0 1 .308-.088c.058 0 .116.009.171.025a6.83 6.83 0 0 0 1.625.26 4.45 4.45 0 0 1-.177-1.251c0-2.936 2.785-5.02 5.824-5.02.05 0 .1 0 .15.002C10.587 3.429 8.392 2 5.796 2 2.596 2 0 4.16 0 6.826Zm4.632-1.555a.77.77 0 1 1-1.54 0 .77.77 0 0 1 1.54 0Zm3.875 0a.77.77 0 1 1-1.54 0 .77.77 0 0 1 1.54 0Z";
|
||||
}
|
||||
.feedback-icon{
|
||||
-fx-icon-content: "M13 2.5a1.5 1.5 0 0 1 3 0v11a1.5 1.5 0 0 1-3 0v-.214c-2.162-1.241-4.49-1.843-6.912-2.083l.405 2.712A1 1 0 0 1 5.51 15.1h-.548a1 1 0 0 1-.916-.599l-1.85-3.49a68.14 68.14 0 0 0-.202-.003A2.014 2.014 0 0 1 0 9V7a2.02 2.02 0 0 1 1.992-2.013 74.663 74.663 0 0 0 2.483-.075c3.043-.154 6.148-.849 8.525-2.199V2.5zm1 0v11a.5.5 0 0 0 1 0v-11a.5.5 0 0 0-1 0zm-1 1.35c-2.344 1.205-5.209 1.842-8 2.033v4.233c.18.01.359.022.537.036 2.568.189 5.093.744 7.463 1.993V3.85zm-9 6.215v-4.13a95.09 95.09 0 0 1-1.992.052A1.02 1.02 0 0 0 1 7v2c0 .55.448 1.002 1.006 1.009A60.49 60.49 0 0 1 4 10.065zm-.657.975 1.609 3.037.01.024h.548l-.002-.014-.443-2.966a68.019 68.019 0 0 0-1.722-.082z";
|
||||
}
|
||||
.file-icon{
|
||||
-fx-icon-content: "M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z";
|
||||
}
|
||||
.folder-icon{
|
||||
-fx-icon-content: "M.54 3.87.5 3a2 2 0 0 1 2-2h3.672a2 2 0 0 1 1.414.586l.828.828A2 2 0 0 0 9.828 3h3.982a2 2 0 0 1 1.992 2.181l-.637 7A2 2 0 0 1 13.174 14H2.826a2 2 0 0 1-1.991-1.819l-.637-7a1.99 1.99 0 0 1 .342-1.31zM2.19 4a1 1 0 0 0-.996 1.09l.637 7a1 1 0 0 0 .995.91h10.348a1 1 0 0 0 .995-.91l.637-7A1 1 0 0 0 13.81 4H2.19zm4.69-1.707A1 1 0 0 0 6.172 2H2.5a1 1 0 0 0-1 .981l.006.139C1.72 3.042 1.95 3 2.19 3h5.396l-.707-.707z";
|
||||
}
|
||||
.forum-icon{
|
||||
-fx-icon-content: "M14 1a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1h-2.5a2 2 0 0 0-1.6.8L8 14.333 6.1 11.8a2 2 0 0 0-1.6-.8H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2.5a1 1 0 0 1 .8.4l1.9 2.533a1 1 0 0 0 1.6 0l1.9-2.533a1 1 0 0 1 .8-.4H14a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z M3 3.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zM3 6a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9A.5.5 0 0 1 3 6zm0 2.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5z";
|
||||
}
|
||||
.label-icon{
|
||||
-fx-icon-content: "M6 4.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm-1 0a.5.5 0 1 0-1 0 .5.5 0 0 0 1 0z M2 1h4.586a1 1 0 0 1 .707.293l7 7a1 1 0 0 1 0 1.414l-4.586 4.586a1 1 0 0 1-1.414 0l-7-7A1 1 0 0 1 1 6.586V2a1 1 0 0 1 1-1zm0 5.586 7 7L13.586 9l-7-7H2v4.586z";
|
||||
}
|
||||
.quiz-icon{
|
||||
-fx-icon-content: "M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.235.235 0 0 1 .02-.022z";
|
||||
}
|
||||
.survey-icon{
|
||||
-fx-icon-content: "M11 2a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v12h.5a.5.5 0 0 1 0 1H.5a.5.5 0 0 1 0-1H1v-3a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v3h1V7a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v7h1V2zm1 12h2V2h-2v12zm-3 0V7H7v7h2zm-5 0v-3H2v3h2z";
|
||||
}
|
||||
.url-icon{
|
||||
-fx-icon-content: "M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-6.923c-.67.204-1.335.82-1.887 1.855-.143.268-.276.56-.395.872.705.157 1.472.257 2.282.287V1.077zM4.249 3.539c.142-.384.304-.744.481-1.078a6.7 6.7 0 0 1 .597-.933A7.01 7.01 0 0 0 3.051 3.05c.362.184.763.349 1.198.49zM3.509 7.5c.036-1.07.188-2.087.436-3.008a9.124 9.124 0 0 1-1.565-.667A6.964 6.964 0 0 0 1.018 7.5h2.49zm1.4-2.741a12.344 12.344 0 0 0-.4 2.741H7.5V5.091c-.91-.03-1.783-.145-2.591-.332zM8.5 5.09V7.5h2.99a12.342 12.342 0 0 0-.399-2.741c-.808.187-1.681.301-2.591.332zM4.51 8.5c.035.987.176 1.914.399 2.741A13.612 13.612 0 0 1 7.5 10.91V8.5H4.51zm3.99 0v2.409c.91.03 1.783.145 2.591.332.223-.827.364-1.754.4-2.741H8.5zm-3.282 3.696c.12.312.252.604.395.872.552 1.035 1.218 1.65 1.887 1.855V11.91c-.81.03-1.577.13-2.282.287zm.11 2.276a6.696 6.696 0 0 1-.598-.933 8.853 8.853 0 0 1-.481-1.079 8.38 8.38 0 0 0-1.198.49 7.01 7.01 0 0 0 2.276 1.522zm-1.383-2.964A13.36 13.36 0 0 1 3.508 8.5h-2.49a6.963 6.963 0 0 0 1.362 3.675c.47-.258.995-.482 1.565-.667zm6.728 2.964a7.009 7.009 0 0 0 2.275-1.521 8.376 8.376 0 0 0-1.197-.49 8.853 8.853 0 0 1-.481 1.078 6.688 6.688 0 0 1-.597.933zM8.5 11.909v3.014c.67-.204 1.335-.82 1.887-1.855.143-.268.276-.56.395-.872A12.63 12.63 0 0 0 8.5 11.91zm3.555-.401c.57.185 1.095.409 1.565.667A6.963 6.963 0 0 0 14.982 8.5h-2.49a13.36 13.36 0 0 1-.437 3.008zM14.982 7.5a6.963 6.963 0 0 0-1.362-3.675c-.47.258-.995.482-1.565.667.248.92.4 1.938.437 3.008h2.49zM11.27 2.461c.177.334.339.694.482 1.078a8.368 8.368 0 0 0 1.196-.49 7.01 7.01 0 0 0-2.275-1.52c.218.283.418.597.597.932zm-.488 1.343a7.765 7.765 0 0 0-.395-.872C9.835 1.897 9.17 1.282 8.5 1.077V4.09c.81-.03 1.577-.13 2.282-.287z";
|
||||
}
|
||||
.other-icon{
|
||||
-fx-icon-content: "M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z M5.255 5.786a.237.237 0 0 0 .241.247h.825c.138 0 .248-.113.266-.25.09-.656.54-1.134 1.342-1.134.686 0 1.314.343 1.314 1.168 0 .635-.374.927-.965 1.371-.673.489-1.206 1.06-1.168 1.987l.003.217a.25.25 0 0 0 .25.246h.811a.25.25 0 0 0 .25-.25v-.105c0-.718.273-.927 1.01-1.486.609-.463 1.244-.977 1.244-2.056 0-1.511-1.276-2.241-2.673-2.241-1.267 0-2.655.59-2.75 2.286zm1.557 5.763c0 .533.425.927 1.01.927.609 0 1.028-.394 1.028-.927 0-.552-.42-.94-1.029-.94-.584 0-1.009.388-1.009.94z";
|
||||
}
|
||||
|
||||
.ftp-icon{
|
||||
-fx-icon-content: "M4.5 11a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1zM3 10.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0z M16 11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V9.51c0-.418.105-.83.305-1.197l2.472-4.531A1.5 1.5 0 0 1 4.094 3h7.812a1.5 1.5 0 0 1 1.317.782l2.472 4.53c.2.368.305.78.305 1.198V11zM3.655 4.26 1.592 8.043C1.724 8.014 1.86 8 2 8h12c.14 0 .276.014.408.042L12.345 4.26a.5.5 0 0 0-.439-.26H4.094a.5.5 0 0 0-.44.26zM1 10v1a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-1a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1z";
|
||||
}
|
||||
|
||||
.trash-icon{
|
||||
-fx-icon-content: "M6.5 1h3a.5.5 0 0 1 .5.5v1H6v-1a.5.5 0 0 1 .5-.5ZM11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3A1.5 1.5 0 0 0 5 1.5v1H2.506a.58.58 0 0 0-.01 0H1.5a.5.5 0 0 0 0 1h.538l.853 10.66A2 2 0 0 0 4.885 16h6.23a2 2 0 0 0 1.994-1.84l.853-10.66h.538a.5.5 0 0 0 0-1h-.995a.59.59 0 0 0-.01 0H11Zm1.958 1-.846 10.58a1 1 0 0 1-.997.92h-6.23a1 1 0 0 1-.997-.92L3.042 3.5h9.916Zm-7.487 1a.5.5 0 0 1 .528.47l.5 8.5a.5.5 0 0 1-.998.06L5 5.03a.5.5 0 0 1 .47-.53Zm5.058 0a.5.5 0 0 1 .47.53l-.5 8.5a.5.5 0 1 1-.998-.06l.5-8.5a.5.5 0 0 1 .528-.47ZM8 4.5a.5.5 0 0 1 .5.5v8.5a.5.5 0 0 1-1 0V5a.5.5 0 0 1 .5-.5Z";
|
||||
}
|
||||
|
||||
|
||||
.check-box .box {
|
||||
-fx-background-color: white;
|
||||
-fx-border-color:grey;
|
||||
-fx-border-radius:3px;
|
||||
}
|
||||
|
||||
.check-box:selected .mark {
|
||||
|
||||
-fx-background-color: white;
|
||||
|
||||
}
|
||||
|
||||
.check-box:selected .box {
|
||||
-fx-background-color: #28a3f4;
|
||||
}
|
||||
|
||||
.table-column .label {
|
||||
-fx-content-display: right ;
|
||||
}
|
||||
|
||||
.table-column {
|
||||
-fx-cell-size: 40;
|
||||
}
|
||||
|
||||
.headerstyle.table-row-cell {
|
||||
}
|
||||
|
||||
.headerstyle.table-row-cell .table-cell {
|
||||
-fx-border-width: 0;
|
||||
}
|
||||
|
||||
.popUpTextArea {
|
||||
-fx-text-fill: black;
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
In this document, the layout of the "start-page" is defined
|
||||
@author Daniel Schröter
|
||||
-->
|
||||
|
||||
|
||||
<?import javafx.scene.control.cell.PropertyValueFactory?>
|
||||
|
||||
<?import org.lecturestudio.javafx.layout.DynamicResizePolicy?>
|
||||
<?import org.lecturestudio.javafx.layout.ColumnSizeConstraints?>
|
||||
|
||||
<?import org.lecturestudio.javafx.control.SvgIcon?>
|
||||
|
||||
<?import moodle.sync.javafx.custom.CourseListCell?>
|
||||
<?import moodle.sync.javafx.custom.CourseCellFactory?>
|
||||
<?import moodle.sync.javafx.custom.SectionListCell?>
|
||||
<?import moodle.sync.javafx.custom.SectionCellFactory?>
|
||||
<?import moodle.sync.javafx.custom.DragAndDropRowFactory?>
|
||||
<?import moodle.sync.javafx.custom.HighlightSectionCellFactory?>
|
||||
<?import moodle.sync.javafx.custom.StatusCellFactory?>
|
||||
<?import moodle.sync.javafx.custom.CheckBoxTableCellFactory?>
|
||||
<?import moodle.sync.javafx.custom.UploadElementCellValueFactory?>
|
||||
<?import moodle.sync.javafx.custom.AvailabilityCellFactory?>
|
||||
<?import moodle.sync.javafx.custom.AvailabilityCellValueFactory?>
|
||||
<?import moodle.sync.javafx.custom.AvailableDateTimeTableCellFactory?>
|
||||
|
||||
<fx:root alignment="CENTER" type="VBox" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<BorderPane VBox.vgrow="ALWAYS" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<top>
|
||||
<HBox alignment="CENTER_LEFT" spacing="5.0">
|
||||
<VBox alignment="CENTER">
|
||||
<Button fx:id="folderButton" mnemonicParsing="false">
|
||||
<graphic>
|
||||
<SvgIcon styleClass="icon, folderButton"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
</VBox>
|
||||
<VBox>
|
||||
<Label text ="%start.selectcourse"/>
|
||||
<ComboBox fx:id="courseCombo">
|
||||
<buttonCell>
|
||||
<CourseListCell/>
|
||||
</buttonCell>
|
||||
<cellFactory>
|
||||
<CourseCellFactory/>
|
||||
</cellFactory>
|
||||
</ComboBox>
|
||||
</VBox>
|
||||
<VBox>
|
||||
<Label text ="%start.selectsection"/>
|
||||
<ComboBox fx:id="sectionCombo">
|
||||
<buttonCell>
|
||||
<SectionListCell/>
|
||||
</buttonCell>
|
||||
<cellFactory>
|
||||
<SectionCellFactory/>
|
||||
</cellFactory>
|
||||
</ComboBox>
|
||||
</VBox>
|
||||
<Pane HBox.hgrow="ALWAYS"/>
|
||||
<Button fx:id="updateButton" mnemonicParsing="false" text="%start.update"/>
|
||||
<Button fx:id="syncButton" mnemonicParsing="false" text="%start.sync"/>
|
||||
<Button fx:id="settingsButton" mnemonicParsing="false" text="%start.settings"/>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
|
||||
</padding>
|
||||
</HBox>
|
||||
</top>
|
||||
<center>
|
||||
<TableView fx:id="syncTable" BorderPane.alignment="CENTER" editable="true" prefHeight="27.0">
|
||||
<rowFactory>
|
||||
<DragAndDropRowFactory/>
|
||||
</rowFactory>
|
||||
<columnResizePolicy>
|
||||
<DynamicResizePolicy tableView="$syncTable">
|
||||
<columnConstraints>
|
||||
<ColumnSizeConstraints percentWidth="0.495"/>
|
||||
<ColumnSizeConstraints percentWidth="0.495"/>
|
||||
<ColumnSizeConstraints prefWidth="90.0"/>
|
||||
<ColumnSizeConstraints prefWidth="85.0"/>
|
||||
<ColumnSizeConstraints prefWidth="200.0"/>
|
||||
</columnConstraints>
|
||||
</DynamicResizePolicy>
|
||||
</columnResizePolicy>
|
||||
<columns>
|
||||
<TableColumn fx:id="courseViewTableColumn" text="Moodle">
|
||||
<cellFactory>
|
||||
<HighlightSectionCellFactory/>
|
||||
</cellFactory>
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="moduleName"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn fx:id="localViewTableColumn" text="%start.local">
|
||||
<cellFactory>
|
||||
<StatusCellFactory/>
|
||||
</cellFactory>
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="existingFileName"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn fx:id="executeTableColumn" styleClass="CENTER" editable="true" text="%start.execute">
|
||||
<graphic>
|
||||
<CheckBox fx:id="allSelected" mnemonicParsing="false" />
|
||||
</graphic>
|
||||
<cellFactory>
|
||||
<CheckBoxTableCellFactory/>
|
||||
</cellFactory>
|
||||
<cellValueFactory>
|
||||
<UploadElementCellValueFactory/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn fx:id="availabilityTableColumn" styleClass="CENTER" editable="true" text="%start.visible">
|
||||
<cellFactory>
|
||||
<AvailabilityCellFactory/>
|
||||
</cellFactory>
|
||||
<cellValueFactory>
|
||||
<AvailabilityCellValueFactory/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn fx:id="availabilityTimeTableColumn" styleClass="CENTER" editable="true" text="%start.availability">
|
||||
<cellFactory>
|
||||
<AvailableDateTimeTableCellFactory/>
|
||||
</cellFactory>
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="availabilityDateTime"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<!--- <TableColumn fx:id="deleteColumn" styleClass="Center" editable="false" text="%start.delete">
|
||||
<cellFactory>
|
||||
<DeleteButtonCellFactory/>
|
||||
</cellFactory>
|
||||
</TableColumn> -->
|
||||
</columns>
|
||||
<BorderPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||
</BorderPane.margin>
|
||||
|
||||
</TableView>
|
||||
</center>
|
||||
</BorderPane>
|
||||
</fx:root>
|
|
@ -0,0 +1,22 @@
|
|||
start.exit = Beenden
|
||||
start.sync = Synchronisieren
|
||||
start.update = Aktualisieren
|
||||
start.local = Lokal
|
||||
start.execute = Ausf\u00FChren
|
||||
start.visible = Sichtbarkeit
|
||||
start.availability = Zeitpunkt
|
||||
start.sync.showall = Alle anzeigen
|
||||
start.sync.error.title = Fehler
|
||||
start.sync.error.message = Synchronisierung fehlgeschlagen. Bitte versuchen Sie es erneut.
|
||||
start.settings = Einstellungen
|
||||
start.selectcourse = Kurs
|
||||
start.selectsection = Sektion
|
||||
start.delete = Löschen
|
||||
start.sync.error.fileserver1.message = Fileserver Angaben unvollst\u00E4ndig. Bitte in den Einstellungen erg\u00E4nzen.
|
||||
start.sync.error.fileserver2.message = Verbindungsaufbau zum Fileserver fehlgeschlagen. Bitte Einstellungen \u00FCberpr\u00FCfen.
|
||||
start.sync.error.course.message = Synchronisierung fehlgeschlagen. Bitte Kurs ausw\u00E4hlen.
|
||||
start.sync.error.section.message = Synchronisierung fehlgeschlagen. Bitte Kurssektion ausw\u00E4hlen.
|
||||
start.sync.error.invalidurl.message = Fehler beim Verbindungsaufbau zur Moodle-Plattform. Bitte angegebene URL und Token \u00FCberpr\u00FCfen.
|
||||
start.sync.error.path.message = Root-Verzeichnis fehlerhaft. Bitte in den Einstellungen korrigieren.
|
||||
start.sync.error.path.unknown.message = Kein Kurs ausgew\u00E4hlt. Bitte Kurs w\u00E4hlen.
|
||||
start.sync.error.upload.message = Upload nicht m\u00F6glich. Bitte Dateigr\u00F6\u00DFe, Moodle-Berechtigungen und Internetverbindung \u00FCberpr\u00FCfen.\nBetreffende Datei: {0}
|
|
@ -0,0 +1,22 @@
|
|||
start.exit = Exit
|
||||
start.sync = Sync
|
||||
start.update = Update
|
||||
start.local = Local
|
||||
start.execute = Execute
|
||||
start.visible = Visibility
|
||||
start.sync.showall = Show all
|
||||
start.availability = Availability
|
||||
start.sync.error.title = Error
|
||||
start.sync.error.message = Synchronization failed. Please try again.
|
||||
start.settings = Settings
|
||||
start.selectcourse = Course
|
||||
start.selectsection = Section
|
||||
start.delete = Delete
|
||||
start.sync.error.fileserver1.message = Fileserver settings incomplete.
|
||||
start.sync.error.fileserver2.message = Connecting to fileserver was failed. Please check settings.
|
||||
start.sync.error.course.message = Synchronization failed. Please choose a course.
|
||||
start.sync.error.section.message = Synchronization failed. Please choose a section.
|
||||
start.sync.error.invalidurl.message = Error when connecting to Moodle. Please check URL and Token.
|
||||
start.sync.error.path.message = Root-Directory faulty. Please correct in settings.
|
||||
start.sync.error.path.unknown.message = No course chosen. Please choose a course.
|
||||
start.sync.error.upload.message = Upload to Moodle not possible, check permissions and upload size. File:
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.String?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import moodle.sync.javafx.view.FxStartView?>
|
||||
|
||||
<!--
|
||||
In this document, the layout of the "main-window" is defined
|
||||
-->
|
||||
|
||||
<fx:root type="StackPane" prefWidth="800" prefHeight="600" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<stylesheets>
|
||||
<String fx:value="/resources/css/base.css" />
|
||||
</stylesheets>
|
||||
|
||||
<BorderPane fx:id="contentPane">
|
||||
<center>
|
||||
<FxStartView />
|
||||
</center>
|
||||
</BorderPane>
|
||||
</fx:root>
|
17
moodle-sync-package-archive/assembly-bundle.xml
Normal file
17
moodle-sync-package-archive/assembly-bundle.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.1"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.1 http://maven.apache.org/xsd/assembly-2.1.1.xsd">
|
||||
|
||||
<id>bundle</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.parent.build.directory}/${package.bundle.dir}</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
17
moodle-sync-package-archive/assembly.xml
Normal file
17
moodle-sync-package-archive/assembly.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.1"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.1 http://maven.apache.org/xsd/assembly-2.1.1.xsd">
|
||||
|
||||
<id>default</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.parent.build.directory}/${package.dir}</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
63
moodle-sync-package-archive/pom.xml
Normal file
63
moodle-sync-package-archive/pom.xml
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>moodle.sync</groupId>
|
||||
<artifactId>moodle-sync</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>moodle-sync-package-archive</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<package.dir>${package.name}-${os.name}</package.dir>
|
||||
<package.bundle.dir>${package.name}-bundle-${os.name}</package.bundle.dir>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>assembly.xml</descriptor>
|
||||
</descriptors>
|
||||
<finalName>${package.name}-${package.version}-${os.name}</finalName>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>bundle</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>assembly-bundle.xml</descriptor>
|
||||
</descriptors>
|
||||
<finalName>${package.name}-bundle-${package.version}-${os.name}</finalName>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<outputDirectory>${project.parent.build.directory}</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
68
moodle-sync-package-cli/pom.xml
Normal file
68
moodle-sync-package-cli/pom.xml
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>moodle.sync</groupId>
|
||||
<artifactId>moodle-sync</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>moodle-sync-package-cli</artifactId>
|
||||
<packaging>jpackage</packaging>
|
||||
|
||||
<properties>
|
||||
<app.name>moodle-sync-cli</app.name>
|
||||
<app.name.parent>moodle-sync-bundle-${os.name}</app.name.parent>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.tentackle</groupId>
|
||||
<artifactId>tentackle-jlink-maven-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<mainClass>moodle.sync.cli.CommandLineInterface</mainClass>
|
||||
<variables>
|
||||
<appName>moodle-sync-cli</appName>
|
||||
<appVersion>${package.version}</appVersion>
|
||||
<appVendor>TU Darmstadt</appVendor>
|
||||
<appCopyright>Copyright © 2022 TU Darmstadt</appCopyright>
|
||||
</variables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<target>
|
||||
<mkdir dir="${project.basedir}/../target/${app.name.parent}" />
|
||||
<copy todir="${project.basedir}/../target/${app.name.parent}">
|
||||
<fileset dir="${project.build.directory}/jpackage/${app.name}" />
|
||||
</copy>
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>moodle.sync</groupId>
|
||||
<artifactId>moodle-sync-cli</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
13
moodle-sync-package-cli/templates/package-image.ftl
Normal file
13
moodle-sync-package-cli/templates/package-image.ftl
Normal file
|
@ -0,0 +1,13 @@
|
|||
<#-- template to create the options file for the jpackage tool to create the application image -->
|
||||
<#if osName?upper_case?contains("WIN")>
|
||||
--win-console
|
||||
<#elseif osName?upper_case?contains("MAC")>
|
||||
|
||||
<#else>
|
||||
|
||||
</#if>
|
||||
|
||||
--name ${appName}
|
||||
--app-version "${appVersion}"
|
||||
--vendor "${appVendor}"
|
||||
--copyright "${appCopyright}"
|
8
moodle-sync-package-cli/templates/package-installer.ftl
Normal file
8
moodle-sync-package-cli/templates/package-installer.ftl
Normal file
|
@ -0,0 +1,8 @@
|
|||
<#-- template to create the options file for the jpackage tool to create the installer -->
|
||||
<#if osName?upper_case?contains("WIN")>
|
||||
|
||||
<#elseif osName?upper_case?contains("MAC")>
|
||||
|
||||
<#else>
|
||||
|
||||
</#if>
|
68
moodle-sync-package-fx/pom.xml
Normal file
68
moodle-sync-package-fx/pom.xml
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>moodle.sync</groupId>
|
||||
<artifactId>moodle-sync</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>moodle-sync-package-fx</artifactId>
|
||||
<packaging>jpackage</packaging>
|
||||
|
||||
<properties>
|
||||
<app.name>moodle-sync-fx</app.name>
|
||||
<app.name.parent>moodle-sync-bundle-${os.name}</app.name.parent>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.tentackle</groupId>
|
||||
<artifactId>tentackle-jlink-maven-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<mainClass>moodle.sync.javafx.SyncApplication</mainClass>
|
||||
<variables>
|
||||
<appName>moodle-sync-fx</appName>
|
||||
<appVersion>${package.version}</appVersion>
|
||||
<appVendor>TU Darmstadt</appVendor>
|
||||
<appCopyright>Copyright © 2022 TU Darmstadt</appCopyright>
|
||||
</variables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<target>
|
||||
<mkdir dir="${project.basedir}/../target/${app.name.parent}" />
|
||||
<copy todir="${project.basedir}/../target/${app.name.parent}">
|
||||
<fileset dir="${project.build.directory}/jpackage/${app.name}" />
|
||||
</copy>
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>moodle.sync</groupId>
|
||||
<artifactId>moodle-sync-fx</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
13
moodle-sync-package-fx/templates/package-image.ftl
Normal file
13
moodle-sync-package-fx/templates/package-image.ftl
Normal file
|
@ -0,0 +1,13 @@
|
|||
<#-- template to create the options file for the jpackage tool to create the application image -->
|
||||
<#if osName?upper_case?contains("WIN")>
|
||||
|
||||
<#elseif osName?upper_case?contains("MAC")>
|
||||
|
||||
<#else>
|
||||
|
||||
</#if>
|
||||
|
||||
--name ${appName}
|
||||
--app-version "${appVersion}"
|
||||
--vendor "${appVendor}"
|
||||
--copyright "${appCopyright}"
|
8
moodle-sync-package-fx/templates/package-installer.ftl
Normal file
8
moodle-sync-package-fx/templates/package-installer.ftl
Normal file
|
@ -0,0 +1,8 @@
|
|||
<#-- template to create the options file for the jpackage tool to create the installer -->
|
||||
<#if osName?upper_case?contains("WIN")>
|
||||
|
||||
<#elseif osName?upper_case?contains("MAC")>
|
||||
|
||||
<#else>
|
||||
|
||||
</#if>
|
331
pom.xml
Normal file
331
pom.xml
Normal file
|
@ -0,0 +1,331 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>moodle.sync</groupId>
|
||||
<artifactId>moodle-sync</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<description>
|
||||
Moodle platform file synchronization from a desktop app.
|
||||
</description>
|
||||
<url>https://github.com/lectureStudio/MoodleSync</url>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>d.schroeter</id>
|
||||
<name>Daniel Schröter</name>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>GNU General Public License, Version 3.0</name>
|
||||
<url>http://www.gnu.org/licenses/gpl-3.0.txt</url>
|
||||
<distribution>manual</distribution>
|
||||
<comments>A free, copyleft license for software and other kinds of works.</comments>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<issueManagement>
|
||||
<system>GitHub</system>
|
||||
<url>https://github.com/lectureStudio/MoodleSync/issues</url>
|
||||
</issueManagement>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/lectureStudio/MoodleSync.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/lectureStudio/MoodleSync.git</developerConnection>
|
||||
<url>https://github.com/lectureStudio/MoodleSync/tree/main</url>
|
||||
</scm>
|
||||
|
||||
<modules>
|
||||
<module>moodle-sync-core</module>
|
||||
<module>moodle-sync-cli</module>
|
||||
<module>moodle-sync-fx</module>
|
||||
<module>moodle-sync-package-cli</module>
|
||||
<module>moodle-sync-package-fx</module>
|
||||
<module>moodle-sync-package-archive</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<package.name>moodle-sync</package.name>
|
||||
<package.version>1.0.${git.commitsCount}</package.version>
|
||||
<package.dir>${package.name}-${os.name}</package.dir>
|
||||
<package.bundle.dir>${package.name}-bundle-${os.name}</package.bundle.dir>
|
||||
<maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
|
||||
<license.path>${project.basedir}/../</license.path>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<!-- Set versions of common plugins for reproducibility, ordered alphabetically. -->
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.10.0</version>
|
||||
<configuration>
|
||||
<release>14</release>
|
||||
<showDeprecation>true</showDeprecation>
|
||||
<showWarnings>true</showWarnings>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<version>3.8.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-installed</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>${project.packaging}</type>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<stripVersion>true</stripVersion>
|
||||
<outputDirectory>${project.parent.build.directory}/${package.dir}</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<excludeArtifactIds>junit</excludeArtifactIds>
|
||||
<stripVersion>true</stripVersion>
|
||||
<excludeTransitive>false</excludeTransitive>
|
||||
<excludeTypes>war</excludeTypes>
|
||||
<excludeScope>provided</excludeScope>
|
||||
<outputDirectory>${project.parent.build.directory}/${package.dir}/lib</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<compress>true</compress>
|
||||
<manifestEntries>
|
||||
<Version>${project.version}</Version>
|
||||
<Package-Version>${package.version}</Package-Version>
|
||||
<SCM-Revision>${git.revision}</SCM-Revision>
|
||||
<Build-Date>${maven.build.timestamp}</Build-Date>
|
||||
</manifestEntries>
|
||||
<addMavenDescriptor>false</addMavenDescriptor>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<failOnError>false</failOnError>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-license</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.outputDirectory}/META-INF</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${license.path}</directory>
|
||||
<includes>
|
||||
<include>LICENSE</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>ru.concerteza.buildnumber</groupId>
|
||||
<artifactId>maven-jgit-buildnumber-plugin</artifactId>
|
||||
<version>1.2.10</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.tentackle</groupId>
|
||||
<artifactId>tentackle-jlink-maven-plugin</artifactId>
|
||||
<version>17.12.0.0</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<noHeaderFiles>true</noHeaderFiles>
|
||||
<noManPages>true</noManPages>
|
||||
<stripDebug>true</stripDebug>
|
||||
<compress>0</compress>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>ru.concerteza.buildnumber</groupId>
|
||||
<artifactId>maven-jgit-buildnumber-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>git-buildnumber</id>
|
||||
<goals>
|
||||
<goal>extract-buildnumber</goal>
|
||||
</goals>
|
||||
<phase>validate</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- We want to sign the artifact, the POM, and all attached artifacts. -->
|
||||
<!--
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
</plugin>
|
||||
-->
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>linux-x86_64</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>unix</family>
|
||||
<name>Linux</name>
|
||||
<arch>amd64</arch>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<platform.arch>x86_64</platform.arch>
|
||||
<platform.name>linux</platform.name>
|
||||
<os.name>linux-${platform.arch}</os.name>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>mac-x86_64</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>mac</family>
|
||||
<arch>x86_64</arch>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<platform.arch>x86_64</platform.arch>
|
||||
<platform.name>mac</platform.name>
|
||||
<os.name>macosx-${platform.arch}</os.name>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>windows-x86_64</id>
|
||||
<activation>
|
||||
<os>
|
||||
<family>windows</family>
|
||||
<arch>amd64</arch>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<platform.arch>x86_64</platform.arch>
|
||||
<platform.name>windows</platform.name>
|
||||
<os.name>windows-${platform.arch}</os.name>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.9.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.9.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
Loading…
Reference in a new issue