parent
34c6476b90
commit
714c2d1971
@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="accountSettings">
|
||||
<option name="activeRegion" value="us-east-1" />
|
||||
<option name="recentlyUsedRegions">
|
||||
<list>
|
||||
<option value="us-east-1" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="openmeetings-web" />
|
||||
<module name="openmeetings-server" />
|
||||
<module name="openmeetings-webservice" />
|
||||
<module name="openmeetings-util" />
|
||||
<module name="openmeetings-core" />
|
||||
<module name="openmeetings-service" />
|
||||
<module name="openmeetings-mediaserver" />
|
||||
<module name="openmeetings-screenshare" />
|
||||
<module name="openmeetings-install" />
|
||||
<module name="openmeetings-db" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/openmeetings-core/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/openmeetings-db/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/openmeetings-install/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/openmeetings-mediaserver/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/openmeetings-screenshare/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/openmeetings-server/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/openmeetings-service/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/openmeetings-util/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/openmeetings-web/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/openmeetings-webservice/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="apache.snapshots" />
|
||||
<option name="name" value="Apache Snapshot Repository" />
|
||||
<option name="url" value="https://repository.apache.org/snapshots" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="sonatype-snapshots" />
|
||||
<option name="name" value="Sonatype Snapshots Repository" />
|
||||
<option name="url" value="https://oss.sonatype.org/content/repositories/snapshots/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,11 @@
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK" />
|
||||
</project>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="WebContextManager">
|
||||
<option name="state">
|
||||
<map>
|
||||
<entry key="file://$PROJECT_DIR$/openmeetings-web/src/main/webapp/test.html" value="file://$PROJECT_DIR$/openmeetings-web/src/main/webapp" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,18 @@
|
||||
Apache OpenMeetings
|
||||
Licensed under Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0
|
||||
Copyright 2011- The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
|
||||
This product includes icons from FamFamFam Icon Set Silk.
|
||||
http://www.famfamfam.com/lab/icons/silk/
|
||||
|
||||
This product includes icons from Yusuke Kamiyamane.
|
||||
http://p.yusukekamiyamane.com/
|
||||
|
||||
This product includes free icons from FatCow.
|
||||
http://www.fatcow.com/free-icons
|
||||
|
||||
This product includes loading indicator animated with CSS from SpinKit
|
||||
https://github.com/tobiasahlin/SpinKit
|
||||
@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>example</title>
|
||||
<style>
|
||||
#app{padding: 32px;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<iframe width="1500" height="800" src="https://aixt.ricitech.com/openmeetings/hash?secure=4ace63b1-b4b4-4bc3-b18c-cd85e624f621"></iframe>
|
||||
</div>
|
||||
<script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<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/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.openmeetings</groupId>
|
||||
<artifactId>openmeetings-parent</artifactId>
|
||||
<version>7.2.0-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>openmeetings-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Openmeetings Core</name>
|
||||
<description>Module for OpenMeetings core classes and services</description>
|
||||
<properties>
|
||||
<site.basedir>${project.parent.basedir}</site.basedir>
|
||||
<autoModuleName>apache.openmeetings.core</autoModuleName>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<configuration>
|
||||
<groups>
|
||||
<group>
|
||||
<title>OM core components</title>
|
||||
<packages>org.apache.openmeetings.core</packages>
|
||||
</group>
|
||||
</groups>
|
||||
<skip>${site.skip}</skip>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>javadoc-no-fork</goal>
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.openmeetings</groupId>
|
||||
<artifactId>openmeetings-db</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.wicket</groupId>
|
||||
<artifactId>wicket-ioc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.directory.api</groupId>
|
||||
<artifactId>api-all</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jodconverter</groupId>
|
||||
<artifactId>jodconverter-local</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.asteriskjava</groupId>
|
||||
<artifactId>asterisk-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.sip</groupId>
|
||||
<artifactId>jain-sip-ri</artifactId>
|
||||
</dependency>
|
||||
<!-- FIXME TODO -->
|
||||
<dependency>
|
||||
<groupId>com.sun.activation</groupId>
|
||||
<artifactId>jakarta.activation</artifactId> <!-- required for jakarta.mail -->
|
||||
<version>2.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.annotation</groupId>
|
||||
<artifactId>javax.annotation-api</artifactId>
|
||||
</dependency>
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.openmeetings</groupId>
|
||||
<artifactId>openmeetings-util</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -0,0 +1,387 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.converter;
|
||||
|
||||
import static org.apache.commons.io.FileUtils.copyFile;
|
||||
import static org.apache.commons.lang3.math.NumberUtils.toInt;
|
||||
import static org.apache.openmeetings.util.CalendarHelper.formatMillis;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PNG;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.getPublicDir;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.getRecordingChunk;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.getStreamsSubDir;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PATH_FFMPEG;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PATH_IMAGEMAGIC;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PATH_SOX;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getAudioBitrate;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getAudioRate;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getVideoPreset;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
|
||||
import org.apache.openmeetings.db.dao.file.FileItemLogDao;
|
||||
import org.apache.openmeetings.db.dao.record.RecordingChunkDao;
|
||||
import org.apache.openmeetings.db.dao.record.RecordingDao;
|
||||
import org.apache.openmeetings.db.entity.file.BaseFileItem;
|
||||
import org.apache.openmeetings.db.entity.record.Recording;
|
||||
import org.apache.openmeetings.db.entity.record.RecordingChunk;
|
||||
import org.apache.openmeetings.db.entity.record.RecordingChunk.Status;
|
||||
import org.apache.openmeetings.util.process.ProcessHelper;
|
||||
import org.apache.openmeetings.util.process.ProcessResult;
|
||||
import org.apache.openmeetings.util.process.ProcessResultList;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public abstract class BaseConverter {
|
||||
private static final Logger log = LoggerFactory.getLogger(BaseConverter.class);
|
||||
private static final Pattern p = Pattern.compile("\\d{2,5}(x)\\d{2,5}");
|
||||
public static final String EXEC_EXT = System.getProperty("os.name").toUpperCase(Locale.ROOT).indexOf("WINDOWS") < 0 ? "" : ".exe";
|
||||
private static final int MINUTE_MULTIPLIER = 60 * 1000;
|
||||
public static final int TIME_TO_WAIT_FOR_FRAME = 5 * MINUTE_MULTIPLIER;
|
||||
public static final double HALF_STEP = 1. / 2;
|
||||
|
||||
@Autowired
|
||||
protected ConfigurationDao cfgDao;
|
||||
@Autowired
|
||||
protected RecordingChunkDao chunkDao;
|
||||
@Autowired
|
||||
protected FileItemLogDao logDao;
|
||||
@Autowired
|
||||
protected RecordingDao recordingDao;
|
||||
|
||||
protected record Dimension(int width, int height) {}
|
||||
|
||||
private String getPath(String key, String app) {
|
||||
final String cfg = cfgDao.getString(key, "");
|
||||
StringBuilder path = new StringBuilder(cfg);
|
||||
if (!Strings.isEmpty(path) && !cfg.endsWith(File.separator)) {
|
||||
path.append(File.separator);
|
||||
}
|
||||
path.append(app).append(EXEC_EXT);
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
public String getPathToFFMPEG() {
|
||||
return getPath(CONFIG_PATH_FFMPEG, "ffmpeg");
|
||||
}
|
||||
|
||||
protected String getPathToSoX() {
|
||||
return getPath(CONFIG_PATH_SOX, "sox");
|
||||
}
|
||||
|
||||
protected String getPathToConvert() {
|
||||
return getPath(CONFIG_PATH_IMAGEMAGIC, "convert");
|
||||
}
|
||||
|
||||
protected File getStreamFolder(Recording recording) {
|
||||
return getStreamsSubDir(recording.getRoomId());
|
||||
}
|
||||
|
||||
protected long diff(Date from, Date to) {
|
||||
return from == null || to == null ? 0 : from.getTime() - to.getTime();
|
||||
}
|
||||
|
||||
protected double diffSeconds(Date from, Date to) {
|
||||
return diffSeconds(diff(from, to));
|
||||
}
|
||||
|
||||
protected double diffSeconds(long val) {
|
||||
return ((double)val) / 1000;
|
||||
}
|
||||
|
||||
protected void updateDuration(Recording r) {
|
||||
r.setDuration(formatMillis(diff(r.getRecordEnd(), r.getRecordStart())));
|
||||
}
|
||||
|
||||
protected void deleteFileIfExists(File f) throws IOException {
|
||||
Files.deleteIfExists(f.toPath());
|
||||
}
|
||||
|
||||
private List<String> mergeAudioToWaves(List<File> waveFiles, File wav) throws IOException {
|
||||
List<String> argv = new ArrayList<>();
|
||||
|
||||
argv.add(getPathToSoX());
|
||||
argv.add("-m");
|
||||
for (File arg : waveFiles) {
|
||||
argv.add(arg.getCanonicalPath());
|
||||
}
|
||||
argv.add(wav.getCanonicalPath());
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
protected void createWav(Recording r, ProcessResultList logs, File streamFolder, List<File> waveFiles, File wav, List<RecordingChunk> chunks) throws IOException {
|
||||
deleteFileIfExists(wav);
|
||||
stripAudioFirstPass(r, logs, waveFiles, streamFolder, chunks == null ? chunkDao.getNotScreenChunksByRecording(r.getId()) : chunks);
|
||||
if (waveFiles.isEmpty()) {
|
||||
// create default Audio to merge it. strip to content length
|
||||
String oneSecWav = new File(getPublicDir(), "one_second.wav").getCanonicalPath();
|
||||
|
||||
// Calculate delta at beginning
|
||||
double duration = diffSeconds(r.getRecordEnd(), r.getRecordStart());
|
||||
|
||||
List<String> cmd = List.of(getPathToSoX(), oneSecWav, wav.getCanonicalPath(), "pad", "0", String.valueOf(duration));
|
||||
|
||||
logs.add(ProcessHelper.exec("generateSampleAudio", cmd));
|
||||
} else if (waveFiles.size() == 1) {
|
||||
copyFile(waveFiles.get(0), wav);
|
||||
} else {
|
||||
logs.add(ProcessHelper.exec("mergeAudioToWaves", mergeAudioToWaves(waveFiles, wav)));
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> addSoxPad(ProcessResultList logs, String job, double length, double position, File inFile, File outFile) throws IOException {
|
||||
if (length < 0 || position < 0) {
|
||||
log.debug("::addSoxPad {} Invalid parameters: length = {}; position = {}; inFile = {}", job, length, position, inFile);
|
||||
}
|
||||
List<String> argv = List.of(getPathToSoX(), inFile.getCanonicalPath(), outFile.getCanonicalPath(), "pad"
|
||||
, String.valueOf(length < 0 ? 0 : length)
|
||||
, String.valueOf(position < 0 ? 0 : position));
|
||||
|
||||
logs.add(ProcessHelper.exec(job, argv));
|
||||
return argv;
|
||||
}
|
||||
|
||||
public static void printChunkInfo(RecordingChunk chunk, String prefix) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("### {}:: recording id {}; stream with id {}; current status: {} ", prefix, chunk.getRecording().getId()
|
||||
, chunk.getId(), chunk.getStreamStatus());
|
||||
File chunkFlv = getRecordingChunk(chunk.getRecording().getRoomId(), chunk.getStreamName());
|
||||
log.debug("### {}:: Chunk file [{}] exists ? {}; size: {}, lastModified: {} ", prefix, chunkFlv.getPath(), chunkFlv.exists(), chunkFlv.length(), chunkFlv.lastModified());
|
||||
}
|
||||
}
|
||||
|
||||
protected RecordingChunk waitForTheStream(long chunkId) {
|
||||
RecordingChunk chunk = null;
|
||||
try {
|
||||
long counter = 0;
|
||||
long maxTimestamp = 0;
|
||||
while (true) {
|
||||
chunk = chunkDao.get(chunkId);
|
||||
|
||||
if (chunk.getStreamStatus() == Status.STOPPED) {
|
||||
printChunkInfo(chunk, "Stream now written");
|
||||
log.debug("### Chunk stopped, unblocking thread ... " );
|
||||
break;
|
||||
}
|
||||
File chunkFlv = getRecordingChunk(chunk.getRecording().getRoomId(), chunk.getStreamName());
|
||||
if (chunkFlv.exists() && maxTimestamp < chunkFlv.lastModified()) {
|
||||
maxTimestamp = chunkFlv.lastModified();
|
||||
}
|
||||
if (maxTimestamp + TIME_TO_WAIT_FOR_FRAME < System.currentTimeMillis()) {
|
||||
log.debug("### long time without any update, closing ... ");
|
||||
chunk.setStreamStatus(Status.STOPPED);
|
||||
chunkDao.update(chunk);
|
||||
break;
|
||||
}
|
||||
if (++counter % 1000 == 0) {
|
||||
printChunkInfo(chunk, "Still waiting");
|
||||
}
|
||||
|
||||
log.trace("### Stream not yet written Thread Sleep - {}", chunkId);
|
||||
Thread.sleep(100L);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
private void stripAudioFirstPass(Recording recording,
|
||||
ProcessResultList logs,
|
||||
List<File> waveFiles, File streamFolder,
|
||||
List<RecordingChunk> chunks) {
|
||||
try {
|
||||
// Init variables
|
||||
log.debug("### Chunks count - {}", chunks.size());
|
||||
log.debug("###################################################");
|
||||
|
||||
for (RecordingChunk chunk : chunks) {
|
||||
long chunkId = chunk.getId();
|
||||
log.debug("### processing chunk: {}", chunkId);
|
||||
if (chunk.getStreamStatus() == Status.NONE) {
|
||||
log.debug("Stream has not been started, error in recording {}", chunkId);
|
||||
continue;
|
||||
}
|
||||
|
||||
chunk = waitForTheStream(chunkId);
|
||||
|
||||
File inputFlvFile = getRecordingChunk(chunk.getRecording().getRoomId(), chunk.getStreamName());
|
||||
|
||||
File outputWav = new File(streamFolder, chunk.getStreamName() + "_WAVE.wav");
|
||||
|
||||
log.debug("FLV File Name: {} Length: {} ", inputFlvFile.getName(), inputFlvFile.length());
|
||||
|
||||
if (inputFlvFile.exists()) {
|
||||
List<String> argv = List.of(
|
||||
getPathToFFMPEG(), "-y"
|
||||
, "-i", inputFlvFile.getCanonicalPath()
|
||||
, "-af", String.format("aresample=%s:min_comp=0.001:min_hard_comp=0.100000", getAudioBitrate())
|
||||
, outputWav.getCanonicalPath());
|
||||
//there might be no audio in the stream
|
||||
logs.add(ProcessHelper.exec("stripAudioFromFLVs", argv, true));
|
||||
}
|
||||
|
||||
if (outputWav.exists() && outputWav.length() != 0) {
|
||||
// Strip Wave to Full Length
|
||||
// Strip Wave to Full Length
|
||||
String hashFileFullName = chunk.getStreamName() + "_FULL_WAVE.wav";
|
||||
File outputFullWav = new File(streamFolder, hashFileFullName);
|
||||
|
||||
// Calculate delta at beginning
|
||||
double startPad = diffSeconds(chunk.getStart(), recording.getRecordStart());
|
||||
|
||||
// Calculate delta at ending
|
||||
double endPad = diffSeconds(recording.getRecordEnd(), chunk.getEnd());
|
||||
|
||||
addSoxPad(logs, "addStartEndToAudio", startPad, endPad, outputWav, outputFullWav);
|
||||
|
||||
// Fix for Audio Length - Invalid Audio Length in Recorded Files
|
||||
// Audio must match 100% the Video
|
||||
log.debug("############################################");
|
||||
log.debug("Trim Audio to Full Length -- Start");
|
||||
|
||||
if (!outputFullWav.exists()) {
|
||||
throw new ConversionException("Audio File does not exist , could not extract the Audio correctly");
|
||||
}
|
||||
|
||||
// Finally add it to the row!
|
||||
waveFiles.add(outputFullWav);
|
||||
}
|
||||
chunkDao.update(chunk);
|
||||
}
|
||||
} catch (Exception err) {
|
||||
log.error("[stripAudioFirstPass]", err);
|
||||
}
|
||||
}
|
||||
|
||||
protected String getDimensions(Recording r, char delim) {
|
||||
return String.format("%s%s%s", r.getWidth(), delim, r.getHeight());
|
||||
}
|
||||
|
||||
protected String getDimensions(Recording r) {
|
||||
return getDimensions(r, 'x');
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be overridden to supply any additional parameters
|
||||
*
|
||||
* @param r - recording to get params from
|
||||
* @return additional conversion parameters
|
||||
*/
|
||||
protected List<String> additionalMp4OutParams(Recording r) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
private List<String> addMp4OutParams(Recording r, List<String> argv, boolean interview, String mp4path) {
|
||||
argv.addAll(List.of(
|
||||
"-c:v", "h264" //
|
||||
, "-crf", "24"
|
||||
, "-vsync", "0"
|
||||
, "-pix_fmt", "yuv420p"
|
||||
, "-preset", getVideoPreset()
|
||||
, "-profile:v", "baseline"
|
||||
, "-level", "3.0"
|
||||
, "-movflags", "faststart"
|
||||
, "-c:a", "aac"
|
||||
, "-ar", String.valueOf(getAudioRate())
|
||||
, "-b:a", getAudioBitrate()
|
||||
));
|
||||
if (!interview) {
|
||||
argv.addAll(List.of("-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2"));
|
||||
}
|
||||
argv.addAll(additionalMp4OutParams(r));
|
||||
argv.add(mp4path);
|
||||
return argv;
|
||||
}
|
||||
|
||||
protected String convertToMp4(Recording r, List<String> inArgv, boolean interview, ProcessResultList logs) throws IOException {
|
||||
String mp4path = r.getFile().getCanonicalPath();
|
||||
List<String> argv = new ArrayList<>(List.of(getPathToFFMPEG(), "-y"));
|
||||
argv.addAll(inArgv);
|
||||
logs.add(ProcessHelper.exec("generate MP4", addMp4OutParams(r, argv, interview, mp4path)));
|
||||
return mp4path;
|
||||
}
|
||||
|
||||
protected void convertToPng(BaseFileItem f, String mp4path, ProcessResultList logs) throws IOException {
|
||||
// Extract first Image for preview purpose
|
||||
// ffmpeg -i movie.mp4 -vf "thumbnail,scale=640:-1" -frames:v 1 movie.png
|
||||
File png = f.getFile(EXTENSION_PNG);
|
||||
List<String> argv = List.of(
|
||||
getPathToFFMPEG(), "-y"
|
||||
, "-i", mp4path
|
||||
, "-vf", "thumbnail,scale=640:-1"
|
||||
, "-frames:v", "1"
|
||||
, png.getCanonicalPath());
|
||||
logs.add(ProcessHelper.exec("generate preview PNG :: " + f.getHash(), argv));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the width height from the FFMPEG output
|
||||
*
|
||||
* @param txt FFMPEG output
|
||||
* @return {@link Dimension} parsed
|
||||
*/
|
||||
protected static Dimension getDimension(String txt, Dimension def) {
|
||||
Matcher matcher = p.matcher(txt);
|
||||
|
||||
if (matcher.find()) {
|
||||
String foundResolution = txt.substring(matcher.start(), matcher.end());
|
||||
String[] resolutions = foundResolution.split("x");
|
||||
return new Dimension(toInt(resolutions[0]), toInt(resolutions[1]));
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
protected void finalizeRec(Recording r, String mp4path, ProcessResultList logs) throws IOException {
|
||||
convertToPng(r, mp4path, logs);
|
||||
|
||||
updateDuration(r);
|
||||
r.setStatus(Recording.Status.PROCESSED);
|
||||
}
|
||||
|
||||
protected void postProcess(Recording r, ProcessResultList logs) {
|
||||
logDao.delete(r);
|
||||
for (ProcessResult res : logs.getJobs()) {
|
||||
logDao.add("generateFFMPEG", r, res);
|
||||
}
|
||||
}
|
||||
|
||||
protected void postProcess(List<File> waveFiles) {
|
||||
// Delete Wave Files
|
||||
for (File audio : waveFiles) {
|
||||
try {
|
||||
Files.deleteIfExists(audio.toPath());
|
||||
} catch (IOException e) {
|
||||
log.error("Unexpected error while deleting waveFile {}", audio, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.converter;
|
||||
|
||||
public class ConversionException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ConversionException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.converter;
|
||||
|
||||
import static org.apache.commons.io.FileUtils.copyFile;
|
||||
import static org.apache.openmeetings.core.converter.BaseConverter.HALF_STEP;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PDF;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_PATH_OFFICE;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Optional;
|
||||
import java.util.function.DoubleConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
|
||||
import org.apache.openmeetings.db.entity.file.FileItem;
|
||||
import org.apache.openmeetings.util.StoredFile;
|
||||
import org.apache.openmeetings.util.process.ProcessResult;
|
||||
import org.apache.openmeetings.util.process.ProcessResultList;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.jodconverter.core.job.ConversionJob;
|
||||
import org.jodconverter.core.office.OfficeException;
|
||||
import org.jodconverter.core.office.OfficeManager;
|
||||
import org.jodconverter.local.LocalConverter;
|
||||
import org.jodconverter.local.office.LocalOfficeManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class DocumentConverter {
|
||||
private static final Logger log = LoggerFactory.getLogger(DocumentConverter.class);
|
||||
private static final String JOD_JOD_NAME = "doJodConvert";
|
||||
|
||||
@Autowired
|
||||
protected ConfigurationDao cfgDao;
|
||||
@Autowired
|
||||
private ImageConverter imageConverter;
|
||||
|
||||
public ProcessResultList convertPDF(FileItem f, StoredFile sf) throws Exception {
|
||||
return convertPDF(f, sf, new ProcessResultList(), Optional.empty());
|
||||
}
|
||||
|
||||
public ProcessResultList convertPDF(FileItem f, StoredFile sf, ProcessResultList logs, Optional<DoubleConsumer> progress) throws Exception {
|
||||
boolean fullProcessing = !sf.isPdf();
|
||||
File original = f.getFile(sf.getExt());
|
||||
File pdf = f.getFile(EXTENSION_PDF);
|
||||
log.debug("fullProcessing: {}", fullProcessing);
|
||||
if (fullProcessing) {
|
||||
log.debug("-- running JOD --");
|
||||
logs.add(doJodConvert(original, pdf));
|
||||
} else if (!EXTENSION_PDF.equals(sf.getExt())) {
|
||||
copyFile(original, pdf);
|
||||
}
|
||||
progress.ifPresent(theProgress -> theProgress.accept(HALF_STEP));
|
||||
|
||||
log.debug("-- generate page images --");
|
||||
return imageConverter.convertDocument(f, pdf, logs, progress);
|
||||
}
|
||||
|
||||
public static void createOfficeManager(String officePath, Function<OfficeManager, ConversionJob> job) throws OfficeException {
|
||||
OfficeManager manager = null;
|
||||
try {
|
||||
LocalOfficeManager.Builder builder = LocalOfficeManager.builder();
|
||||
if (!Strings.isEmpty(officePath)) {
|
||||
builder.officeHome(officePath);
|
||||
}
|
||||
manager = builder.build();
|
||||
manager.start();
|
||||
if (job != null) {
|
||||
job.apply(manager).execute();
|
||||
}
|
||||
} finally {
|
||||
if (manager != null) {
|
||||
manager.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates PDF using JOD Library (external library)
|
||||
*
|
||||
* @param in - file to convert
|
||||
* @param out - file to write result
|
||||
* @return - result of the conversion as {@link ProcessResult}
|
||||
*/
|
||||
public ProcessResult doJodConvert(File in, File out) {
|
||||
try {
|
||||
createOfficeManager(cfgDao.getString(CONFIG_PATH_OFFICE, null)
|
||||
, man -> LocalConverter.make(man).convert(in).to(out));
|
||||
} catch (Exception ex) {
|
||||
log.error(JOD_JOD_NAME, ex);
|
||||
return new ProcessResult(JOD_JOD_NAME, ex.getMessage(), ex);
|
||||
}
|
||||
return new ProcessResult(JOD_JOD_NAME, "Document converted successfully", null)
|
||||
.setExitCode(0);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.converter;
|
||||
|
||||
import org.apache.openmeetings.db.entity.record.Recording;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface IRecordingConverter {
|
||||
void startConversion(Recording rec);
|
||||
}
|
||||
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.converter;
|
||||
|
||||
import static org.apache.commons.io.FileUtils.copyFile;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.DOC_PAGE_PREFIX;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PNG;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.PNG_MIME_TYPE;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.PROFILE_FILE_NAME;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.getUploadProfilesUserDir;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DOCUMENT_DPI;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DOCUMENT_QUALITY;
|
||||
import static org.apache.openmeetings.util.process.ProcessResult.ZERO;
|
||||
import static org.apache.tika.metadata.HttpHeaders.CONTENT_TYPE;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.DoubleConsumer;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.openmeetings.db.dao.user.UserDao;
|
||||
import org.apache.openmeetings.db.entity.file.BaseFileItem;
|
||||
import org.apache.openmeetings.db.entity.file.FileItem;
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
import org.apache.openmeetings.util.OmFileHelper;
|
||||
import org.apache.openmeetings.util.StoredFile;
|
||||
import org.apache.openmeetings.util.process.ProcessHelper;
|
||||
import org.apache.openmeetings.util.process.ProcessResult;
|
||||
import org.apache.openmeetings.util.process.ProcessResultList;
|
||||
import org.apache.tika.metadata.Metadata;
|
||||
import org.apache.tika.metadata.TIFF;
|
||||
import org.apache.tika.parser.ParseContext;
|
||||
import org.apache.tika.parser.Parser;
|
||||
import org.apache.tika.parser.image.ImageParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
@Component
|
||||
public class ImageConverter extends BaseConverter {
|
||||
private static final Logger log = LoggerFactory.getLogger(ImageConverter.class);
|
||||
private static final String PAGE_TMPLT = DOC_PAGE_PREFIX + "-%04d." + EXTENSION_PNG;
|
||||
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
|
||||
public ProcessResultList convertImage(BaseFileItem f, StoredFile sf, Optional<DoubleConsumer> progress) throws IOException {
|
||||
return convertImage(f, sf, new ProcessResultList(), progress);
|
||||
}
|
||||
|
||||
public ProcessResultList convertImage(BaseFileItem f, StoredFile sf, ProcessResultList logs, Optional<DoubleConsumer> progress) throws IOException {
|
||||
File png = f.getFile(EXTENSION_PNG);
|
||||
if (!sf.isPng()) {
|
||||
File img = f.getFile(sf.getExt());
|
||||
|
||||
log.debug("##### convertImage destinationFile: {}", png);
|
||||
logs.add(convertSinglePng(img, png));
|
||||
} else if (!png.exists()){
|
||||
copyFile(f.getFile(sf.getExt()), png);
|
||||
}
|
||||
progress.ifPresent(theProgress -> theProgress.accept(HALF_STEP));
|
||||
logs.add(initSize(f, png, PNG_MIME_TYPE));
|
||||
progress.ifPresent(theProgress -> theProgress.accept(HALF_STEP));
|
||||
return logs;
|
||||
}
|
||||
|
||||
public ProcessResultList convertImageUserProfile(File file, Long userId) throws Exception {
|
||||
ProcessResultList returnMap = new ProcessResultList();
|
||||
|
||||
// User Profile Update
|
||||
Files.newDirectoryStream(
|
||||
getUploadProfilesUserDir(userId).toPath()
|
||||
, fi -> fi.toString().endsWith(EXTENSION_PNG))
|
||||
.forEach(path -> FileUtils.deleteQuietly(path.toFile()));
|
||||
|
||||
File destinationFile = OmFileHelper.getNewFile(getUploadProfilesUserDir(userId), PROFILE_FILE_NAME, EXTENSION_PNG);
|
||||
returnMap.add(resize(file, destinationFile, 250, 250, true));
|
||||
|
||||
// Delete old one
|
||||
Files.deleteIfExists(file.toPath());
|
||||
|
||||
String img = destinationFile.getName();
|
||||
User us = userDao.get(userId);
|
||||
us.setPictureUri(img);
|
||||
userDao.update(us, userId);
|
||||
|
||||
return returnMap;
|
||||
}
|
||||
|
||||
private String getDpi() {
|
||||
return cfgDao.getString(CONFIG_DOCUMENT_DPI, "150");
|
||||
}
|
||||
|
||||
private String getQuality() {
|
||||
return cfgDao.getString(CONFIG_DOCUMENT_QUALITY, "90");
|
||||
}
|
||||
|
||||
private static ProcessResult initSize(BaseFileItem f, File img, String mime) {
|
||||
ProcessResult res = new ProcessResult();
|
||||
res.setProcess("get image dimensions :: " + f.getId());
|
||||
final Parser parser = new ImageParser();
|
||||
try (InputStream is = new FileInputStream(img)) {
|
||||
Metadata metadata = new Metadata();
|
||||
metadata.set(CONTENT_TYPE, mime);
|
||||
parser.parse(is, new DefaultHandler(), metadata, new ParseContext());
|
||||
f.setWidth(Integer.valueOf(metadata.get(TIFF.IMAGE_WIDTH)));
|
||||
f.setHeight(Integer.valueOf(metadata.get(TIFF.IMAGE_LENGTH)));
|
||||
res.setExitCode(ZERO);
|
||||
} catch (Exception e) {
|
||||
log.error("Error while getting dimensions", e);
|
||||
res.setError("Error while getting dimensions");
|
||||
res.setException(e.getMessage());
|
||||
res.setExitCode(-1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param in - input file
|
||||
* @param out - output file
|
||||
* @return - conversion result
|
||||
* @throws IOException - if any Io exception occurs while processing
|
||||
*
|
||||
*/
|
||||
private ProcessResult convertSinglePng(File in, File out) throws IOException {
|
||||
List<String> argv = List.of(getPathToConvert(), in.getCanonicalPath(), out.getCanonicalPath());
|
||||
|
||||
return ProcessHelper.exec("convertSinglePng", argv);
|
||||
}
|
||||
|
||||
public ProcessResult resize(File in, File out, Integer width, Integer height, boolean max) throws IOException {
|
||||
List<String> argv = List.of(getPathToConvert()
|
||||
, "-resize", (width == null ? "" : width) + (height == null ? "" : "x" + height) + (max ? ">" : "")
|
||||
, in.getCanonicalPath(), out.getCanonicalPath());
|
||||
return ProcessHelper.exec("resize", argv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts PDF document to the series of images
|
||||
*
|
||||
* @param f - {@link FileItem} object to write number of pages and size
|
||||
* @param pdf - input PDF document
|
||||
* @param logs - logs of the conversion
|
||||
* @return - result of conversion
|
||||
* @throws IOException in case IO exception occurred
|
||||
*/
|
||||
public ProcessResultList convertDocument(FileItem f, File pdf, ProcessResultList logs, Optional<DoubleConsumer> progress) throws IOException {
|
||||
log.debug("convertDocument");
|
||||
List<String> argv = List.of(
|
||||
getPathToConvert()
|
||||
, "-density", getDpi()
|
||||
, "-define", "pdf:use-cropbox=true"
|
||||
, pdf.getCanonicalPath()
|
||||
, "+profile", "'*'"
|
||||
, "-quality", getQuality()
|
||||
, new File(pdf.getParentFile(), PAGE_TMPLT).getCanonicalPath());
|
||||
ProcessResult res = ProcessHelper.exec("convert PDF to images", argv);
|
||||
logs.add(res);
|
||||
progress.ifPresent(theProgress -> theProgress.accept(1. / 4));
|
||||
if (res.isOk()) {
|
||||
File[] pages = pdf.getParentFile().listFiles(fi -> fi.isFile() && fi.getName().startsWith(DOC_PAGE_PREFIX) && fi.getName().endsWith(EXTENSION_PNG));
|
||||
if (pages == null || pages.length == 0) {
|
||||
f.setCount(0);
|
||||
} else {
|
||||
f.setCount(pages.length);
|
||||
logs.add(initSize(f, pages[0], PNG_MIME_TYPE));
|
||||
}
|
||||
}
|
||||
progress.ifPresent(theProgress -> theProgress.accept(1. / 4));
|
||||
return logs;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.converter;
|
||||
|
||||
import static java.util.UUID.randomUUID;
|
||||
import static org.apache.openmeetings.util.CalendarHelper.formatMillis;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_MP4;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.getRecordingChunk;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.openmeetings.db.entity.record.Recording;
|
||||
import org.apache.openmeetings.db.entity.record.RecordingChunk;
|
||||
import org.apache.openmeetings.util.OmFileHelper;
|
||||
import org.apache.openmeetings.util.process.ProcessHelper;
|
||||
import org.apache.openmeetings.util.process.ProcessResult;
|
||||
import org.apache.openmeetings.util.process.ProcessResultList;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class InterviewConverter extends BaseConverter implements IRecordingConverter {
|
||||
private static final Logger log = LoggerFactory.getLogger(InterviewConverter.class);
|
||||
private static final int WIDTH = 320;
|
||||
private static final int HEIGHT = 260;
|
||||
private String interviewCam;
|
||||
private String interviewBlank;
|
||||
|
||||
private void init() throws ConversionException, IOException {
|
||||
// Default Image for empty interview video pods
|
||||
final File interviewCamFile = new File(OmFileHelper.getImagesDir(), "interview_webcam.png");
|
||||
if (!interviewCamFile.exists()) {
|
||||
throw new ConversionException("defaultInterviewImageFile does not exist!");
|
||||
}
|
||||
interviewCam = interviewCamFile.getCanonicalPath();
|
||||
final File interviewBlankFile = new File(OmFileHelper.getImagesDir(), "blank.png");
|
||||
if (!interviewBlankFile.exists()) {
|
||||
throw new ConversionException("defaultInterviewImageFile does not exist!");
|
||||
}
|
||||
interviewBlank = interviewBlankFile.getCanonicalPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startConversion(Recording r) {
|
||||
if (r == null) {
|
||||
log.warn("Conversion is NOT started. Recording passed is NULL");
|
||||
return;
|
||||
}
|
||||
ProcessResultList logs = new ProcessResultList();
|
||||
List<File> waveFiles = new ArrayList<>();
|
||||
try {
|
||||
log.debug("recording {}", r.getId());
|
||||
if (interviewCam == null) {
|
||||
init();
|
||||
}
|
||||
if (Strings.isEmpty(r.getHash())) {
|
||||
r.setHash(randomUUID().toString());
|
||||
}
|
||||
r.setStatus(Recording.Status.CONVERTING);
|
||||
r = recordingDao.update(r);
|
||||
|
||||
File streamFolder = getStreamFolder(r);
|
||||
List<RecordingChunk> chunks = chunkDao.getByRecording(r.getId());
|
||||
|
||||
File wav = new File(streamFolder, String.format("INTERVIEW_%s_FINAL_WAVE.wav", r.getId()));
|
||||
createWav(r, logs, streamFolder, waveFiles, wav, chunks);
|
||||
|
||||
// Merge Audio with Video / Calculate resulting video
|
||||
|
||||
// group by sid first to get all pods
|
||||
Map<String, List<RecordingChunk>> cunksBySid = chunks.stream().collect(
|
||||
Collectors.groupingBy(RecordingChunk::getSid
|
||||
, LinkedHashMap::new
|
||||
, Collectors.collectingAndThen(Collectors.toList(), l -> l.stream().sorted(Comparator.comparing(RecordingChunk::getStart)).toList())));
|
||||
List<String> pods = new ArrayList<>();
|
||||
for (Entry<String, List<RecordingChunk>> e : cunksBySid.entrySet()) {
|
||||
int podIdx = pods.size();
|
||||
Date pStart = r.getRecordStart();
|
||||
List<PodPart> parts = new ArrayList<>();
|
||||
pStart = processParts(r.getRoomId(), e.getValue(), logs, podIdx, parts, pStart);
|
||||
if (!parts.isEmpty()) {
|
||||
String podX = new File(streamFolder, String.format("rec_%s_pod_%s.%s", r.getId(), podIdx, EXTENSION_MP4)).getCanonicalPath();
|
||||
long diff = diff(r.getRecordEnd(), pStart);
|
||||
PodPart.add(parts, diff);
|
||||
|
||||
createPod(podX, interviewCam, podIdx, parts, logs);
|
||||
pods.add(podX);
|
||||
}
|
||||
}
|
||||
int numPods = pods.size();
|
||||
if (numPods == 0) {
|
||||
ProcessResult res = new ProcessResult();
|
||||
res.setProcess("CheckStreamFilesExists");
|
||||
res.setError("No valid pods found");
|
||||
res.setExitCode(-1);
|
||||
logs.add(res);
|
||||
return;
|
||||
}
|
||||
double ratio = Math.sqrt(numPods / Math.sqrt(2));
|
||||
int w = ratio < 1 ? numPods : (int)Math.round(ratio);
|
||||
w = Math.max(w, (int)Math.round(1. * numPods / w));
|
||||
List<PodPart> missingParts = new ArrayList<>();
|
||||
PodPart.add(missingParts, diff(r.getRecordEnd(), r.getRecordStart()));
|
||||
String missingPod = new File(streamFolder, String.format("rec_%s_pod_%s.%s", r.getId(), numPods, EXTENSION_MP4)).getCanonicalPath();
|
||||
createPod(missingPod, interviewBlank, numPods, missingParts, logs);
|
||||
for (int i = numPods % w; i < w; ++i) {
|
||||
pods.add(missingPod);
|
||||
}
|
||||
|
||||
r.setWidth(w * WIDTH);
|
||||
r.setHeight((numPods / w) * HEIGHT);
|
||||
|
||||
String mp4path = convertToMp4(r, getFinalArgs(pods, wav, w), numPods != 1, logs);
|
||||
|
||||
finalizeRec(r, mp4path, logs);
|
||||
} catch (Exception err) {
|
||||
log.error("[startConversion]", err);
|
||||
r.setStatus(Recording.Status.ERROR);
|
||||
} finally {
|
||||
if (Recording.Status.CONVERTING == r.getStatus()) {
|
||||
r.setStatus(Recording.Status.ERROR);
|
||||
}
|
||||
postProcess(r, logs);
|
||||
postProcess(waveFiles);
|
||||
recordingDao.update(r);
|
||||
}
|
||||
}
|
||||
|
||||
private void createPod(String podX, String image, int podIdx, List<PodPart> parts, ProcessResultList logs) throws ConversionException {
|
||||
/* create continuous pod
|
||||
* ffmpeg \
|
||||
* -loop 1 -framerate 24 -t 10 -i image1.jpg \
|
||||
* -i video.mp4 \
|
||||
* -loop 1 -framerate 24 -t 10 -i image2.jpg \
|
||||
* -loop 1 -framerate 24 -t 10 -i image3.jpg \
|
||||
* -filter_complex "[0][1][2][3]concat=n=4:v=1:a=0" out.mp4
|
||||
*/
|
||||
List<String> args = new ArrayList<>();
|
||||
args.add(getPathToFFMPEG());
|
||||
args.add("-y");
|
||||
StringBuilder videos = new StringBuilder();
|
||||
StringBuilder concat = new StringBuilder();
|
||||
for (int i = 0; i < parts.size(); ++i) {
|
||||
PodPart p = parts.get(i);
|
||||
if (p.getFile() == null) {
|
||||
args.add("-loop");
|
||||
args.add("1");
|
||||
args.add("-t");
|
||||
args.add(formatMillis(p.getDuration()));
|
||||
args.add("-i");
|
||||
args.add(image);
|
||||
} else {
|
||||
args.add("-t");
|
||||
args.add(formatMillis(p.getDuration()));
|
||||
args.add("-i");
|
||||
args.add(p.getFile());
|
||||
}
|
||||
videos.append('[').append(i).append(']')
|
||||
.append("scale=").append(WIDTH).append(':').append(HEIGHT).append(",setsar=1:1")
|
||||
.append("[v").append(i).append("]; ");
|
||||
concat.append("[v").append(i).append(']');
|
||||
}
|
||||
args.add("-filter_complex");
|
||||
args.add(concat.insert(0, videos).append("concat=n=").append(parts.size()).append(":v=1:a=0").toString());
|
||||
args.add("-an");
|
||||
args.add(podX);
|
||||
ProcessResult res = ProcessHelper.exec("Full video pod_" + podIdx, args, true);
|
||||
logs.add(res);
|
||||
if (res.isWarn()) {
|
||||
throw new ConversionException("Fail to create pod");
|
||||
}
|
||||
}
|
||||
|
||||
private Date processParts(Long roomId, List<RecordingChunk> chunks, ProcessResultList logs, int numPods, List<PodPart> parts, Date pStart) throws IOException {
|
||||
for (RecordingChunk chunk : chunks) {
|
||||
File chunkStream = getRecordingChunk(roomId, chunk.getStreamName());
|
||||
if (!chunkStream.exists()) {
|
||||
log.debug("Chunk stream doesn't exist: {}", chunkStream);
|
||||
continue;
|
||||
}
|
||||
String path = chunkStream.getCanonicalPath();
|
||||
/* CHECK FILE:
|
||||
* ffmpeg -i rec_316_stream_567_2013_08_28_11_51_45.webm -v error -f null file.null
|
||||
*/
|
||||
List<String> args = List.of(getPathToFFMPEG(), "-y"
|
||||
, "-i", path
|
||||
, "-v", "error"
|
||||
, "-f", "null"
|
||||
, "file.null");
|
||||
ProcessResult res = ProcessHelper.exec(String.format("Check chunk pod video_%s_%s", numPods, parts.size()), args, true);
|
||||
logs.add(res);
|
||||
if (!res.isWarn()) {
|
||||
long diff = diff(chunk.isAudioOnly() ? chunk.getEnd() : chunk.getStart(), pStart);
|
||||
PodPart.add(parts, diff);
|
||||
if (!chunk.isAudioOnly()) {
|
||||
parts.add(new PodPart(path, diff(chunk.getEnd(), chunk.getStart())));
|
||||
}
|
||||
pStart = chunk.getEnd();
|
||||
}
|
||||
}
|
||||
return pStart;
|
||||
}
|
||||
|
||||
private static void fillFinalArgsOnlyPod(List<String> args, String pod, File wav) throws IOException {
|
||||
args.add("-i");
|
||||
args.add(pod);
|
||||
args.add("-i");
|
||||
args.add(wav.getCanonicalPath());
|
||||
args.add("-map");
|
||||
args.add("0:v");
|
||||
}
|
||||
|
||||
private static void fillFinalArgsGrid(List<String> args, List<String> pods, File wav, int w) throws IOException {
|
||||
final int numPods = pods.size();
|
||||
/* Creating grid
|
||||
* ffmpeg -i top_l.mp4 -i top_r.mp4 -i bottom_l.mp4 -i bottom_r.mp4 -i audio.mp4 \
|
||||
* -filter_complex "[0:v][1:v]hstack=inputs=2[t];[2:v][3:v]hstack=inputs=2[b];[t][b]vstack=inputs=2[v]" \
|
||||
* -map "[v]" -map 4:a -c:a copy -shortest output.mp4
|
||||
*/
|
||||
StringBuilder cols = new StringBuilder();
|
||||
StringBuilder rows = new StringBuilder();
|
||||
int colCount = 0;
|
||||
int j = 0;
|
||||
for (int i = 0; i < numPods; ++i) {
|
||||
colCount++;
|
||||
args.add("-i");
|
||||
args.add(pods.get(i));
|
||||
cols.append('[').append(i).append(":v]");
|
||||
if (i != 0 && colCount % w == 0) {
|
||||
cols.append("hstack=inputs=").append(colCount);
|
||||
if (j == 0 && i == numPods - 1) {
|
||||
cols.append("[v]");
|
||||
} else {
|
||||
cols.append("[c").append(j).append("];");
|
||||
}
|
||||
rows.append("[c").append(j).append(']');
|
||||
j++;
|
||||
colCount = 0;
|
||||
}
|
||||
if (i == numPods - 1) {
|
||||
if (j > 1) {
|
||||
rows.append("vstack=inputs=").append(j).append("[out];[out]pad=ceil(iw/2)*2:ceil(ih/2)*2[v]");
|
||||
} else {
|
||||
rows.setLength(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
args.add("-i");
|
||||
args.add(wav.getCanonicalPath());
|
||||
args.add("-filter_complex");
|
||||
args.add(cols.append(rows).toString());
|
||||
args.add("-map");
|
||||
args.add("[v]");
|
||||
}
|
||||
|
||||
private static List<String> getFinalArgs(List<String> pods, File wav, int w) throws IOException {
|
||||
final int numPods = pods.size();
|
||||
List<String> args = new ArrayList<>();
|
||||
if (numPods == 1) {
|
||||
fillFinalArgsOnlyPod(args, pods.get(0), wav);
|
||||
} else {
|
||||
fillFinalArgsGrid(args, pods, wav, w);
|
||||
}
|
||||
args.add("-map");
|
||||
args.add(numPods + ":a");
|
||||
args.add("-qmax"); args.add("1");
|
||||
args.add("-qmin"); args.add("1");
|
||||
return args;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> additionalMp4OutParams(Recording r) {
|
||||
return List.of("-s", getDimensions(r));
|
||||
}
|
||||
|
||||
private static class PodPart {
|
||||
final String file;
|
||||
final long duration;
|
||||
|
||||
public PodPart(String file, long duration) {
|
||||
this.file = file;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public PodPart(long duration) {
|
||||
this(null, duration);
|
||||
}
|
||||
|
||||
public String getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public static void add(List<PodPart> parts, long duration) {
|
||||
if (duration > 19L) { // ffmpeg ignores durations less than 19ms, can hang
|
||||
parts.add(new PodPart(duration));
|
||||
} else {
|
||||
log.warn("PodPart with duration less than 19ms found: {}", duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.converter;
|
||||
|
||||
import static java.util.UUID.randomUUID;
|
||||
import static org.apache.openmeetings.util.CalendarHelper.formatMillis;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.getRecordingChunk;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.openmeetings.db.entity.record.Recording;
|
||||
import org.apache.openmeetings.db.entity.record.RecordingChunk;
|
||||
import org.apache.openmeetings.db.entity.record.RecordingChunk.Status;
|
||||
import org.apache.openmeetings.util.process.ProcessResultList;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class RecordingConverter extends BaseConverter implements IRecordingConverter {
|
||||
private static final Logger log = LoggerFactory.getLogger(RecordingConverter.class);
|
||||
|
||||
@Override
|
||||
public void startConversion(Recording r) {
|
||||
if (r == null) {
|
||||
log.warn("Conversion is NOT started. Recording passed is NULL");
|
||||
return;
|
||||
}
|
||||
ProcessResultList logs = new ProcessResultList();
|
||||
List<File> waveFiles = new ArrayList<>();
|
||||
try {
|
||||
log.debug("recording {}", r.getId());
|
||||
|
||||
File streamFolder = getStreamFolder(r);
|
||||
|
||||
RecordingChunk screenChunk = chunkDao.getScreenByRecording(r.getId());
|
||||
|
||||
if (screenChunk == null) {
|
||||
throw new ConversionException("screenMetaData is Null recordingId " + r.getId());
|
||||
}
|
||||
|
||||
if (screenChunk.getStreamStatus() == Status.NONE) {
|
||||
printChunkInfo(screenChunk, "StartConversion");
|
||||
throw new ConversionException("Stream has not been started, error in recording");
|
||||
}
|
||||
if (Strings.isEmpty(r.getHash())) {
|
||||
r.setHash(randomUUID().toString());
|
||||
}
|
||||
r.setStatus(Recording.Status.CONVERTING);
|
||||
r = recordingDao.update(r);
|
||||
|
||||
screenChunk = waitForTheStream(screenChunk.getId());
|
||||
|
||||
// Merge Wave to Full Length
|
||||
File wav = new File(streamFolder, screenChunk.getStreamName() + "_FINAL_WAVE.wav");
|
||||
createWav(r, logs, streamFolder, waveFiles, wav, null);
|
||||
|
||||
chunkDao.update(screenChunk);
|
||||
|
||||
// Merge Audio with Video / Calculate resulting FLV
|
||||
|
||||
String inputScreenFullFlv = getRecordingChunk(r.getRoomId(), screenChunk.getStreamName()).getCanonicalPath();
|
||||
|
||||
// ffmpeg -vcodec flv -qscale 9.5 -r 25 -ar 22050 -ab 32k -s 320x240
|
||||
// -i 65318fb5c54b1bc1b1bca077b493a914_28_12_2009_23_38_17_FINAL_WAVE.wav
|
||||
// -i 65318fb5c54b1bc1b1bca077b493a914_28_12_2009_23_38_17.flv
|
||||
// final1.flv
|
||||
|
||||
String mp4path = convertToMp4(r, List.of(
|
||||
"-itsoffset", formatMillis(diff(screenChunk.getStart(), r.getRecordStart())),
|
||||
"-i", inputScreenFullFlv, "-i", wav.getCanonicalPath()
|
||||
), false, logs);
|
||||
Dimension dim = getDimension(logs.getLast().getError(), null); // will return 100x100 for non-video to be able to play
|
||||
if (dim != null) {
|
||||
r.setWidth(dim.width());
|
||||
r.setHeight(dim.height());
|
||||
}
|
||||
|
||||
finalizeRec(r, mp4path, logs);
|
||||
} catch (Exception err) {
|
||||
log.error("[startConversion]", err);
|
||||
r.setStatus(Recording.Status.ERROR);
|
||||
}
|
||||
postProcess(r, logs);
|
||||
postProcess(waveFiles);
|
||||
recordingDao.update(r);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.converter;
|
||||
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_MP4;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.getCssImagesDir;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.DoubleConsumer;
|
||||
|
||||
import org.apache.openmeetings.db.entity.file.BaseFileItem.Type;
|
||||
import org.apache.openmeetings.db.entity.file.FileItem;
|
||||
import org.apache.openmeetings.util.StoredFile;
|
||||
import org.apache.openmeetings.util.process.ProcessHelper;
|
||||
import org.apache.openmeetings.util.process.ProcessResult;
|
||||
import org.apache.openmeetings.util.process.ProcessResultList;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class VideoConverter extends BaseConverter {
|
||||
private static final Logger log = LoggerFactory.getLogger(VideoConverter.class);
|
||||
private static final double STEP = 1. / 5;
|
||||
|
||||
public void convertVideo(FileItem f, StoredFile sf, ProcessResultList logs, Optional<DoubleConsumer> progress) {
|
||||
try {
|
||||
final File mp4 = f.getFile(EXTENSION_MP4);
|
||||
f.setType(Type.VIDEO);
|
||||
final String ext = sf.getExt();
|
||||
String input = f.getFile(ext).getCanonicalPath();
|
||||
boolean sameExt = EXTENSION_MP4.equals(ext);
|
||||
Path tmp = null;
|
||||
if (sameExt) {
|
||||
//we should do in-place conversion
|
||||
tmp = Files.createTempFile("video", ".mp4");
|
||||
input = Files.move(mp4.toPath(), tmp, REPLACE_EXISTING).toFile().getCanonicalPath();
|
||||
}
|
||||
progress.ifPresent(theProgress -> theProgress.accept(STEP));
|
||||
List<String> args = new ArrayList<>(List.of(getPathToFFMPEG(), "-y"));
|
||||
if (sf.isAudio()) {
|
||||
// need to add background image, it should be jpg since black on transparent will be invisible
|
||||
args.addAll(List.of("-loop", "1"
|
||||
, "-framerate", "24"
|
||||
, "-i", new File(getCssImagesDir(), "audio.jpg").getCanonicalPath()));
|
||||
}
|
||||
args.addAll(List.of("-i", input
|
||||
, "-c:v", "h264"
|
||||
, "-c:a", "aac"
|
||||
, "-pix_fmt", "yuv420p"
|
||||
, "-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2"
|
||||
));
|
||||
if (sf.isAudio()) {
|
||||
args.add("-shortest");
|
||||
}
|
||||
args.add(mp4.getCanonicalPath());
|
||||
ProcessResult res = ProcessHelper.exec("convert to MP4 :: " + f.getHash(), args);
|
||||
logs.add(res);
|
||||
progress.ifPresent(theProgress -> theProgress.accept(STEP));
|
||||
if (sameExt && tmp != null) {
|
||||
if (res.isOk()) {
|
||||
Files.delete(tmp);
|
||||
} else {
|
||||
//conversion fails, need to move temp file back
|
||||
Files.move(tmp, mp4.toPath(), REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
progress.ifPresent(theProgress -> theProgress.accept(STEP));
|
||||
//Parse the width height from the FFMPEG output
|
||||
Dimension dim = getDimension(res.getError(), new Dimension(100, 100)); // will return 100x100 for non-video to be able to play
|
||||
progress.ifPresent(theProgress -> theProgress.accept(STEP));
|
||||
f.setWidth(dim.width());
|
||||
f.setHeight(dim.height());
|
||||
convertToPng(f, mp4.getCanonicalPath(), logs);
|
||||
progress.ifPresent(theProgress -> theProgress.accept(STEP));
|
||||
} catch (Exception err) {
|
||||
log.error("[convertVideo]", err);
|
||||
logs.add(new ProcessResult("convertToMP4", err.getMessage(), err));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.data.file;
|
||||
|
||||
import static java.util.UUID.randomUUID;
|
||||
import static org.apache.commons.io.FileUtils.copyFile;
|
||||
import static org.apache.commons.io.FileUtils.copyInputStreamToFile;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.getFileExt;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Optional;
|
||||
import java.util.function.DoubleConsumer;
|
||||
|
||||
import org.apache.openmeetings.core.converter.DocumentConverter;
|
||||
import org.apache.openmeetings.core.converter.ImageConverter;
|
||||
import org.apache.openmeetings.core.converter.VideoConverter;
|
||||
import org.apache.openmeetings.db.dao.file.FileItemDao;
|
||||
import org.apache.openmeetings.db.entity.file.BaseFileItem.Type;
|
||||
import org.apache.openmeetings.db.entity.file.FileItem;
|
||||
import org.apache.openmeetings.util.StoredFile;
|
||||
import org.apache.openmeetings.util.process.ProcessResult;
|
||||
import org.apache.openmeetings.util.process.ProcessResultList;
|
||||
import org.apache.tika.exception.UnsupportedFormatException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class FileProcessor {
|
||||
private static final Logger log = LoggerFactory.getLogger(FileProcessor.class);
|
||||
|
||||
//Spring loaded Beans
|
||||
@Autowired
|
||||
private VideoConverter videoConverter;
|
||||
@Autowired
|
||||
private FileItemDao fileDao;
|
||||
@Autowired
|
||||
private ImageConverter imageConverter;
|
||||
@Autowired
|
||||
private DocumentConverter docConverter;
|
||||
|
||||
public ProcessResultList processFile(FileItem f, InputStream is, Optional<DoubleConsumer> progress) throws Exception {
|
||||
ProcessResultList logs = new ProcessResultList();
|
||||
// Generate a random string to prevent any problems with
|
||||
// foreign characters and duplicates
|
||||
String hash = randomUUID().toString();
|
||||
|
||||
File temp = null;
|
||||
try {
|
||||
temp = File.createTempFile(String.format("upload_%s", hash), ".tmp");
|
||||
copyInputStreamToFile(is, temp);
|
||||
|
||||
String ext = getFileExt(f.getName());
|
||||
log.debug("file extension: {}", ext);
|
||||
//this method moves stream, so temp file MUST be created first
|
||||
StoredFile sf = new StoredFile(hash, ext, temp);
|
||||
|
||||
log.debug("isAsIs: {}", sf.isAsIs());
|
||||
|
||||
if (sf.isImage()) {
|
||||
f.setType(Type.IMAGE);
|
||||
} else if (sf.isVideo()) {
|
||||
f.setType(Type.VIDEO);
|
||||
} else if (sf.isChart()) {
|
||||
f.setType(Type.POLL_CHART);
|
||||
} else if (sf.isPdf() || sf.isOffice()) {
|
||||
f.setType(Type.PRESENTATION);
|
||||
} else {
|
||||
throw new UnsupportedFormatException("The file type cannot be converted :: " + f.getName());
|
||||
}
|
||||
f.setHash(hash);
|
||||
|
||||
processFile(f, sf, temp, logs, progress);
|
||||
} catch (Exception e) {
|
||||
log.debug("Error while processing the file", e);
|
||||
throw e;
|
||||
} finally {
|
||||
if (temp != null && temp.exists() && temp.isFile()) {
|
||||
log.debug("Clean up was successful ? {}", Files.deleteIfExists(temp.toPath()));
|
||||
}
|
||||
}
|
||||
return logs;
|
||||
}
|
||||
|
||||
private void processFile(FileItem f, StoredFile sf, File temp, ProcessResultList logs, Optional<DoubleConsumer> progress) throws Exception {
|
||||
try {
|
||||
File file = f.getFile(sf.getExt());
|
||||
log.debug("writing file to: {}", file);
|
||||
if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {
|
||||
logs.add(new ProcessResult("Unable to create parent for file: " + file.getCanonicalPath()));
|
||||
return;
|
||||
}
|
||||
switch(f.getType()) {
|
||||
case PRESENTATION:
|
||||
log.debug("Office document: {}", file);
|
||||
copyFile(temp, file);
|
||||
// convert to pdf, thumbs, swf and xml-description
|
||||
docConverter.convertPDF(f, sf, logs, progress);
|
||||
break;
|
||||
case POLL_CHART:
|
||||
log.debug("uploaded chart file"); // NOT implemented yet
|
||||
break;
|
||||
case IMAGE:
|
||||
// convert it to PNG
|
||||
log.debug("##### convert it to PNG: ");
|
||||
copyFile(temp, file);
|
||||
imageConverter.convertImage(f, sf, progress);
|
||||
break;
|
||||
case VIDEO:
|
||||
copyFile(temp, file);
|
||||
videoConverter.convertVideo(f, sf, logs, progress);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
f = fileDao.update(f);
|
||||
log.debug("fileId: {}", f.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.documents;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.thoughtworks.xstream.XStream;
|
||||
import com.thoughtworks.xstream.io.xml.XppDriver;
|
||||
|
||||
public class LibraryChartLoader {
|
||||
private static final Logger log = LoggerFactory.getLogger(LibraryChartLoader.class);
|
||||
private static final String CHART_EXT = ".xchart";
|
||||
|
||||
private LibraryChartLoader() {}
|
||||
|
||||
public static List<?> loadChart(File dir, String fileName) {
|
||||
try {
|
||||
File file = new File(dir, fileName + CHART_EXT);
|
||||
|
||||
log.error("filepathComplete: {}", file);
|
||||
|
||||
XStream xStream = new XStream(new XppDriver());
|
||||
xStream.setMode(XStream.NO_REFERENCES);
|
||||
|
||||
try (InputStream is = new FileInputStream(file);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is, UTF_8)))
|
||||
{
|
||||
return (List<?>) xStream.fromXML(reader);
|
||||
}
|
||||
} catch (Exception err) {
|
||||
log.error("Unexpected error while loading chart", err);
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,510 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.ldap;
|
||||
|
||||
import static org.apache.openmeetings.db.dao.user.UserDao.getNewUserInstance;
|
||||
import static org.apache.openmeetings.db.util.LocaleHelper.validateCountry;
|
||||
import static org.apache.openmeetings.db.util.TimezoneUtil.getTimeZone;
|
||||
import static org.apache.openmeetings.util.OmException.BAD_CREDENTIALS;
|
||||
import static org.apache.openmeetings.util.OmException.UNKNOWN;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.loadLdapConf;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getDefaultGroup;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.directory.api.ldap.model.cursor.CursorException;
|
||||
import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException;
|
||||
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
|
||||
import org.apache.directory.api.ldap.model.entry.Attribute;
|
||||
import org.apache.directory.api.ldap.model.entry.Entry;
|
||||
import org.apache.directory.api.ldap.model.entry.Value;
|
||||
import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException;
|
||||
import org.apache.directory.api.ldap.model.exception.LdapException;
|
||||
import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
|
||||
import org.apache.directory.api.ldap.model.message.AliasDerefMode;
|
||||
import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
|
||||
import org.apache.directory.api.ldap.model.message.SearchScope;
|
||||
import org.apache.directory.api.ldap.model.name.Dn;
|
||||
import org.apache.directory.api.ldap.model.name.Rdn;
|
||||
import org.apache.directory.ldap.client.api.EntryCursorImpl;
|
||||
import org.apache.directory.ldap.client.api.LdapConnection;
|
||||
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
|
||||
import org.apache.openmeetings.core.converter.ImageConverter;
|
||||
import org.apache.openmeetings.db.dao.server.LdapConfigDao;
|
||||
import org.apache.openmeetings.db.dao.user.GroupDao;
|
||||
import org.apache.openmeetings.db.dao.user.UserDao;
|
||||
import org.apache.openmeetings.db.entity.server.LdapConfig;
|
||||
import org.apache.openmeetings.db.entity.user.Address;
|
||||
import org.apache.openmeetings.db.entity.user.Group;
|
||||
import org.apache.openmeetings.db.entity.user.GroupUser;
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
import org.apache.openmeetings.db.entity.user.User.Right;
|
||||
import org.apache.openmeetings.db.entity.user.User.Type;
|
||||
import org.apache.openmeetings.util.OmException;
|
||||
import org.apache.openmeetings.util.StoredFile;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Management of optional LDAP Login
|
||||
*
|
||||
* @author o.becherer
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class LdapLoginManager {
|
||||
private static final Logger log = LoggerFactory.getLogger(LdapLoginManager.class);
|
||||
private static final String WARN_REFERRAL = "Referral LDAP entry found, ignore it";
|
||||
// LDAP custom attribute mapping keys
|
||||
private static final String CONFIGKEY_LDAP_KEY_LOGIN = "ldap_user_attr_login";
|
||||
private static final String CONFIGKEY_LDAP_KEY_LASTNAME = "ldap_user_attr_lastname";
|
||||
private static final String CONFIGKEY_LDAP_KEY_FIRSTNAME = "ldap_user_attr_firstname";
|
||||
private static final String CONFIGKEY_LDAP_KEY_MAIL = "ldap_user_attr_mail";
|
||||
private static final String CONFIGKEY_LDAP_KEY_STREET = "ldap_user_attr_street";
|
||||
private static final String CONFIGKEY_LDAP_KEY_ADDITIONAL_NAME = "ldap_user_attr_additionalname";
|
||||
private static final String CONFIGKEY_LDAP_KEY_FAX = "ldap_user_attr_fax";
|
||||
private static final String CONFIGKEY_LDAP_KEY_ZIP = "ldap_user_attr_zip";
|
||||
private static final String CONFIGKEY_LDAP_KEY_COUNTRY = "ldap_user_attr_country";
|
||||
private static final String CONFIGKEY_LDAP_KEY_TOWN = "ldap_user_attr_town";
|
||||
private static final String CONFIGKEY_LDAP_KEY_PHONE = "ldap_user_attr_phone";
|
||||
private static final String CONFIGKEY_LDAP_KEY_GROUP = "ldap_group_attr";
|
||||
public static final String CONFIGKEY_LDAP_KEY_PICTURE = "ldap_user_attr_picture";
|
||||
|
||||
// LDAP default attributes mapping
|
||||
private static final String LDAP_KEY_LOGIN = "uid";
|
||||
private static final String LDAP_KEY_LASTNAME = "sn";
|
||||
private static final String LDAP_KEY_FIRSTNAME = "givenName";
|
||||
private static final String LDAP_KEY_MAIL = "mail";
|
||||
private static final String LDAP_KEY_STREET = "streetAddress";
|
||||
private static final String LDAP_KEY_ADDITIONAL_NAME = "description";
|
||||
private static final String LDAP_KEY_FAX = "facsimileTelephoneNumber";
|
||||
private static final String LDAP_KEY_ZIP = "postalCode";
|
||||
private static final String LDAP_KEY_COUNTRY = "co";
|
||||
private static final String LDAP_KEY_TOWN = "l";
|
||||
private static final String LDAP_KEY_PHONE = "telephoneNumber";
|
||||
private static final String LDAP_KEY_TIMEZONE = "timezone";
|
||||
private static final String LDAP_KEY_GROUP = "memberOf";
|
||||
|
||||
public enum AuthType {
|
||||
NONE
|
||||
, SEARCHANDBIND
|
||||
, SIMPLEBIND
|
||||
}
|
||||
|
||||
public enum Provisionning {
|
||||
NONE
|
||||
, AUTOUPDATE
|
||||
, AUTOCREATE
|
||||
}
|
||||
|
||||
public enum GroupMode {
|
||||
NONE
|
||||
, ATTRIBUTE
|
||||
, QUERY
|
||||
}
|
||||
@Autowired
|
||||
private LdapConfigDao ldapConfigDao;
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
@Autowired
|
||||
private GroupDao groupDao;
|
||||
@Autowired
|
||||
private ImageConverter imageConverter;
|
||||
|
||||
private static void bindAdmin(LdapConnection conn, LdapOptions options) throws LdapException {
|
||||
if (!Strings.isEmpty(options.adminDn)) {
|
||||
conn.bind(options.adminDn, options.adminPasswd);
|
||||
} else {
|
||||
conn.bind();
|
||||
}
|
||||
}
|
||||
|
||||
private static Attribute getAttr(Properties config, Entry entry, String aliasCode, String defaultAlias) {
|
||||
String alias = config.getProperty(aliasCode, "");
|
||||
if (Strings.isEmpty(alias)) {
|
||||
alias = defaultAlias;
|
||||
}
|
||||
return Strings.isEmpty(alias) ? null : entry.get(alias);
|
||||
}
|
||||
|
||||
private static String getStringAttr(Properties config, Entry entry, String aliasCode, String defaultAlias) throws LdapInvalidAttributeValueException {
|
||||
Attribute a = getAttr(config, entry, aliasCode, defaultAlias);
|
||||
return a == null ? null : a.getString();
|
||||
}
|
||||
|
||||
private static String getLogin(Properties config, Entry entry) throws LdapInvalidAttributeValueException {
|
||||
return getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_LOGIN, LDAP_KEY_LOGIN);
|
||||
}
|
||||
|
||||
private User doLogin(LdapWorker w, String login, String passwd, Long domainId) throws Exception {
|
||||
User u = null;
|
||||
boolean authenticated = true;
|
||||
Dn userDn = null;
|
||||
Entry entry = null;
|
||||
switch (w.options.type) {
|
||||
case SEARCHANDBIND:
|
||||
Map.Entry<Dn, Entry> search = searchAndBind(w, login, passwd);
|
||||
userDn = search.getKey();
|
||||
entry = search.getValue();
|
||||
break;
|
||||
case SIMPLEBIND:
|
||||
userDn = new Dn(String.format(w.options.userDn, login));
|
||||
w.conn.bind(userDn, passwd);
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
authenticated = false;
|
||||
break;
|
||||
}
|
||||
u = authenticated ? userDao.getByLogin(login, Type.LDAP, domainId) : userDao.login(login, passwd);
|
||||
log.debug("getByLogin:: authenticated ? {}, login = '{}', domain = {}, user = {}", authenticated, login, domainId, u);
|
||||
if (u == null && Provisionning.AUTOCREATE != w.options.prov) {
|
||||
log.error("User not found in OM DB and Provisionning.AUTOCREATE was not set");
|
||||
throw BAD_CREDENTIALS;
|
||||
}
|
||||
if (authenticated && entry == null) {
|
||||
if (w.options.useAdminForAttrs) {
|
||||
bindAdmin(w.conn, w.options);
|
||||
}
|
||||
entry = w.conn.lookup(userDn);
|
||||
}
|
||||
switch (w.options.prov) {
|
||||
case AUTOUPDATE, AUTOCREATE:
|
||||
u = w.getUser(entry, u);
|
||||
if (w.options.syncPasswd) {
|
||||
u.updatePassword(passwd);
|
||||
}
|
||||
u = userDao.update(u, null);
|
||||
u = w.setUserPicture(entry, u);
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
/**
|
||||
* Ldap Login
|
||||
*
|
||||
* Connection Data is retrieved from ConfigurationFile
|
||||
*
|
||||
* @param inLogin - user login
|
||||
* @param passwd - user password
|
||||
* @param domainId - user domain id
|
||||
* @return - {@link User} with this credentials or <code>null</code>
|
||||
* @throws OmException - in case of any error
|
||||
*/
|
||||
public User login(String inLogin, String passwd, Long domainId) throws OmException {
|
||||
log.debug("LdapLoginmanager.doLdapLogin");
|
||||
if (!userDao.validLogin(inLogin)) {
|
||||
log.error("Invalid login provided");
|
||||
return null;
|
||||
}
|
||||
|
||||
User u = null;
|
||||
try (LdapWorker w = new LdapWorker(domainId)) {
|
||||
String login = w.options.useLowerCase ? inLogin.toLowerCase(Locale.ROOT) : inLogin;
|
||||
|
||||
u = doLogin(w, login, passwd, domainId);
|
||||
} catch (LdapAuthenticationException ae) {
|
||||
log.error("Not authenticated.", ae);
|
||||
throw BAD_CREDENTIALS;
|
||||
} catch (OmException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected exception.", e);
|
||||
throw new OmException(e);
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
private static Map.Entry<Dn, Entry> searchAndBind(LdapWorker w, String login, String passwd) throws LdapException, CursorException, OmException, IOException {
|
||||
Dn userDn = null;
|
||||
Entry entry = null;
|
||||
bindAdmin(w.conn, w.options);
|
||||
Dn baseDn = new Dn(w.options.searchBase);
|
||||
String searchQ = String.format(w.options.searchQuery, login);
|
||||
|
||||
try (EntryCursor cursor = new EntryCursorImpl(w.conn.search(
|
||||
new SearchRequestImpl()
|
||||
.setBase(baseDn)
|
||||
.setFilter(searchQ)
|
||||
.setScope(w.options.scope)
|
||||
.addAttributes("*")
|
||||
.setDerefAliases(w.options.derefMode))))
|
||||
{
|
||||
while (cursor.next()) {
|
||||
try {
|
||||
Entry e = cursor.get();
|
||||
if (userDn != null) {
|
||||
log.error("more than 1 user found in LDAP");
|
||||
throw UNKNOWN;
|
||||
}
|
||||
userDn = e.getDn();
|
||||
if (w.options.useAdminForAttrs) {
|
||||
entry = e;
|
||||
}
|
||||
} catch (CursorLdapReferralException cle) {
|
||||
log.warn(WARN_REFERRAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (userDn == null) {
|
||||
log.error("NONE users found in LDAP");
|
||||
throw BAD_CREDENTIALS;
|
||||
}
|
||||
w.conn.bind(userDn, passwd);
|
||||
return new AbstractMap.SimpleEntry<>(userDn, entry);
|
||||
}
|
||||
|
||||
public void importUsers(Long domainId, boolean print) throws OmException {
|
||||
try (LdapWorker w = new LdapWorker(domainId)) {
|
||||
bindAdmin(w.conn, w.options);
|
||||
Dn baseDn = new Dn(w.options.searchBase);
|
||||
|
||||
try (EntryCursor cursor = new EntryCursorImpl(w.conn.search(
|
||||
new SearchRequestImpl()
|
||||
.setBase(baseDn)
|
||||
.setFilter(w.options.importQuery)
|
||||
.setScope(w.options.scope)
|
||||
.addAttributes("*")
|
||||
.setDerefAliases(w.options.derefMode))))
|
||||
{
|
||||
importUsers(w, cursor, domainId, print);
|
||||
}
|
||||
} catch (LdapAuthenticationException ae) {
|
||||
log.error("Not authenticated.", ae);
|
||||
throw BAD_CREDENTIALS;
|
||||
} catch (OmException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected exception.", e);
|
||||
throw new OmException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void importUsers(LdapWorker w, EntryCursor cursor, Long domainId, boolean print) throws LdapException, CursorException, OmException, IOException {
|
||||
while (cursor.next()) {
|
||||
try {
|
||||
Entry e = cursor.get();
|
||||
User u = userDao.getByLogin(getLogin(w.config, e), Type.LDAP, domainId);
|
||||
u = w.getUser(e, u);
|
||||
if (print) {
|
||||
log.info("Going to import user: {}", u);
|
||||
} else {
|
||||
userDao.update(u, null);
|
||||
log.info("User {}, was imported", u);
|
||||
}
|
||||
} catch (CursorLdapReferralException cle) {
|
||||
log.warn(WARN_REFERRAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class LdapWorker implements Closeable {
|
||||
final LdapConnection conn;
|
||||
final Properties config = new Properties();
|
||||
final LdapOptions options;
|
||||
final Long domainId;
|
||||
final LdapConfig ldapCfg;
|
||||
|
||||
public LdapWorker(Long domainId) {
|
||||
this.domainId = domainId;
|
||||
ldapCfg = ldapConfigDao.get(domainId);
|
||||
loadLdapConf(ldapCfg.getConfigFileName(), config);
|
||||
options = new LdapOptions(config);
|
||||
|
||||
conn = new LdapNetworkConnection(options.host, options.port, options.secure);
|
||||
}
|
||||
|
||||
private User updatePic(User inUser, InputStream is, StoredFile sf) {
|
||||
User u = inUser;
|
||||
Path tempImage = null;
|
||||
try {
|
||||
tempImage = Files.createTempFile("omLdap", "img");
|
||||
FileUtils.copyToFile(is, tempImage.toFile());
|
||||
imageConverter.convertImageUserProfile(tempImage.toFile(), inUser.getId());
|
||||
u = userDao.get(inUser.getId());
|
||||
} catch (Exception e) {
|
||||
log.error("Unable to store binary image from LDAP", e);
|
||||
} finally {
|
||||
if (tempImage != null) {
|
||||
try {
|
||||
Files.deleteIfExists(tempImage);
|
||||
} catch (IOException e) {
|
||||
log.error("Unexpected error while clean-up", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
public User setUserPicture(Entry entry, User inUser) {
|
||||
User user = Optional.ofNullable(getAttr(config, entry, CONFIGKEY_LDAP_KEY_PICTURE, ""))
|
||||
.map(Attribute::get)
|
||||
.filter(val -> val != null && val.getBytes() != null)
|
||||
.map(val -> {
|
||||
User u = inUser;
|
||||
InputStream is = new ByteArrayInputStream(val.getBytes());
|
||||
StoredFile sf = new StoredFile("picture", is);
|
||||
if (sf.isImage()) {
|
||||
u = updatePic(inUser, is, sf);
|
||||
} else {
|
||||
u.setPictureUri(val.getString());
|
||||
}
|
||||
return u;
|
||||
}).orElse(inUser);
|
||||
if (Strings.isEmpty(user.getPictureUri()) && !Strings.isEmpty(options.pictureUri)) {
|
||||
user.setPictureUri(options.pictureUri);
|
||||
}
|
||||
return userDao.update(user, null);
|
||||
}
|
||||
|
||||
private User create(Entry entry) throws LdapInvalidAttributeValueException {
|
||||
User u = getNewUserInstance(null);
|
||||
u.setType(Type.LDAP);
|
||||
u.getRights().remove(Right.LOGIN);
|
||||
u.setDomainId(domainId);
|
||||
Group g = groupDao.get(getDefaultGroup());
|
||||
if (g != null) {
|
||||
u.addGroup(g);
|
||||
}
|
||||
String login = getLogin(config, entry);
|
||||
if (ldapCfg.getAddDomainToUserName()) {
|
||||
login = login + "@" + ldapCfg.getDomain();
|
||||
}
|
||||
if (options.useLowerCase) {
|
||||
login = login.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
u.setLogin(login);
|
||||
u.setShowContactDataToContacts(true);
|
||||
u.setAddress(new Address());
|
||||
return u;
|
||||
}
|
||||
|
||||
private List<Dn> getGroupDns(Entry entry, User u) throws IOException, LdapException, CursorException {
|
||||
List<Dn> groups = new ArrayList<>();
|
||||
if (GroupMode.ATTRIBUTE == options.groupMode) {
|
||||
Attribute attr = getAttr(config, entry, CONFIGKEY_LDAP_KEY_GROUP, LDAP_KEY_GROUP);
|
||||
if (attr != null) {
|
||||
for (Value v : attr) {
|
||||
groups.add(new Dn(v.getString()));
|
||||
}
|
||||
}
|
||||
} else if (GroupMode.QUERY == options.groupMode) {
|
||||
Dn baseDn = new Dn(options.searchBase);
|
||||
String searchQ = String.format(options.groupQuery, u.getLogin());
|
||||
fillGroups(baseDn, searchQ, groups);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
public User getUser(Entry entry, User user) throws LdapException, CursorException, OmException, IOException {
|
||||
if (entry == null) {
|
||||
log.error("LDAP entry is null, search or lookup by Dn failed");
|
||||
throw BAD_CREDENTIALS;
|
||||
}
|
||||
User u = user == null ? create(entry) : user;
|
||||
u.setLastname(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_LASTNAME, LDAP_KEY_LASTNAME));
|
||||
u.setFirstname(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_FIRSTNAME, LDAP_KEY_FIRSTNAME));
|
||||
u.getAddress().setEmail(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_MAIL, LDAP_KEY_MAIL));
|
||||
u.getAddress().setStreet(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_STREET, LDAP_KEY_STREET));
|
||||
u.getAddress().setAdditionalname(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_ADDITIONAL_NAME, LDAP_KEY_ADDITIONAL_NAME));
|
||||
u.getAddress().setFax(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_FAX, LDAP_KEY_FAX));
|
||||
u.getAddress().setZip(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_ZIP, LDAP_KEY_ZIP));
|
||||
u.getAddress().setCountry(validateCountry(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_COUNTRY, LDAP_KEY_COUNTRY)));
|
||||
u.getAddress().setTown(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_TOWN, LDAP_KEY_TOWN));
|
||||
u.getAddress().setPhone(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_PHONE, LDAP_KEY_PHONE));
|
||||
String tz = getStringAttr(config, entry, LdapOptions.CONFIGKEY_LDAP_TIMEZONE_NAME, LDAP_KEY_TIMEZONE);
|
||||
if (tz == null) {
|
||||
tz = options.tz;
|
||||
}
|
||||
u.setTimeZoneId(getTimeZone(tz).getID());
|
||||
|
||||
getGroupDns(entry, u).stream()
|
||||
.map(Dn::getRdn)
|
||||
.map(Rdn::getValue)
|
||||
.filter(name -> !Strings.isEmpty(name))
|
||||
.forEach(name -> {
|
||||
Group o = groupDao.get(name);
|
||||
boolean found = false;
|
||||
if (o == null) {
|
||||
o = new Group();
|
||||
o.setName(name);
|
||||
o = groupDao.update(o, u.getId());
|
||||
} else {
|
||||
for (GroupUser ou : u.getGroupUsers()) {
|
||||
if (ou.getGroup().getName().equals(name)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
u.addGroup(o);
|
||||
log.debug("Going to add user to group:: {}", name);
|
||||
}
|
||||
});
|
||||
return u;
|
||||
}
|
||||
|
||||
private void fillGroups(Dn baseDn, String searchQ, List<Dn> groups) throws IOException, LdapException, CursorException {
|
||||
try (EntryCursor cursor = new EntryCursorImpl(conn.search(
|
||||
new SearchRequestImpl()
|
||||
.setBase(baseDn)
|
||||
.setFilter(searchQ)
|
||||
.setScope(SearchScope.SUBTREE)
|
||||
.addAttributes("*")
|
||||
.setDerefAliases(AliasDerefMode.DEREF_ALWAYS))))
|
||||
{
|
||||
while (cursor.next()) {
|
||||
try {
|
||||
Entry e = cursor.get();
|
||||
groups.add(e.getDn());
|
||||
} catch (CursorLdapReferralException cle) {
|
||||
log.warn(WARN_REFERRAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (conn != null) {
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.ldap;
|
||||
|
||||
import static org.apache.commons.lang3.math.NumberUtils.toInt;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.directory.api.ldap.model.message.AliasDerefMode;
|
||||
import org.apache.directory.api.ldap.model.message.SearchScope;
|
||||
import org.apache.openmeetings.core.ldap.LdapLoginManager.AuthType;
|
||||
import org.apache.openmeetings.core.ldap.LdapLoginManager.GroupMode;
|
||||
import org.apache.openmeetings.core.ldap.LdapLoginManager.Provisionning;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LdapOptions {
|
||||
private static final Logger log = LoggerFactory.getLogger(LdapOptions.class);
|
||||
private static final String EMPTY_FORMAT = "%s";
|
||||
public static final String CONFIGKEY_LDAP_HOST = "ldap_conn_host";
|
||||
public static final String CONFIGKEY_LDAP_PORT = "ldap_conn_port";
|
||||
private static final String CONFIGKEY_LDAP_SECURE = "ldap_conn_secure";
|
||||
public static final String CONFIGKEY_LDAP_ADMIN_DN = "ldap_admin_dn";
|
||||
public static final String CONFIGKEY_LDAP_ADMIN_PASSWD = "ldap_passwd";
|
||||
public static final String CONFIGKEY_LDAP_AUTH_TYPE = "ldap_auth_type";
|
||||
private static final String CONFIGKEY_LDAP_PROV_TYPE = "ldap_provisionning";
|
||||
private static final String CONFIGKEY_LDAP_USE_LOWER_CASE = "ldap_use_lower_case";
|
||||
private static final String CONFIGKEY_LDAP_USE_ADMIN_4ATTRS = "ldap_use_admin_to_get_attrs";
|
||||
private static final String CONFIGKEY_LDAP_DEREF_MODE = "ldap_deref_mode";
|
||||
private static final String CONFIGKEY_LDAP_GROUP_MODE = "ldap_group_mode";
|
||||
public static final String CONFIGKEY_LDAP_SEARCH_BASE = "ldap_search_base";
|
||||
private static final String CONFIGKEY_LDAP_SEARCH_QUERY = "ldap_search_query";
|
||||
public static final String CONFIGKEY_LDAP_SEARCH_SCOPE = "ldap_search_scope";
|
||||
private static final String CONFIGKEY_LDAP_SYNC_PASSWD_OM = "ldap_sync_password_to_om"; // 'true' or 'false'
|
||||
static final String CONFIGKEY_LDAP_TIMEZONE_NAME = "ldap_user_timezone";
|
||||
private static final String CONFIGKEY_LDAP_USERDN_FORMAT = "ldap_userdn_format";
|
||||
private static final String CONFIGKEY_LDAP_GROUP_QUERY = "ldap_group_query";
|
||||
private static final String CONFIGKEY_LDAP_IMPORT_QUERY = "ldap_import_query";
|
||||
public static final String CONFIGKEY_LDAP_PICTURE_URI = "ldap_user_picture_uri";
|
||||
|
||||
AuthType type = AuthType.SIMPLEBIND;
|
||||
Provisionning prov = Provisionning.AUTOCREATE;
|
||||
AliasDerefMode derefMode = AliasDerefMode.DEREF_ALWAYS;
|
||||
GroupMode groupMode = GroupMode.NONE;
|
||||
boolean useLowerCase = false;
|
||||
boolean useAdminForAttrs = true;
|
||||
String host = null;
|
||||
int port = 389;
|
||||
boolean secure = false;
|
||||
String adminDn = null;
|
||||
String adminPasswd = null;
|
||||
String searchBase = "";
|
||||
String searchQuery = EMPTY_FORMAT;
|
||||
SearchScope scope = SearchScope.ONELEVEL;
|
||||
boolean syncPasswd = false;
|
||||
String tz = null;
|
||||
String groupQuery = EMPTY_FORMAT;
|
||||
String userDn = EMPTY_FORMAT;
|
||||
String pictureUri = null;
|
||||
String importQuery = null;
|
||||
|
||||
public LdapOptions(Properties config) {
|
||||
String useLowerCaseProp = config.getProperty(CONFIGKEY_LDAP_USE_LOWER_CASE, "false");
|
||||
useLowerCase = "true".equals(useLowerCaseProp);
|
||||
|
||||
String authType = config.getProperty(CONFIGKEY_LDAP_AUTH_TYPE, "");
|
||||
try {
|
||||
type = AuthType.valueOf(authType);
|
||||
} catch (Exception e) {
|
||||
log.error("ConfigKey in Ldap Config contains invalid auth type : '{}' -> Defaulting to {}", authType, type);
|
||||
}
|
||||
|
||||
String provType = config.getProperty(CONFIGKEY_LDAP_PROV_TYPE, "");
|
||||
try {
|
||||
prov = Provisionning.valueOf(provType);
|
||||
} catch (Exception e) {
|
||||
log.error("ConfigKey in Ldap Config contains invalid provisionning type : '{}' -> Defaulting to {}", provType, prov);
|
||||
}
|
||||
|
||||
String derefModeProp = config.getProperty(CONFIGKEY_LDAP_DEREF_MODE, "");
|
||||
try {
|
||||
derefMode = AliasDerefMode.getDerefMode(derefModeProp);
|
||||
} catch (Exception e) {
|
||||
log.error("ConfigKey in Ldap Config contains invalid deref mode : '{}' -> Defaulting to {}", derefModeProp, derefMode);
|
||||
}
|
||||
|
||||
if (AuthType.NONE == type && Provisionning.NONE == prov) {
|
||||
throw new RuntimeException("Both AuthType and Provisionning are NONE!");
|
||||
}
|
||||
try {
|
||||
useAdminForAttrs = "true".equals(config.getProperty(CONFIGKEY_LDAP_USE_ADMIN_4ATTRS, ""));
|
||||
} catch (Exception e) {
|
||||
//no-op
|
||||
}
|
||||
try {
|
||||
groupMode = GroupMode.valueOf(config.getProperty(CONFIGKEY_LDAP_GROUP_MODE, "NONE"));
|
||||
} catch (Exception e) {
|
||||
//no-op
|
||||
}
|
||||
if (AuthType.NONE == type && !useAdminForAttrs) {
|
||||
throw new RuntimeException("Unable to get Attributes, please change Auth type and/or Use Admin to get attributes");
|
||||
}
|
||||
|
||||
// Connection URL
|
||||
host = config.getProperty(CONFIGKEY_LDAP_HOST);
|
||||
port = toInt(config.getProperty(CONFIGKEY_LDAP_PORT), 389);
|
||||
secure = "true".equals(config.getProperty(CONFIGKEY_LDAP_SECURE, "false"));
|
||||
|
||||
// Username for LDAP SERVER himself
|
||||
adminDn = config.getProperty(CONFIGKEY_LDAP_ADMIN_DN);
|
||||
|
||||
// Password for LDAP SERVER himself
|
||||
adminPasswd = config.getProperty(CONFIGKEY_LDAP_ADMIN_PASSWD);
|
||||
|
||||
searchBase = config.getProperty(CONFIGKEY_LDAP_SEARCH_BASE, "");
|
||||
searchQuery = config.getProperty(CONFIGKEY_LDAP_SEARCH_QUERY, EMPTY_FORMAT);
|
||||
scope = SearchScope.valueOf(config.getProperty(CONFIGKEY_LDAP_SEARCH_SCOPE, SearchScope.ONELEVEL.name()));
|
||||
syncPasswd = "true".equals(config.getProperty(CONFIGKEY_LDAP_SYNC_PASSWD_OM, ""));
|
||||
tz = config.getProperty(CONFIGKEY_LDAP_TIMEZONE_NAME, null);
|
||||
groupQuery = config.getProperty(CONFIGKEY_LDAP_GROUP_QUERY, EMPTY_FORMAT);
|
||||
userDn = config.getProperty(CONFIGKEY_LDAP_USERDN_FORMAT, EMPTY_FORMAT);
|
||||
pictureUri = config.getProperty(CONFIGKEY_LDAP_PICTURE_URI, null);
|
||||
importQuery = config.getProperty(CONFIGKEY_LDAP_IMPORT_QUERY, "(objectClass=*)");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.mail;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getMailFrom;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getSmtpConnectionTimeOut;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getSmtpPass;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getSmtpPort;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getSmtpServer;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getSmtpTimeOut;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getSmtpUser;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.isInitComplete;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.isMailAddReplyTo;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.isSmtpUseSsl;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.isSmtpUseTls;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.openmeetings.db.dao.basic.MailMessageDao;
|
||||
import org.apache.openmeetings.db.entity.basic.MailMessage;
|
||||
import org.apache.openmeetings.db.entity.basic.MailMessage.Status;
|
||||
import org.apache.openmeetings.util.mail.ByteArrayDataSource;
|
||||
import org.apache.openmeetings.util.mail.MailUtil;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.activation.DataHandler;
|
||||
import jakarta.mail.Authenticator;
|
||||
import jakarta.mail.BodyPart;
|
||||
import jakarta.mail.Message;
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.Multipart;
|
||||
import jakarta.mail.PasswordAuthentication;
|
||||
import jakarta.mail.Session;
|
||||
import jakarta.mail.Transport;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.mail.internet.MimeBodyPart;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
import jakarta.mail.internet.MimeMultipart;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author swagner
|
||||
*
|
||||
* For a documentation about Javax mail please see for example:
|
||||
* http://connector.sourceforge.net/doc-files/Properties.html
|
||||
*
|
||||
*/
|
||||
@Component("mailHandler")
|
||||
public class MailHandler {
|
||||
private static final Logger log = LoggerFactory.getLogger(MailHandler.class);
|
||||
private static final int MAIL_SEND_TIMEOUT = 60 * 60 * 1000; // 1 hour
|
||||
private static final int MAXIMUM_ERROR_COUNT = 5;
|
||||
|
||||
@Autowired
|
||||
private TaskExecutor taskExecutor;
|
||||
@Autowired
|
||||
private MailMessageDao mailMessageDao;
|
||||
|
||||
protected MimeMessage appendIcsBody(MimeMessage msg, MailMessage m) throws Exception {
|
||||
log.debug("setMessageBody for iCal message");
|
||||
// -- Create a new message --
|
||||
Multipart multipart = new MimeMultipart();
|
||||
|
||||
Multipart multiBody = new MimeMultipart("alternative");
|
||||
BodyPart html = new MimeBodyPart();
|
||||
html.setDataHandler(new DataHandler(new ByteArrayDataSource(m.getBody(), "text/html; charset=UTF-8")));
|
||||
multiBody.addBodyPart(html);
|
||||
|
||||
BodyPart iCalContent = new MimeBodyPart();
|
||||
iCalContent.addHeader("content-class", "urn:content-classes:calendarmessage");
|
||||
iCalContent.setDataHandler(new DataHandler(new ByteArrayDataSource(new ByteArrayInputStream(m.getIcs()),
|
||||
"text/calendar; charset=UTF-8; method=" + m.getIcsMethod())));
|
||||
multiBody.addBodyPart(iCalContent);
|
||||
BodyPart body = new MimeBodyPart();
|
||||
body.setContent(multiBody);
|
||||
multipart.addBodyPart(body);
|
||||
|
||||
BodyPart iCalAttachment = new MimeBodyPart();
|
||||
iCalAttachment.setDataHandler(new DataHandler(new ByteArrayDataSource(new ByteArrayInputStream(m.getIcs()),
|
||||
"application/ics")));
|
||||
iCalAttachment.removeHeader("Content-Transfer-Encoding");
|
||||
iCalAttachment.addHeader("Content-Transfer-Encoding", "base64");
|
||||
iCalAttachment.removeHeader("Content-Type");
|
||||
iCalAttachment.addHeader("Content-Type", "application/ics");
|
||||
iCalAttachment.setFileName("invite.ics");
|
||||
multipart.addBodyPart(iCalAttachment);
|
||||
|
||||
msg.setContent(multipart);
|
||||
return msg;
|
||||
}
|
||||
|
||||
private MimeMessage appendBody(MimeMessage msg, MailMessage m) throws MessagingException {
|
||||
return appendBody(msg, m.getBody());
|
||||
}
|
||||
|
||||
public MimeMessage appendBody(MimeMessage msg, String body) throws MessagingException {
|
||||
// -- Set the subject and body text --
|
||||
msg.setDataHandler(new DataHandler(new ByteArrayDataSource(body, "text/html; charset=\"utf-8\"")));
|
||||
|
||||
// -- Set some other header information --
|
||||
msg.setHeader("X-Mailer", "XML-Mail");
|
||||
msg.setSentDate(new Date());
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
// this method should be public for tests
|
||||
public MimeMessage getBasicMimeMessage() throws Exception {
|
||||
log.debug("getBasicMimeMessage");
|
||||
if (getSmtpServer() == null) {
|
||||
throw new IllegalStateException("SMTP settings were not provided");
|
||||
}
|
||||
Properties props = new Properties(System.getProperties());
|
||||
|
||||
props.put("mail.smtp.host", getSmtpServer());
|
||||
props.put("mail.smtp.port", getSmtpPort());
|
||||
if (isSmtpUseTls() || isSmtpUseSsl()) {
|
||||
props.put("mail.smtp.ssl.trust", getSmtpServer());
|
||||
}
|
||||
if (isSmtpUseTls() && isSmtpUseSsl()) {
|
||||
log.warn("Both SSL and TLS are enabled, TLS will be started");
|
||||
}
|
||||
props.put("mail.smtp.starttls.enable", isSmtpUseTls());
|
||||
props.put("mail.smtp.ssl.enable", isSmtpUseSsl());
|
||||
props.put("mail.smtp.connectiontimeout", getSmtpConnectionTimeOut());
|
||||
props.put("mail.smtp.timeout", getSmtpTimeOut());
|
||||
|
||||
// Check for Authentication
|
||||
Session session;
|
||||
if (!Strings.isEmpty(getSmtpUser()) && !Strings.isEmpty(getSmtpPass())) {
|
||||
// use SMTP Authentication
|
||||
props.put("mail.smtp.auth", true);
|
||||
session = Session.getDefaultInstance(props, new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(getSmtpUser(), getSmtpPass());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// not use SMTP Authentication
|
||||
session = Session.getInstance(props, null);
|
||||
}
|
||||
|
||||
// Building MimeMessage
|
||||
MimeMessage msg = new MimeMessage(session);
|
||||
msg.setFrom(new InternetAddress(getMailFrom()));
|
||||
return msg;
|
||||
}
|
||||
|
||||
private MimeMessage getMimeMessage(MailMessage m) throws Exception {
|
||||
log.debug("getMimeMessage");
|
||||
// Building MimeMessage
|
||||
MimeMessage msg = getBasicMimeMessage();
|
||||
msg.setSubject(m.getSubject(), UTF_8.name());
|
||||
String replyTo = m.getReplyTo();
|
||||
if (replyTo != null && isMailAddReplyTo()) {
|
||||
log.debug("setReplyTo {}", replyTo);
|
||||
if (MailUtil.isValid(replyTo)) {
|
||||
msg.setReplyTo(new InternetAddress[]{new InternetAddress(replyTo)});
|
||||
}
|
||||
}
|
||||
msg.addRecipients(Message.RecipientType.TO, InternetAddress.parse(m.getRecipients(), false));
|
||||
|
||||
return m.getIcs() == null ? appendBody(msg, m) : appendIcsBody(msg, m);
|
||||
}
|
||||
|
||||
public void send(String toEmail, String subj, String message) {
|
||||
send(toEmail, null, subj, message);
|
||||
}
|
||||
|
||||
public void send(String toEmail, String replyTo, String subj, String message) {
|
||||
send(new MailMessage(toEmail, replyTo, subj, message));
|
||||
}
|
||||
|
||||
public void send(MailMessage m) {
|
||||
send(m, false);
|
||||
}
|
||||
|
||||
public void send(final MailMessage m, boolean send) {
|
||||
if (send) {
|
||||
if (m.getId() != null) {
|
||||
m.setStatus(Status.SENDING);
|
||||
mailMessageDao.update(m, null);
|
||||
}
|
||||
taskExecutor.execute(() -> {
|
||||
log.debug("Message sending in progress");
|
||||
log.debug(" To: {}", m.getRecipients());
|
||||
log.debug(" Subject: {}", m.getSubject());
|
||||
|
||||
// -- Send the message --
|
||||
try {
|
||||
Transport.send(getMimeMessage(m));
|
||||
m.setLastError("");
|
||||
m.setStatus(Status.DONE);
|
||||
} catch (Exception e) {
|
||||
log.error("Error while sending message", e);
|
||||
m.setErrorCount(m.getErrorCount() + 1);
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
m.setLastError(sw.getBuffer().toString());
|
||||
m.setStatus(m.getErrorCount() < MAXIMUM_ERROR_COUNT ? Status.NONE : Status.ERROR);
|
||||
}
|
||||
if (m.getId() != null) {
|
||||
mailMessageDao.update(m, null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
m.setStatus(Status.NONE);
|
||||
mailMessageDao.update(m, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetSendingStatus() {
|
||||
log.trace("resetSendingStatus enter ...");
|
||||
if (!isInitComplete()) {
|
||||
return;
|
||||
}
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.add(Calendar.MILLISECOND, -MAIL_SEND_TIMEOUT);
|
||||
mailMessageDao.resetSendingStatus(c);
|
||||
log.trace("... resetSendingStatus done.");
|
||||
}
|
||||
|
||||
public void sendMails() {
|
||||
log.trace("sendMails enter ...");
|
||||
List<MailMessage> list = mailMessageDao.get(0, 1, MailMessage.Status.NONE);
|
||||
if (!list.isEmpty()) {
|
||||
log.debug("Number of emails in init queue {}", list.size());
|
||||
while (!list.isEmpty()) {
|
||||
send(list.get(0), true);
|
||||
list = mailMessageDao.get(0, 1, MailMessage.Status.NONE);
|
||||
}
|
||||
log.debug("... sendMails done.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.notifier;
|
||||
|
||||
import org.apache.openmeetings.db.entity.calendar.Appointment;
|
||||
import org.apache.openmeetings.db.entity.room.Invitation;
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface INotifier {
|
||||
void notify(User u, Appointment a, Invitation inv) throws Exception;
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.notifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.openmeetings.db.entity.calendar.Appointment;
|
||||
import org.apache.openmeetings.db.entity.room.Invitation;
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author iarkh
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class NotifierService {
|
||||
private static final Logger log = LoggerFactory.getLogger(NotifierService.class);
|
||||
|
||||
private List<INotifier> notifiers = new ArrayList<>();
|
||||
|
||||
public void addNotifier(INotifier n) {
|
||||
notifiers.add(n);
|
||||
}
|
||||
|
||||
public void notify(User u, Appointment a, Invitation inv) {
|
||||
if (inv == null) {
|
||||
log.error("Error retrieving Invitation for member {} in Appointment {}"
|
||||
, u.getAddress().getEmail(), a.getTitle());
|
||||
return;
|
||||
}
|
||||
for (INotifier n : notifiers) {
|
||||
try {
|
||||
n.notify(u, a, inv);
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected exception while sending notifications", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.rss;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.github.openjson.JSONArray;
|
||||
|
||||
public class LoadAtomRssFeed {
|
||||
private static final Logger log = LoggerFactory.getLogger(LoadAtomRssFeed.class);
|
||||
private static JSONArray rss = new JSONArray();
|
||||
|
||||
private LoadAtomRssFeed() {}
|
||||
|
||||
public static HttpURLConnection getFeedConnection(String urlStr) throws IOException {
|
||||
log.trace("getFeedConnection:: {}", urlStr);
|
||||
|
||||
URL url = new URL(urlStr);
|
||||
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)");
|
||||
conn.setRequestProperty("Referer", "https://openmeetings.apache.org/");
|
||||
conn.setUseCaches(false);
|
||||
conn.connect();
|
||||
return conn;
|
||||
}
|
||||
|
||||
public static JSONArray getRss() {
|
||||
return rss;
|
||||
}
|
||||
|
||||
public static void setRss(JSONArray rss) {
|
||||
LoadAtomRssFeed.rss = rss;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.sip;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface ISipCallbacks {
|
||||
void onRegisterOk();
|
||||
void onInviteOk(String sdp, Consumer<String> answerConsumer);
|
||||
}
|
||||
@ -0,0 +1,310 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.sip;
|
||||
|
||||
import static org.apache.openmeetings.util.OmFileHelper.SIP_USER_ID;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.isSipEnabled;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
|
||||
import org.apache.openmeetings.db.entity.room.Room;
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
import org.apache.openmeetings.db.manager.ISipManager;
|
||||
import org.apache.openmeetings.util.OmFileHelper;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.asteriskjava.manager.DefaultManagerConnection;
|
||||
import org.asteriskjava.manager.ManagerConnection;
|
||||
import org.asteriskjava.manager.ManagerConnectionFactory;
|
||||
import org.asteriskjava.manager.ManagerConnectionState;
|
||||
import org.asteriskjava.manager.ResponseEvents;
|
||||
import org.asteriskjava.manager.action.ConfbridgeListAction;
|
||||
import org.asteriskjava.manager.action.DbDelAction;
|
||||
import org.asteriskjava.manager.action.DbDelTreeAction;
|
||||
import org.asteriskjava.manager.action.DbGetAction;
|
||||
import org.asteriskjava.manager.action.DbPutAction;
|
||||
import org.asteriskjava.manager.action.EventGeneratingAction;
|
||||
import org.asteriskjava.manager.action.HangupAction;
|
||||
import org.asteriskjava.manager.action.ManagerAction;
|
||||
import org.asteriskjava.manager.action.OriginateAction;
|
||||
import org.asteriskjava.manager.event.ConfbridgeListEvent;
|
||||
import org.asteriskjava.manager.response.ManagerError;
|
||||
import org.asteriskjava.manager.response.ManagerResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class SipManager implements ISipManager {
|
||||
private static final Logger log = LoggerFactory.getLogger(SipManager.class);
|
||||
public static final String ASTERISK_OM_FAMILY = "openmeetings";
|
||||
public static final String ASTERISK_OM_KEY = "rooms";
|
||||
public static final String SIP_FIRST_NAME = "SIP Transport";
|
||||
public static final String SIP_USER_NAME = "--SIP--";
|
||||
|
||||
@Value("${sip.hostname}")
|
||||
private String sipHostname;
|
||||
@Value("${sip.manager.port}")
|
||||
private int managerPort;
|
||||
@Value("${sip.manager.user}")
|
||||
private String managerUser;
|
||||
@Value("${sip.manager.password}")
|
||||
private String managerPass;
|
||||
@Value("${sip.manager.timeout}")
|
||||
private long managerTimeout;
|
||||
|
||||
@Value("${sip.ws.remote.port}")
|
||||
private int wsPort;
|
||||
@Value("${sip.ws.remote.user}")
|
||||
private String omSipUser;
|
||||
@Value("${sip.ws.remote.password}")
|
||||
private String omSipPasswd;
|
||||
@Value("${sip.ws.local.host}")
|
||||
private String localWsHost;
|
||||
@Value("${sip.ws.local.port.min}")
|
||||
private int minLocalWsPort = 6666;
|
||||
@Value("${sip.ws.local.port.max}")
|
||||
private int maxLocalWsPort = 7666;
|
||||
|
||||
private ManagerConnectionFactory factory;
|
||||
private ManagerConnection con;
|
||||
private String sipUserPicture;
|
||||
private BitSet ports;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (!Strings.isEmpty(sipHostname)) {
|
||||
factory = new ManagerConnectionFactory(
|
||||
sipHostname
|
||||
, managerPort
|
||||
, managerUser
|
||||
, managerPass);
|
||||
ports = new BitSet(maxLocalWsPort - minLocalWsPort);
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
if (con != null) {
|
||||
con.logoff();
|
||||
}
|
||||
}
|
||||
|
||||
private void connectManager() throws Exception {
|
||||
if (con == null || ManagerConnectionState.CONNECTED != con.getState()) {
|
||||
DefaultManagerConnection defCon = (DefaultManagerConnection)factory.createManagerConnection();
|
||||
defCon.setDefaultEventTimeout(managerTimeout);
|
||||
defCon.setDefaultResponseTimeout(managerTimeout);
|
||||
defCon.setSocketReadTimeout((int)managerTimeout);
|
||||
defCon.setSocketTimeout((int)managerTimeout);
|
||||
con = defCon;
|
||||
con.login("on");
|
||||
}
|
||||
}
|
||||
|
||||
private ManagerResponse exec(ManagerAction action) {
|
||||
if (factory == null) {
|
||||
log.warn("There is no Asterisk configured");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
connectManager();
|
||||
ManagerResponse r = con.sendAction(action);
|
||||
if (r != null) {
|
||||
log.debug("{}", r);
|
||||
}
|
||||
return (r instanceof ManagerError) ? null : r;
|
||||
} catch (Exception e) {
|
||||
log.error("Error while executing ManagerAction: {}", action, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ResponseEvents execEvent(EventGeneratingAction action) {
|
||||
if (factory == null) {
|
||||
log.warn("There is no Asterisk configured");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
connectManager();
|
||||
ResponseEvents r = con.sendEventGeneratingAction(action);
|
||||
if (r != null) {
|
||||
log.trace("{}", r.getResponse());
|
||||
}
|
||||
return (r == null || r.getResponse() instanceof ManagerError) ? null : r;
|
||||
} catch (Exception e) {
|
||||
log.error("Error while executing EventGeneratingAction: {}", action, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getKey(String confno) {
|
||||
return ASTERISK_OM_KEY + "/" + confno;
|
||||
}
|
||||
|
||||
static String getSipNumber(Room r) {
|
||||
return (r != null && r.getConfno() != null) ? r.getConfno() : null;
|
||||
}
|
||||
|
||||
public String get(String confno) {
|
||||
String pin = null;
|
||||
DbGetAction da = new DbGetAction(ASTERISK_OM_FAMILY, getKey(confno));
|
||||
ManagerResponse r = exec(da);
|
||||
if (r != null) {
|
||||
pin = r.getResponse();
|
||||
}
|
||||
return pin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String confno, String pin) {
|
||||
delete(confno);
|
||||
DbPutAction da = new DbPutAction(ASTERISK_OM_FAMILY, getKey(confno), pin);
|
||||
exec(da);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
DbDelTreeAction da = new DbDelTreeAction(ASTERISK_OM_FAMILY, ASTERISK_OM_KEY);
|
||||
exec(da);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String confno) {
|
||||
DbDelAction da = new DbDelAction(ASTERISK_OM_FAMILY, getKey(confno));
|
||||
exec(da);
|
||||
}
|
||||
|
||||
public long countUsers(String confno) {
|
||||
if (confno != null) {
|
||||
ConfbridgeListAction da = new ConfbridgeListAction(confno);
|
||||
ResponseEvents r = execEvent(da);
|
||||
if (r != null) {
|
||||
long count = r.getEvents().stream()
|
||||
.filter(ConfbridgeListEvent.class::isInstance)
|
||||
.filter(evt -> !omSipUser.equals(evt.getCallerIdName()))
|
||||
.count();
|
||||
log.trace("SipManager::countUsers == {}", count);
|
||||
return count;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform call to specified phone number and join to conference
|
||||
*
|
||||
* @param number
|
||||
* number to call
|
||||
* @param r
|
||||
* room to be connected to the call
|
||||
*/
|
||||
public void callExternalNumber(String number, Room r) {
|
||||
String sipNumber = getSipNumber(r);
|
||||
if (sipNumber == null) {
|
||||
log.warn("Failed to get SIP number for room: {}", r);
|
||||
return;
|
||||
}
|
||||
|
||||
OriginateAction oa = new OriginateAction();
|
||||
oa.setChannel(String.format("Local/%s@rooms-originate", sipNumber));
|
||||
oa.setContext("rooms-out");
|
||||
oa.setExten(number);
|
||||
oa.setPriority(1);
|
||||
oa.setTimeout(managerTimeout);
|
||||
|
||||
exec(oa);
|
||||
}
|
||||
|
||||
public void hangup(Room r) {
|
||||
String sipNumber = getSipNumber(r);
|
||||
if (sipNumber == null) {
|
||||
log.warn("Failed to get SIP number for room: {}", r);
|
||||
return;
|
||||
}
|
||||
|
||||
HangupAction ha = new HangupAction(
|
||||
String.format("/^Local/%s@rooms-originate-.*$/", sipNumber)
|
||||
, 16 // AST_CAUSE_NORMAL_CLEARING
|
||||
);
|
||||
exec(ha);
|
||||
}
|
||||
|
||||
public void setUserPicture(Function<User, String> pictureCreator) {
|
||||
User u = new User();
|
||||
u.setId(SIP_USER_ID);
|
||||
sipUserPicture = pictureCreator.apply(u);
|
||||
}
|
||||
|
||||
public User getSipUser(Room r) {
|
||||
if (factory == null || !isSipEnabled() || !r.isSipEnabled()) {
|
||||
return null;
|
||||
}
|
||||
User u = new User();
|
||||
u.setId(OmFileHelper.SIP_USER_ID);
|
||||
u.setFirstname(SIP_FIRST_NAME);
|
||||
u.setLogin(SIP_USER_NAME);
|
||||
u.setPictureUri(sipUserPicture);
|
||||
return u;
|
||||
}
|
||||
|
||||
String getSipHostname() {
|
||||
return sipHostname;
|
||||
}
|
||||
|
||||
int getWsPort() {
|
||||
return wsPort;
|
||||
}
|
||||
|
||||
String getOmSipUser() {
|
||||
return omSipUser;
|
||||
}
|
||||
|
||||
String getOmSipPasswd() {
|
||||
return omSipPasswd;
|
||||
}
|
||||
|
||||
String getLocalWsHost() {
|
||||
return localWsHost;
|
||||
}
|
||||
|
||||
public Optional<SipStackProcessor> createSipStackProcessor(String name, Room r, ISipCallbacks callbacks) throws Exception {
|
||||
if (factory == null || !isSipEnabled() || !r.isSipEnabled()) {
|
||||
log.warn("Asterisk is not configured or denied in room #{}", r.getId());
|
||||
return Optional.empty();
|
||||
}
|
||||
int port;
|
||||
synchronized (ports) {
|
||||
int free = ports.nextClearBit(0);
|
||||
ports.flip(free);
|
||||
port = minLocalWsPort + free;
|
||||
}
|
||||
return Optional.of(new SipStackProcessor(this, name, port, callbacks));
|
||||
}
|
||||
|
||||
void freePort(int port) {
|
||||
synchronized (ports) {
|
||||
ports.clear(port - minLocalWsPort);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,377 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.sip;
|
||||
|
||||
import static javax.sip.message.Request.BYE;
|
||||
import static javax.sip.message.Request.INVITE;
|
||||
import static javax.sip.message.Request.REGISTER;
|
||||
import static javax.sip.message.Response.OK;
|
||||
import static javax.sip.message.Response.RINGING;
|
||||
import static javax.sip.message.Response.TRYING;
|
||||
import static javax.sip.message.Response.UNAUTHORIZED;
|
||||
import static org.apache.openmeetings.core.sip.SipManager.getSipNumber;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.sip.ClientTransaction;
|
||||
import javax.sip.Dialog;
|
||||
import javax.sip.DialogTerminatedEvent;
|
||||
import javax.sip.IOExceptionEvent;
|
||||
import javax.sip.ListeningPoint;
|
||||
import javax.sip.RequestEvent;
|
||||
import javax.sip.ResponseEvent;
|
||||
import javax.sip.ServerTransaction;
|
||||
import javax.sip.SipException;
|
||||
import javax.sip.SipFactory;
|
||||
import javax.sip.SipProvider;
|
||||
import javax.sip.TimeoutEvent;
|
||||
import javax.sip.TransactionTerminatedEvent;
|
||||
import javax.sip.address.Address;
|
||||
import javax.sip.address.AddressFactory;
|
||||
import javax.sip.header.CSeqHeader;
|
||||
import javax.sip.header.ContactHeader;
|
||||
import javax.sip.header.HeaderFactory;
|
||||
import javax.sip.message.MessageFactory;
|
||||
import javax.sip.message.Request;
|
||||
import javax.sip.message.Response;
|
||||
|
||||
import org.apache.openmeetings.db.entity.room.Room;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import gov.nist.javax.sip.DialogTimeoutEvent;
|
||||
import gov.nist.javax.sip.SipListenerExt;
|
||||
import gov.nist.javax.sip.SipStackExt;
|
||||
import gov.nist.javax.sip.SipStackImpl;
|
||||
import gov.nist.javax.sip.address.SipUri;
|
||||
import gov.nist.javax.sip.clientauthutils.AuthenticationHelper;
|
||||
import gov.nist.javax.sip.clientauthutils.UserCredentials;
|
||||
import gov.nist.javax.sip.stack.NioMessageProcessorFactory;
|
||||
|
||||
public class SipStackProcessor implements SipListenerExt {
|
||||
private static final Logger log = LoggerFactory.getLogger(SipStackProcessor.class);
|
||||
private static final String SIP_TRANSPORT = "ws";
|
||||
private static final <T> Consumer<T> noop() {
|
||||
return t -> {};
|
||||
}
|
||||
|
||||
private final SipManager manager;
|
||||
private final ISipCallbacks callbacks;
|
||||
private final String sipHost;
|
||||
private final int wsPort;
|
||||
private final String omSipUser;
|
||||
private final String omSipPasswd;
|
||||
private final String localWsHost;
|
||||
private final int localWsPort;
|
||||
private final String tag;
|
||||
|
||||
private final SipProvider sipProvider;
|
||||
private final SipStackExt sipStack;
|
||||
private final MessageFactory messageFactory;
|
||||
private final HeaderFactory headerFactory;
|
||||
private final AddressFactory addressFactory;
|
||||
private final ContactHeader contactHeader;
|
||||
private final AtomicLong cseq = new AtomicLong();
|
||||
private final Random rnd = new Random();
|
||||
private Dialog dialog;
|
||||
|
||||
SipStackProcessor(SipManager manager, String name, int localWsPort, ISipCallbacks callbacks) throws Exception {
|
||||
this.manager = manager;
|
||||
this.callbacks = callbacks;
|
||||
this.sipHost = manager.getSipHostname();
|
||||
this.wsPort = manager.getWsPort();
|
||||
this.omSipUser = manager.getOmSipUser();
|
||||
this.omSipPasswd = manager.getOmSipPasswd();
|
||||
this.localWsHost = manager.getLocalWsHost();
|
||||
this.localWsPort = localWsPort;
|
||||
|
||||
final SipFactory sipFactory = SipFactory.getInstance();
|
||||
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty("javax.sip.STACK_NAME", name);
|
||||
if (log.isTraceEnabled()) {
|
||||
properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
|
||||
}
|
||||
properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "true");
|
||||
properties.setProperty("gov.nist.javax.sip.MESSAGE_PROCESSOR_FACTORY", NioMessageProcessorFactory.class.getName());
|
||||
sipStack = new SipStackImpl(properties);
|
||||
tag = getRnd(10);
|
||||
|
||||
messageFactory = sipFactory.createMessageFactory();
|
||||
headerFactory = sipFactory.createHeaderFactory();
|
||||
addressFactory = sipFactory.createAddressFactory();
|
||||
final ListeningPoint listeningPoint = sipStack.createListeningPoint(
|
||||
localWsHost
|
||||
, localWsPort
|
||||
, SIP_TRANSPORT);
|
||||
sipProvider = sipStack.createSipProvider(listeningPoint);
|
||||
sipProvider.addSipListener(this);
|
||||
Address contact = createAddr(omSipUser, localWsHost, uri -> {
|
||||
try {
|
||||
uri.setPort(localWsPort);
|
||||
uri.setTransportParam(SIP_TRANSPORT);
|
||||
} catch (ParseException e) {
|
||||
log.error("fail to create contact address", e);
|
||||
}
|
||||
});
|
||||
contactHeader = headerFactory.createContactHeader(contact);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processDialogTerminated(DialogTerminatedEvent evt) {
|
||||
log.error("processDialogTerminated: \n{}", evt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processIOException(IOExceptionEvent evt) {
|
||||
log.error("processIOException: \n{}", evt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processTimeout(TimeoutEvent evt) {
|
||||
log.error("processTimeout: \n{}", evt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processTransactionTerminated(TransactionTerminatedEvent evt) {
|
||||
log.error("processTransactionTerminated: \n{}", evt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processDialogTimeout(DialogTimeoutEvent timeoutEvent) {
|
||||
log.error("processDialogTimeout: \n{}", timeoutEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processRequest(RequestEvent evt) {
|
||||
log.debug("processRequest: \n\n{}", evt.getRequest());
|
||||
Request rq = evt.getRequest();
|
||||
String method = rq.getMethod();
|
||||
try {
|
||||
if (Request.OPTIONS.equals(method) || Request.BYE.equals(method)) {
|
||||
ServerTransaction transaction = evt.getServerTransaction();
|
||||
if (transaction == null) {
|
||||
transaction = sipProvider.getNewServerTransaction(rq);
|
||||
}
|
||||
Response resp = messageFactory.createResponse(200, rq);
|
||||
resp.addHeader(contactHeader);
|
||||
transaction.sendResponse(resp);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("processRequest", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processResponse(ResponseEvent evt) {
|
||||
Response resp = evt.getResponse();
|
||||
ClientTransaction curTrans = evt.getClientTransaction();
|
||||
Request prevReq = curTrans.getRequest();
|
||||
log.warn("Response code: {} on {}", resp.getStatusCode(), prevReq.getMethod());
|
||||
switch (resp.getStatusCode()) {
|
||||
case UNAUTHORIZED:
|
||||
processUnauth(evt);
|
||||
break;
|
||||
case OK:
|
||||
if (REGISTER.equals(prevReq.getMethod())) {
|
||||
callbacks.onRegisterOk();
|
||||
} else if (INVITE.equals(prevReq.getMethod())) {
|
||||
dialog = evt.getDialog();
|
||||
callbacks.onInviteOk(new String((byte[])resp.getContent()), answer -> ack(evt, answer));
|
||||
} else if (BYE.equals(prevReq.getMethod())) {
|
||||
doDestroy();
|
||||
}
|
||||
break;
|
||||
case TRYING, RINGING:
|
||||
break;
|
||||
default:
|
||||
log.debug("No handler for response: \n\n{}", resp);
|
||||
}
|
||||
}
|
||||
|
||||
private void ack(ResponseEvent evt, String answer) {
|
||||
try {
|
||||
Response resp = evt.getResponse();
|
||||
Dialog dlg = evt.getDialog();
|
||||
CSeqHeader cseqHead = (CSeqHeader)resp.getHeader(CSeqHeader.NAME);
|
||||
Request ack = dlg.createAck(cseqHead.getSeqNumber());
|
||||
addSdp(ack, answer);
|
||||
dlg.sendAck(ack);
|
||||
} catch (Exception e) {
|
||||
log.error("ack {}", evt, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sayBye() {
|
||||
try {
|
||||
Request req = dialog.createRequest(BYE);
|
||||
ClientTransaction ct = sipProvider.getNewClientTransaction(req);
|
||||
dialog.sendRequest(ct);
|
||||
} catch (SipException e) {
|
||||
log.error("bye ", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getRnd(int count) {
|
||||
return rnd.ints('0', 'z' + 1)
|
||||
.filter(ch -> (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z'))
|
||||
.limit(count)
|
||||
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
|
||||
.toString();
|
||||
}
|
||||
|
||||
private Address createAddr(String user) {
|
||||
return createAddr(user, sipHost, noop());
|
||||
}
|
||||
|
||||
private Address createAddr(String user, String host, Consumer<SipUri> cons) {
|
||||
try {
|
||||
SipUri uri = new SipUri();
|
||||
uri.setHost(host);
|
||||
uri.setUser(user);
|
||||
cons.accept(uri);
|
||||
return addressFactory.createAddress(user, uri);
|
||||
} catch (ParseException e) {
|
||||
log.error("fail to create address", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void sendRequest(String method, String to, Consumer<SipUri> uriCons, Consumer<Request> reqCons) {
|
||||
try {
|
||||
SipUri uri = new SipUri();
|
||||
uri.setHost(sipHost);
|
||||
uri.setPort(wsPort);
|
||||
uri.setTransportParam(SIP_TRANSPORT);
|
||||
uri.setMethodParam("GET");
|
||||
uri.setHeader("Host", sipHost);
|
||||
uri.setHeader("Location", "/ws");
|
||||
uriCons.accept(uri);
|
||||
|
||||
Request request = messageFactory.createRequest(
|
||||
uri
|
||||
, method
|
||||
, sipProvider.getNewCallId()
|
||||
, headerFactory.createCSeqHeader(cseq.incrementAndGet(), method)
|
||||
, headerFactory.createFromHeader(createAddr(omSipUser), tag)
|
||||
, headerFactory.createToHeader(createAddr(to), null)
|
||||
, List.of(headerFactory.createViaHeader(localWsHost, localWsPort, SIP_TRANSPORT, null))
|
||||
, headerFactory.createMaxForwardsHeader(70));
|
||||
request.addHeader(contactHeader);
|
||||
request.addHeader(headerFactory.createExpiresHeader(600));
|
||||
|
||||
reqCons.accept(request);
|
||||
|
||||
ClientTransaction trans = sipProvider.getNewClientTransaction(request);
|
||||
trans.sendRequest();
|
||||
} catch (Exception e) {
|
||||
log.error("Error while sending request", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void processUnauth(ResponseEvent evt) {
|
||||
Response resp = evt.getResponse();
|
||||
ClientTransaction curTrans = evt.getClientTransaction();
|
||||
AuthenticationHelper helper = sipStack.getAuthenticationHelper((trans, s) -> new UserCredentials() {
|
||||
@Override
|
||||
public String getUserName() {
|
||||
return omSipUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return omSipPasswd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSipDomain() {
|
||||
return "asterisk";
|
||||
}
|
||||
}, headerFactory);
|
||||
try {
|
||||
ClientTransaction trans = helper.handleChallenge(resp, curTrans, sipProvider, 5);
|
||||
trans.sendRequest();
|
||||
} catch (SipException e) {
|
||||
log.error("Error while sending AUTH", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addAllow(Request req) throws ParseException {
|
||||
req.addHeader(headerFactory.createAllowHeader("ACK,CANCEL,INVITE,MESSAGE,BYE,OPTIONS,INFO,NOTIFY,REFER"));
|
||||
}
|
||||
|
||||
public void register() {
|
||||
sendRequest(
|
||||
REGISTER
|
||||
, omSipUser
|
||||
, noop()
|
||||
, req -> {
|
||||
try {
|
||||
addAllow(req);
|
||||
} catch (ParseException e) {
|
||||
log.error("fail to create allow header", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addSdp(Request req, String sdp) throws Exception {
|
||||
if (sdp != null) {
|
||||
req.addHeader(headerFactory.createContentLengthHeader(sdp.length()));
|
||||
req.setContent(sdp, headerFactory.createContentTypeHeader("application", "sdp"));
|
||||
}
|
||||
}
|
||||
|
||||
public void invite(Room r, String sdp) {
|
||||
final String sipNumber = getSipNumber(r);
|
||||
if (sipNumber == null) {
|
||||
log.warn("Failed to get SIP number for room: {}", r);
|
||||
return;
|
||||
}
|
||||
sendRequest(
|
||||
INVITE
|
||||
, sipNumber
|
||||
, uri -> uri.setUser(sipNumber)
|
||||
, req -> {
|
||||
try {
|
||||
addAllow(req);
|
||||
addSdp(req, sdp);
|
||||
} catch (Exception e) {
|
||||
log.error("fail patch invite request", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (dialog != null) {
|
||||
sayBye();
|
||||
} else {
|
||||
doDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
private void doDestroy() {
|
||||
sipStack.stop();
|
||||
manager.freePort(localWsPort);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.util;
|
||||
|
||||
import static org.apache.openmeetings.core.util.WebSocketHelper.alwaysTrue;
|
||||
import static org.apache.openmeetings.core.util.WebSocketHelper.doSend;
|
||||
import static org.apache.openmeetings.core.util.WebSocketHelper.publish;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
import org.apache.openmeetings.IApplication;
|
||||
import org.apache.openmeetings.core.util.ws.WsMessageChat;
|
||||
import org.apache.openmeetings.core.util.ws.WsMessageChat2All;
|
||||
import org.apache.openmeetings.core.util.ws.WsMessageChat2User;
|
||||
import org.apache.openmeetings.db.entity.basic.ChatMessage;
|
||||
import org.apache.openmeetings.db.entity.room.Room.Right;
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
import org.apache.openmeetings.db.manager.IClientManager;
|
||||
import org.apache.openmeetings.db.util.FormatHelper;
|
||||
import org.apache.openmeetings.util.ws.IClusterWsMessage;
|
||||
|
||||
import com.github.openjson.JSONArray;
|
||||
import com.github.openjson.JSONObject;
|
||||
|
||||
public class ChatWebSocketHelper {
|
||||
public static final String ID_TAB_PREFIX = "chatTab-";
|
||||
public static final String ID_ALL = ID_TAB_PREFIX + "all";
|
||||
public static final String ID_ROOM_PREFIX = ID_TAB_PREFIX + "r";
|
||||
public static final String ID_USER_PREFIX = ID_TAB_PREFIX + "u";
|
||||
|
||||
private ChatWebSocketHelper() {
|
||||
// denied
|
||||
}
|
||||
|
||||
private static JSONObject setScope(JSONObject o, ChatMessage m, long curUserId) {
|
||||
String scope, scopeName = null;
|
||||
if (m.getToUser() != null) {
|
||||
User u = curUserId == m.getToUser().getId() ? m.getFromUser() : m.getToUser();
|
||||
scope = ID_USER_PREFIX + u.getId();
|
||||
scopeName = u.getDisplayName();
|
||||
} else if (m.getToRoom() != null) {
|
||||
scope = ID_ROOM_PREFIX + m.getToRoom().getId();
|
||||
o.put("needModeration", m.isNeedModeration());
|
||||
} else {
|
||||
scope = ID_ALL;
|
||||
}
|
||||
return o.put("scope", scope).put("scopeName", scopeName);
|
||||
}
|
||||
|
||||
public static JSONObject getMessage(User curUser, List<ChatMessage> list, BiConsumer<JSONObject, User> uFmt) {
|
||||
JSONArray arr = new JSONArray();
|
||||
for (ChatMessage m : list) {
|
||||
String smsg = m.getMessage();
|
||||
smsg = smsg == null ? smsg : " " + smsg.replace(" ", " ") + " ";
|
||||
JSONObject from = new JSONObject()
|
||||
.put("id", m.getFromUser().getId())
|
||||
.put("displayName", m.getFromName())
|
||||
.put("name", m.getFromUser().getDisplayName());
|
||||
if (uFmt != null) {
|
||||
uFmt.accept(from, m.getFromUser());
|
||||
}
|
||||
arr.put(setDates(setScope(new JSONObject(), m, curUser.getId())
|
||||
.put("id", m.getId())
|
||||
.put("message", smsg)
|
||||
.put("from", from)
|
||||
.put("actions", curUser.getId().equals(m.getFromUser().getId()) ? "short" : "full")
|
||||
, m, curUser, true));
|
||||
}
|
||||
return new JSONObject()
|
||||
.put("type", "chat")
|
||||
.put("msg", arr);
|
||||
}
|
||||
|
||||
public static boolean send(IClusterWsMessage msg) {
|
||||
if (msg instanceof WsMessageChat chatMsg) {
|
||||
if (msg instanceof WsMessageChat2User userMsg) {
|
||||
sendUser(userMsg.getUserId(), userMsg.getChatMessage(), userMsg.getMsg(), false);
|
||||
} else if (msg instanceof WsMessageChat2All allMsg) {
|
||||
sendAll(allMsg.getChatMessage(), allMsg.getMsg(), false);
|
||||
} else {
|
||||
sendRoom(chatMsg.getChatMessage(), chatMsg.getMsg(), false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void sendRoom(ChatMessage m, JSONObject msg) {
|
||||
sendRoom(m, msg, true);
|
||||
}
|
||||
|
||||
private static JSONObject setDates(JSONObject o, ChatMessage m, User u, boolean immediate) {
|
||||
final FastDateFormat fullFmt = FormatHelper.getDateTimeFormat(u);
|
||||
final FastDateFormat dateFmt = FormatHelper.getDateFormat(u);
|
||||
final FastDateFormat timeFmt = FormatHelper.getTimeFormat(u);
|
||||
JSONObject obj = immediate ? o : o.getJSONArray("msg").getJSONObject(0);
|
||||
obj.put("sent", fullFmt.format(m.getSent()))
|
||||
.put("date", dateFmt.format(m.getSent()))
|
||||
.put("time", timeFmt.format(m.getSent()));
|
||||
return o;
|
||||
}
|
||||
|
||||
private static void sendRoom(ChatMessage m, JSONObject msg, boolean publish) {
|
||||
if (publish) {
|
||||
publish(new WsMessageChat(m, msg));
|
||||
}
|
||||
WebSocketHelper.sendRoom(m.getToRoom().getId(), msg
|
||||
, c -> !m.isNeedModeration() || (m.isNeedModeration() && c.hasRight(Right.MODERATOR))
|
||||
, (o, c) -> setDates(o, m, c.getUser(), false));
|
||||
}
|
||||
|
||||
public static void sendUser(final Long userId, ChatMessage m, JSONObject msg) {
|
||||
sendUser(userId, m, msg, true);
|
||||
}
|
||||
|
||||
private static void sendUser(final Long userId, ChatMessage m, JSONObject msg, boolean publish) {
|
||||
if (publish) {
|
||||
publish(new WsMessageChat2User(userId, m, msg));
|
||||
}
|
||||
WebSocketHelper.sendUser(userId, msg, (o, c) -> setDates(o, m, c.getUser(), false), false);
|
||||
}
|
||||
|
||||
public static void sendAll(ChatMessage m, JSONObject msg) {
|
||||
sendAll(m, msg, true);
|
||||
}
|
||||
|
||||
private static void sendAll(ChatMessage m, JSONObject msg, boolean publish) {
|
||||
if (publish) {
|
||||
publish(new WsMessageChat2All(m, msg));
|
||||
}
|
||||
WebSocketHelper.send(a -> ((IApplication)a).getBean(IClientManager.class).stream()
|
||||
, (t, c) -> doSend(t, c, msg, (o, cm) -> setDates(o, m, c.getUser(), false), "all"), alwaysTrue());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.util;
|
||||
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getMinPasswdLength;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.isPwdCheckDigit;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.isPwdCheckSpecial;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.isPwdCheckUpper;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.openmeetings.db.dao.label.LabelDao;
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.apache.wicket.validation.IValidatable;
|
||||
import org.apache.wicket.validation.IValidator;
|
||||
import org.apache.wicket.validation.ValidationError;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class StrongPasswordValidator implements IValidator<String> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger log = LoggerFactory.getLogger(StrongPasswordValidator.class);
|
||||
private final boolean web;
|
||||
private User u;
|
||||
|
||||
public StrongPasswordValidator(final User u) {
|
||||
this(true, u);
|
||||
}
|
||||
|
||||
public StrongPasswordValidator(final boolean web, final User u) {
|
||||
this.web = web;
|
||||
this.u = u;
|
||||
}
|
||||
|
||||
private static boolean noDigit(String password) {
|
||||
return password == null || (isPwdCheckDigit() && !password.matches(".*\\d+.*"));
|
||||
}
|
||||
|
||||
private static boolean noSymbol(String password) {
|
||||
return password == null || (isPwdCheckSpecial() && !password.matches(".*[!@#$%^&*\\]\\[]+.*"));
|
||||
}
|
||||
|
||||
private static boolean noUpperCase(String password) {
|
||||
return password == null || (isPwdCheckUpper() && password.equals(password.toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
|
||||
private static boolean noLowerCase(String password) {
|
||||
return password == null || password.equals(password.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
private static boolean badLength(String password) {
|
||||
return password == null || password.length() < getMinPasswdLength();
|
||||
}
|
||||
|
||||
private static boolean checkWord(String password, String word) {
|
||||
if (Strings.isEmpty(password) || Strings.isEmpty(word) || word.length() < 3) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < word.length() - 3; ++i) {
|
||||
String substr = word.toLowerCase(Locale.ROOT).substring(i, i + 3);
|
||||
if (password.toLowerCase(Locale.ROOT).indexOf(substr) > -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasStopWords(String password) {
|
||||
if (checkWord(password, u.getLogin())) {
|
||||
return true;
|
||||
}
|
||||
if (u.getAddress() != null) {
|
||||
String email = u.getAddress().getEmail();
|
||||
if (!Strings.isEmpty(email)) {
|
||||
for (String part : email.split("[.@]")) {
|
||||
if (checkWord(password, part)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void error(IValidatable<String> pass, String key) {
|
||||
error(pass, key, null);
|
||||
}
|
||||
|
||||
private void error(IValidatable<String> pass, String key, Map<String, Object> params) {
|
||||
if (web) {
|
||||
ValidationError err = new ValidationError().addKey(key);
|
||||
if (params != null) {
|
||||
err.setVariables(params);
|
||||
}
|
||||
pass.error(err);
|
||||
} else {
|
||||
String msg = LabelDao.getString(key, 1L);
|
||||
if (params != null && !params.isEmpty() && !Strings.isEmpty(msg)) {
|
||||
for (Map.Entry<String, Object> e : params.entrySet()) {
|
||||
msg = msg.replace(String.format("${%s}", e.getKey()), "" + e.getValue());
|
||||
}
|
||||
}
|
||||
log.warn(msg);
|
||||
pass.error(new ValidationError(msg));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(IValidatable<String> pass) {
|
||||
if (badLength(pass.getValue())) {
|
||||
error(pass, "bad.password.short", Map.of("0", getMinPasswdLength()));
|
||||
}
|
||||
if (noLowerCase(pass.getValue())) {
|
||||
error(pass, "bad.password.lower");
|
||||
}
|
||||
if (noUpperCase(pass.getValue())) {
|
||||
error(pass, "bad.password.upper");
|
||||
}
|
||||
if (noDigit(pass.getValue())) {
|
||||
error(pass, "bad.password.digit");
|
||||
}
|
||||
if (noSymbol(pass.getValue())) {
|
||||
error(pass, "bad.password.special");
|
||||
}
|
||||
if (hasStopWords(pass.getValue())) {
|
||||
error(pass, "bad.password.stop");
|
||||
}
|
||||
}
|
||||
|
||||
public void setUser(User u) {
|
||||
this.u = u;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.util;
|
||||
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getWicketApplicationName;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.apache.openmeetings.IApplication;
|
||||
import org.apache.openmeetings.core.util.ws.WsMessageAll;
|
||||
import org.apache.openmeetings.core.util.ws.WsMessageRoom;
|
||||
import org.apache.openmeetings.core.util.ws.WsMessageRoomMsg;
|
||||
import org.apache.openmeetings.core.util.ws.WsMessageRoomOthers;
|
||||
import org.apache.openmeetings.core.util.ws.WsMessageUser;
|
||||
import org.apache.openmeetings.db.entity.basic.Client;
|
||||
import org.apache.openmeetings.db.entity.basic.IWsClient;
|
||||
import org.apache.openmeetings.db.manager.IClientManager;
|
||||
import org.apache.openmeetings.db.util.ws.RoomMessage;
|
||||
import org.apache.openmeetings.db.util.ws.TextRoomMessage;
|
||||
import org.apache.openmeetings.util.NullStringer;
|
||||
import org.apache.openmeetings.util.ws.IClusterWsMessage;
|
||||
import org.apache.wicket.Application;
|
||||
import org.apache.wicket.protocol.ws.WebSocketSettings;
|
||||
import org.apache.wicket.protocol.ws.api.IWebSocketConnection;
|
||||
import org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry;
|
||||
import org.apache.wicket.protocol.ws.api.registry.PageIdKey;
|
||||
import org.apache.wicket.protocol.ws.concurrent.Executor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.github.openjson.JSONObject;
|
||||
|
||||
public class WebSocketHelper {
|
||||
private static final Logger log = LoggerFactory.getLogger(WebSocketHelper.class);
|
||||
public static final <T> Predicate<T> alwaysTrue() {
|
||||
return x -> true;
|
||||
}
|
||||
|
||||
private WebSocketHelper() {
|
||||
// denied
|
||||
}
|
||||
|
||||
public static void sendClient(final IWsClient omClient, byte[] b) {
|
||||
if (omClient != null) {
|
||||
sendClient(omClient, c -> {
|
||||
try {
|
||||
c.sendMessage(b, 0, b.length);
|
||||
} catch (Exception e) {
|
||||
log.error("Error while sending binary message to client", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendClient(final IWsClient omClient, JSONObject msg) {
|
||||
log.trace("Sending WebSocket message to Client: {} -> {}", omClient, msg);
|
||||
if (omClient != null) {
|
||||
sendClient(omClient, c -> {
|
||||
try {
|
||||
c.sendMessage(msg.toString());
|
||||
} catch (Exception e) {
|
||||
log.error("Error while sending message to client", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendClient(final IWsClient omClient, final RoomMessage m) {
|
||||
log.trace("Sending WebSocket message to client: {} {}", m.getType(), m instanceof TextRoomMessage txtMsg ? txtMsg.getText() : "");
|
||||
sendClient(omClient, c -> {
|
||||
try {
|
||||
c.sendMessage(m);
|
||||
} catch (Exception e) {
|
||||
log.error("Error while sending message to client", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static IApplication getApp() {
|
||||
return (IApplication)Application.get(getWicketApplicationName());
|
||||
}
|
||||
|
||||
private static void sendClient(IWsClient client, Consumer<IWebSocketConnection> wsc) {
|
||||
new Thread(() -> {
|
||||
Application app = (Application)getApp();
|
||||
WebSocketSettings settings = WebSocketSettings.Holder.get(app);
|
||||
IWebSocketConnectionRegistry reg = settings.getConnectionRegistry();
|
||||
Executor executor = settings.getWebSocketPushMessageExecutor();
|
||||
final IWebSocketConnection wc = reg.getConnection(app, client.getSessionId(), new PageIdKey(client.getPageId()));
|
||||
if (wc != null && wc.isOpen()) {
|
||||
executor.run(() -> wsc.accept(wc));
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static boolean send(IClusterWsMessage msg) {
|
||||
if (msg instanceof WsMessageRoomMsg msgRoom) {
|
||||
sendRoom(msgRoom.msg(), false);
|
||||
} else if (msg instanceof WsMessageRoomOthers msgRoomOthers) {
|
||||
sendRoomOthers(msgRoomOthers.getRoomId(), msgRoomOthers.getUid(), msgRoomOthers.getMsg(), false);
|
||||
} else if (msg instanceof WsMessageRoom roomMsg) {
|
||||
sendRoom(roomMsg.getRoomId(), roomMsg.getMsg(), false);
|
||||
} else if (msg instanceof WsMessageUser msgUser) {
|
||||
sendUser(msgUser.getUserId(), msgUser.getMsg(), null, false);
|
||||
} else if (msg instanceof WsMessageAll msgAll) {
|
||||
sendAll(msgAll.msg(), false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void sendRoom(final RoomMessage m) {
|
||||
sendRoom(m, true);
|
||||
}
|
||||
|
||||
private static void sendRoom(final RoomMessage m, boolean publish) {
|
||||
if (publish) {
|
||||
publish(new WsMessageRoomMsg(m));
|
||||
}
|
||||
log.trace("Sending WebSocket message to room: {} {}", m.getType(), m instanceof TextRoomMessage txtMsg ? txtMsg.getText() : "");
|
||||
sendRoom(m.getRoomId(), (t, c) -> t.sendMessage(m), alwaysTrue());
|
||||
}
|
||||
|
||||
public static void sendServer(final RoomMessage m) {
|
||||
log.trace("Sending WebSocket message to All: {}", m);
|
||||
sendAll(c -> {
|
||||
try {
|
||||
c.sendMessage(m);
|
||||
} catch (Exception e) {
|
||||
log.error("Error while sending message to Server", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void sendRoom(final Long roomId, final JSONObject m) {
|
||||
sendRoom(roomId, m, true);
|
||||
}
|
||||
|
||||
private static void sendRoom(final Long roomId, final JSONObject m, boolean publish) {
|
||||
if (publish) {
|
||||
publish(new WsMessageRoom(roomId, m));
|
||||
}
|
||||
sendRoom(roomId, m, alwaysTrue(), null);
|
||||
}
|
||||
|
||||
public static void sendRoomOthers(final Long roomId, final String uid, final JSONObject m) {
|
||||
sendRoomOthers(roomId, uid, m, true);
|
||||
}
|
||||
|
||||
private static void sendRoomOthers(final Long roomId, final String uid, final JSONObject m, boolean publish) {
|
||||
if (publish) {
|
||||
publish(new WsMessageRoomOthers(roomId, uid, m));
|
||||
}
|
||||
sendRoom(roomId, m, c -> !uid.equals(c.getUid()), null);
|
||||
}
|
||||
|
||||
public static void sendUser(final Long userId, final JSONObject m) {
|
||||
sendUser(userId, m, null, true);
|
||||
}
|
||||
|
||||
static void sendUser(final Long userId, final JSONObject m, BiFunction<JSONObject, Client, JSONObject> func, boolean publish) {
|
||||
if (publish) {
|
||||
publish(new WsMessageUser(userId, m));
|
||||
}
|
||||
send(a -> ((IApplication)a).getBean(IClientManager.class).listByUser(userId).stream()
|
||||
, (t, c) -> doSend(t, c, m, func, "user"), alwaysTrue());
|
||||
}
|
||||
|
||||
public static void sendAll(final String m) {
|
||||
sendAll(m, true);
|
||||
}
|
||||
|
||||
private static void sendAll(final String m, boolean publish) {
|
||||
if (publish) {
|
||||
publish(new WsMessageAll(m));
|
||||
}
|
||||
log.trace("Sending text WebSocket message to All: {}", m);
|
||||
sendAll(c -> doSend(c, m, "ALL"));
|
||||
}
|
||||
|
||||
private static void sendAll(Consumer<IWebSocketConnection> sender) {
|
||||
new Thread(() -> {
|
||||
Application app = (Application)getApp();
|
||||
if (app == null) {
|
||||
return; // Application is not ready
|
||||
}
|
||||
WebSocketSettings settings = WebSocketSettings.Holder.get(app);
|
||||
IWebSocketConnectionRegistry reg = settings.getConnectionRegistry();
|
||||
Executor executor = settings.getWebSocketPushMessageExecutor();
|
||||
for (IWebSocketConnection wc : reg.getConnections(app)) {
|
||||
if (wc != null && wc.isOpen()) {
|
||||
executor.run(() -> sender.accept(wc));
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static void publish(@Nonnull IClusterWsMessage m) {
|
||||
IApplication app = getApp();
|
||||
new Thread(() -> app.publishWsTopic(m)).start();
|
||||
}
|
||||
|
||||
public static void sendRoom(final Long roomId, final JSONObject m, Predicate<Client> check, BiFunction<JSONObject, Client, JSONObject> func) {
|
||||
log.trace("Sending json WebSocket message to room: {}", m);
|
||||
sendRoom(roomId, (t, c) -> doSend(t, c, m, func, "room"), check);
|
||||
}
|
||||
|
||||
static void doSend(IWebSocketConnection conn, Client c, JSONObject msg, BiFunction<JSONObject, Client, JSONObject> func, String suffix) {
|
||||
doSend(conn, (func == null ? msg : func.apply(msg, c)).toString(new NullStringer()), suffix);
|
||||
}
|
||||
|
||||
private static void doSend(IWebSocketConnection c, String msg, String suffix) {
|
||||
try {
|
||||
c.sendMessage(msg);
|
||||
} catch (IOException e) {
|
||||
log.error("Error while sending message to {}", suffix, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendRoom(final Long roomId, BiConsumer<IWebSocketConnection, Client> consumer, Predicate<Client> check) {
|
||||
send(a -> ((IApplication)a).getBean(IClientManager.class).streamByRoom(roomId), consumer, check);
|
||||
}
|
||||
|
||||
static void send(
|
||||
final Function<Application, Stream<Client>> func
|
||||
, BiConsumer<IWebSocketConnection, Client> consumer
|
||||
, Predicate<Client> check)
|
||||
{
|
||||
new Thread(() -> {
|
||||
Application app = (Application)getApp();
|
||||
if (app == null) {
|
||||
return; // Application is not ready
|
||||
}
|
||||
WebSocketSettings settings = WebSocketSettings.Holder.get(app);
|
||||
IWebSocketConnectionRegistry reg = settings.getConnectionRegistry();
|
||||
Executor executor = settings.getWebSocketPushMessageExecutor();
|
||||
func.apply(app)
|
||||
.filter(check)
|
||||
.forEach(c -> {
|
||||
final IWebSocketConnection wc = reg.getConnection(app, c.getSessionId(), new PageIdKey(c.getPageId()));
|
||||
if (wc != null && wc.isOpen()) {
|
||||
executor.run(() -> consumer.accept(wc, c));
|
||||
}
|
||||
});
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.util.ws;
|
||||
|
||||
import org.apache.openmeetings.util.ws.IClusterWsMessage;
|
||||
|
||||
public record WsMessageAll(String msg) implements IClusterWsMessage {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.util.ws;
|
||||
|
||||
import org.apache.openmeetings.db.entity.basic.ChatMessage;
|
||||
import org.apache.openmeetings.util.NullStringer;
|
||||
import org.apache.openmeetings.util.ws.IClusterWsMessage;
|
||||
|
||||
import com.github.openjson.JSONObject;
|
||||
|
||||
public class WsMessageChat implements IClusterWsMessage {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final ChatMessage m;
|
||||
private final String msg;
|
||||
|
||||
public WsMessageChat(ChatMessage m, JSONObject msg) {
|
||||
this.m = m;
|
||||
this.msg = msg.toString(new NullStringer());
|
||||
}
|
||||
|
||||
public ChatMessage getChatMessage() {
|
||||
return m;
|
||||
}
|
||||
|
||||
public JSONObject getMsg() {
|
||||
return new JSONObject(msg);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.util.ws;
|
||||
|
||||
import org.apache.openmeetings.db.entity.basic.ChatMessage;
|
||||
|
||||
import com.github.openjson.JSONObject;
|
||||
|
||||
public class WsMessageChat2All extends WsMessageChat {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public WsMessageChat2All(ChatMessage m, JSONObject msg) {
|
||||
super(m, msg);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.util.ws;
|
||||
|
||||
import org.apache.openmeetings.db.entity.basic.ChatMessage;
|
||||
|
||||
import com.github.openjson.JSONObject;
|
||||
|
||||
public class WsMessageChat2User extends WsMessageChat {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Long userId;
|
||||
|
||||
public WsMessageChat2User(Long userId, ChatMessage m, JSONObject msg) {
|
||||
super(m, msg);
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.util.ws;
|
||||
|
||||
import org.apache.openmeetings.util.NullStringer;
|
||||
import org.apache.openmeetings.util.ws.IClusterWsMessage;
|
||||
|
||||
import com.github.openjson.JSONObject;
|
||||
|
||||
public class WsMessageRoom implements IClusterWsMessage {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Long roomId;
|
||||
private final String msg;
|
||||
|
||||
public WsMessageRoom(Long roomId, JSONObject msg) {
|
||||
this.roomId = roomId;
|
||||
this.msg = msg.toString(new NullStringer());
|
||||
}
|
||||
|
||||
public Long getRoomId() {
|
||||
return roomId;
|
||||
}
|
||||
|
||||
public JSONObject getMsg() {
|
||||
return new JSONObject(msg);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.util.ws;
|
||||
|
||||
import org.apache.openmeetings.db.util.ws.RoomMessage;
|
||||
import org.apache.openmeetings.util.ws.IClusterWsMessage;
|
||||
|
||||
public record WsMessageRoomMsg(RoomMessage msg) implements IClusterWsMessage {
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.util.ws;
|
||||
|
||||
import com.github.openjson.JSONObject;
|
||||
|
||||
public class WsMessageRoomOthers extends WsMessageRoom {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final String uid;
|
||||
|
||||
public WsMessageRoomOthers(Long roomId, String uid, JSONObject msg) {
|
||||
super(roomId, msg);
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
public String getUid() {
|
||||
return uid;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.util.ws;
|
||||
|
||||
import org.apache.openmeetings.util.NullStringer;
|
||||
import org.apache.openmeetings.util.ws.IClusterWsMessage;
|
||||
|
||||
import com.github.openjson.JSONObject;
|
||||
|
||||
public class WsMessageUser implements IClusterWsMessage {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Long userId;
|
||||
private final String msg;
|
||||
|
||||
public WsMessageUser(Long userId, JSONObject msg) {
|
||||
this.userId = userId;
|
||||
this.msg = msg.toString(new NullStringer());
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public JSONObject getMsg() {
|
||||
return new JSONObject(msg);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/DECORATION/1.8.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/DECORATION/1.8.0 http://maven.apache.org/xsd/decoration-1.8.0.xsd"
|
||||
name="Apache OpenMeetings Project">
|
||||
<body>
|
||||
<menu ref="parent"/>
|
||||
<menu name="Project">
|
||||
<item name="About" href="/index.html" />
|
||||
<item name="Info" href="/project-info.html" />
|
||||
<item name="Summary" href="/summary.html" />
|
||||
<item name="License" href="https://www.apache.org/licenses/" />
|
||||
<item name="Dependencies" href="/dependencies.html" />
|
||||
<item name="Dependency Convergence" href="/dependency-convergence.html" />
|
||||
<item name="RAT Report" href="/rat-report.html" />
|
||||
<item name="JavaDoc" href="/apidocs/index.html" target="_blank" />
|
||||
</menu>
|
||||
</body>
|
||||
<custom>
|
||||
<reflowSkin>
|
||||
<bottomNav maxSpan="12">
|
||||
<column>Parent Project</column>
|
||||
<column>Project</column>
|
||||
</bottomNav>
|
||||
</reflowSkin>
|
||||
</custom>
|
||||
</project>
|
||||
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.mail;
|
||||
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.setMailFrom;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.setSmtpPass;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.setSmtpPort;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.setSmtpServer;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.setSmtpUseTls;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.setSmtpUser;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jakarta.mail.Message;
|
||||
import jakarta.mail.Transport;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author swagner
|
||||
*
|
||||
*/
|
||||
class TestMailSending {
|
||||
private static final Logger log = LoggerFactory.getLogger(TestMailSending.class);
|
||||
|
||||
//Example GMail email server data
|
||||
private String smtpServer = "smtp.gmail.com";
|
||||
private int smtpPort = 587;
|
||||
private String from = "test-app@apache.org";
|
||||
private String mailAuthUser = "test-app@gmail.com";
|
||||
private String mailAuthPass = "test-pass";
|
||||
private boolean mailTls = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* It does not make a lot of send to test this in every test suite, it is more for manual testing.
|
||||
* Handy to check your server and the JavaMail setting properties
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
void doTestSendEmail() {
|
||||
try {
|
||||
Transport.send(getMimeMessage());
|
||||
} catch (Exception err) {
|
||||
log.error("Error", err);
|
||||
}
|
||||
assertTrue(true);
|
||||
}
|
||||
|
||||
private MimeMessage getMimeMessage() throws Exception {
|
||||
MailHandler h = new MailHandler();
|
||||
setSmtpServer(smtpServer);
|
||||
setSmtpPort(smtpPort);
|
||||
setSmtpUseTls(mailTls);
|
||||
setSmtpUser(mailAuthUser);
|
||||
setSmtpPass(mailAuthPass);
|
||||
setMailFrom(from);
|
||||
// Building MimeMessage
|
||||
MimeMessage msg = h.getBasicMimeMessage();
|
||||
msg.setSubject("getSubject()");
|
||||
msg.addRecipients(Message.RecipientType.TO, InternetAddress.parse("test-recipient@gmail.com", false));
|
||||
|
||||
return h.appendBody(msg, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.core.util;
|
||||
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.setPwdCheckDigit;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.setPwdCheckSpecial;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.setPwdCheckUpper;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.openmeetings.db.dao.label.LabelDao;
|
||||
import org.apache.openmeetings.db.entity.user.Address;
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
import org.apache.wicket.validation.Validatable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
class TestStrongPasswordValidator {
|
||||
private static User getUser(String login, String email) {
|
||||
User u = new User();
|
||||
u.setLogin(login);
|
||||
u.setAddress(new Address());
|
||||
u.getAddress().setEmail(email);
|
||||
return u;
|
||||
}
|
||||
|
||||
private static User getUser3() {
|
||||
return getUser("2222", "2222@local");
|
||||
}
|
||||
|
||||
private static Stream<Arguments> provideTestArgs() {
|
||||
List<Arguments> args = new ArrayList<>();
|
||||
for (boolean web : new boolean[] {true, false}) {
|
||||
args.add(Arguments.of(null, web, getUser(null, null), 5));
|
||||
User u1 = getUser("1", null);
|
||||
args.add(Arguments.of(null, web, u1, 5));
|
||||
User u2 = getUser("2222", null);
|
||||
args.add(Arguments.of("1", web, u2, 4));
|
||||
User u3 = getUser3();
|
||||
args.add(Arguments.of("password", web, u3, 3));
|
||||
args.add(Arguments.of("passWord", web, u3, 2));
|
||||
args.add(Arguments.of("passWord222", web, u3, 2));
|
||||
args.add(Arguments.of("passWord2!", web, u3, 0));
|
||||
}
|
||||
return args.stream();
|
||||
}
|
||||
|
||||
void runWrapped(Runnable task) {
|
||||
try (MockedStatic<LabelDao> labelMock = mockStatic(LabelDao.class)) {
|
||||
labelMock.when(() -> LabelDao.getString(any(String.class), any(Long.class))).then(new Answer<String>() {
|
||||
@Override
|
||||
public String answer(InvocationOnMock invocation) throws Throwable {
|
||||
return invocation.getArgument(0);
|
||||
}
|
||||
});
|
||||
task.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDefCtr() {
|
||||
runWrapped(() -> {
|
||||
Validatable<String> pass = new Validatable<>(null);
|
||||
StrongPasswordValidator validator = new StrongPasswordValidator(new User());
|
||||
validator.validate(pass);
|
||||
assertEquals(5, pass.getErrors().size());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetUser() {
|
||||
runWrapped(() -> {
|
||||
Validatable<String> pass = new Validatable<>(null);
|
||||
StrongPasswordValidator validator = new StrongPasswordValidator(null);
|
||||
validator.setUser(new User());
|
||||
validator.validate(pass);
|
||||
assertEquals(5, pass.getErrors().size());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoUpper() {
|
||||
try {
|
||||
setPwdCheckUpper(false);
|
||||
test("password", false, getUser3(), 2);
|
||||
} finally {
|
||||
setPwdCheckUpper(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoDigit() {
|
||||
try {
|
||||
setPwdCheckDigit(false);
|
||||
test("password", false, getUser3(), 2);
|
||||
} finally {
|
||||
setPwdCheckDigit(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoSpecial() {
|
||||
try {
|
||||
setPwdCheckSpecial(false);
|
||||
test("password", false, getUser3(), 2);
|
||||
} finally {
|
||||
setPwdCheckSpecial(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("provideTestArgs")
|
||||
void test(String pwd, boolean web, User u, int expectedErrors) {
|
||||
runWrapped(() -> {
|
||||
Validatable<String> pass = new Validatable<>(pwd);
|
||||
StrongPasswordValidator validator = new StrongPasswordValidator(web, u);
|
||||
validator.validate(pass);
|
||||
assertEquals(expectedErrors, pass.getErrors().size(), "Expected exactly " + expectedErrors + " errors, pass: '" + pwd + "', user: " + u);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,151 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<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/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.apache.openmeetings</groupId>
|
||||
<artifactId>openmeetings-parent</artifactId>
|
||||
<version>7.2.0-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>openmeetings-db</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Openmeetings DB</name>
|
||||
<description>OpenMeetings module for all Database/DTO related classes</description>
|
||||
<properties>
|
||||
<site.basedir>${project.parent.basedir}</site.basedir>
|
||||
<autoModuleName>apache.openmeetings.db</autoModuleName>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.openmeetings</groupId>
|
||||
<artifactId>openmeetings-util</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- required for JAXB/CXF -->
|
||||
<groupId>org.glassfish.jaxb</groupId>
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.wicketstuff</groupId>
|
||||
<artifactId>wicketstuff-datastore-hazelcast</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.openjpa</groupId>
|
||||
<artifactId>openjpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-dbcp2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.oracle.database.jdbc</groupId>
|
||||
<artifactId>ojdbc10</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- required for @NonNull, temporary -->
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.openmeetings</groupId>
|
||||
<artifactId>openmeetings-util</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<configuration>
|
||||
<groups>
|
||||
<group>
|
||||
<title>DB DAO, DTO and Entity objects</title>
|
||||
<packages>org.apache.openmeetings.db</packages>
|
||||
</group>
|
||||
</groups>
|
||||
<skip>${site.skip}</skip>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>javadoc-no-fork</goal>
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.openjpa</groupId>
|
||||
<artifactId>openjpa-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>**/entity/**/*.class</includes>
|
||||
<addDefaultConstructor>true</addDefaultConstructor>
|
||||
<enforcePropertyRestrictions>true</enforcePropertyRestrictions>
|
||||
<persistenceXmlFile>${project.parent.basedir}/openmeetings-web/src/main/webapp/WEB-INF/classes/META-INF/h2_persistence.xml</persistenceXmlFile>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>enhancer</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>enhance</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.apache.openmeetings.db.entity.room.Invitation;
|
||||
import org.apache.openmeetings.util.ws.IClusterWsMessage;
|
||||
import org.apache.wicket.request.IExceptionMapper;
|
||||
import org.apache.wicket.request.IRequestMapper;
|
||||
import org.apache.wicket.request.mapper.parameter.PageParameters;
|
||||
|
||||
public interface IApplication {
|
||||
<T> T getBean(Class<T> clazz);
|
||||
ServletContext getServletContext();
|
||||
IRequestMapper getRootRequestMapper();
|
||||
Supplier<IExceptionMapper> getExceptionMapperProvider();
|
||||
String getOmString(String key);
|
||||
String getOmString(String key, long languageId);
|
||||
String getOmString(String key, final Locale loc, String... params);
|
||||
String getOmContactsLink();
|
||||
String getOmInvitationLink(Invitation i, String baseUrl);
|
||||
String urlForActivatePage(PageParameters pp);
|
||||
|
||||
String getServerId();
|
||||
|
||||
//JPA
|
||||
void updateJpaAddresses();
|
||||
|
||||
//WS
|
||||
void publishWsTopic(@Nonnull IClusterWsMessage msg);
|
||||
Set<String> getWsUrls();
|
||||
|
||||
void updateTheme();
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public interface IWebSession {
|
||||
void setLanguage(long languageId);
|
||||
Locale getLocale();
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind;
|
||||
|
||||
public class Constants {
|
||||
public static final String VERSION_LIST_NODE = "version";
|
||||
public static final String VERSION_NODE = "version";
|
||||
public static final String CFG_LIST_NODE = "configs";
|
||||
public static final String CFG_NODE = "config";
|
||||
public static final String GROUP_LIST_NODE = "organisations";
|
||||
public static final String GROUP_NODE = "organisation";
|
||||
public static final String LDAP_LIST_NODE = "ldapconfigs";
|
||||
public static final String LDAP_NODE = "ldapconfig";
|
||||
public static final String OAUTH_LIST_NODE = "oauth2servers";
|
||||
public static final String OAUTH_NODE = "OAuthServer";
|
||||
public static final String USER_LIST_NODE = "users";
|
||||
public static final String USER_NODE = "user";
|
||||
public static final String ROOM_LIST_NODE = "rooms";
|
||||
public static final String ROOM_NODE = "room";
|
||||
public static final String ROOM_GRP_LIST_NODE = "room_organisations";
|
||||
public static final String ROOM_GRP_NODE = "room_organisation";
|
||||
public static final String CHAT_LIST_NODE = "chat_messages";
|
||||
public static final String CHAT_NODE = "ChatMessage";
|
||||
public static final String CALENDAR_LIST_NODE = "calendars";
|
||||
public static final String CALENDAR_NODE = "calendar";
|
||||
public static final String APPOINTMENT_LIST_NODE = "appointments";
|
||||
public static final String APPOINTMENT_NODE = "appointment";
|
||||
public static final String MMEMBER_LIST_NODE = "meetingmembers";
|
||||
public static final String MMEMBER_NODE = "meetingmember";
|
||||
public static final String RECORDING_LIST_NODE = "flvrecordings";
|
||||
public static final String RECORDING_NODE = "flvrecording";
|
||||
public static final String MSG_FOLDER_LIST_NODE = "privatemessagefolders";
|
||||
public static final String MSG_FOLDER_NODE = "privatemessagefolder";
|
||||
public static final String CONTACT_LIST_NODE = "usercontacts";
|
||||
public static final String CONTACT_NODE = "usercontact";
|
||||
public static final String MSG_LIST_NODE = "privatemessages";
|
||||
public static final String MSG_NODE = "privatemessage";
|
||||
public static final String FILE_LIST_NODE = "fileExplorerItems";
|
||||
public static final String FILE_NODE = "fileItem";
|
||||
public static final String POLL_LIST_NODE = "roompolls";
|
||||
public static final String POLL_NODE = "roompoll";
|
||||
public static final String ROOM_FILE_LIST_NODE = "RoomFiles";
|
||||
public static final String ROOM_FILE_NODE = "RoomFile";
|
||||
public static final String EXTRA_MENU_LIST_NODE = "ExtraMenus";
|
||||
public static final String EXTRA_MENU_NODE = "ExtraMenu";
|
||||
|
||||
private Constants() {
|
||||
//shoudn't be used
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.openmeetings.db.dao.calendar.AppointmentDao;
|
||||
import org.apache.openmeetings.db.entity.calendar.Appointment;
|
||||
|
||||
public class AppointmentAdapter extends EntityAdapter<Appointment> {
|
||||
public AppointmentAdapter() {
|
||||
super();
|
||||
}
|
||||
|
||||
public AppointmentAdapter(AppointmentDao dao, Map<Long, Long> idMap) {
|
||||
super(dao, idMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Appointment newEntity() {
|
||||
return new Appointment();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import static org.apache.commons.lang3.math.NumberUtils.toInt;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
import org.apache.openmeetings.db.entity.calendar.Appointment;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
|
||||
public class AppointmentReminderAdapter extends XmlAdapter<String, Appointment.Reminder> {
|
||||
|
||||
@Override
|
||||
public String marshal(Appointment.Reminder v) throws Exception {
|
||||
return "" + v.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Appointment.Reminder unmarshal(String v) throws Exception {
|
||||
return Strings.isEmpty(v) ? null : Appointment.Reminder.get(toInt(v));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
public class BooleanAdapter extends XmlAdapter<String, Boolean> {
|
||||
|
||||
@Override
|
||||
public String marshal(Boolean v) throws Exception {
|
||||
return "" + Boolean.TRUE.equals(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean unmarshal(String v) throws Exception {
|
||||
return Boolean.TRUE.equals(Boolean.valueOf(v));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
import org.apache.openmeetings.db.entity.basic.Configuration;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
|
||||
public class ConfigTypeAdapter extends XmlAdapter<String, Configuration.Type> {
|
||||
|
||||
@Override
|
||||
public String marshal(Configuration.Type v) throws Exception {
|
||||
return v.name().toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration.Type unmarshal(String v) throws Exception {
|
||||
return Strings.isEmpty(v) ? null : Configuration.Type.valueOf(v.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
public class DateAdapter extends XmlAdapter<String, Date> {
|
||||
|
||||
@Override
|
||||
public String marshal(Date v) throws Exception {
|
||||
return "" + v.getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date unmarshal(String v) throws Exception {
|
||||
if (v == null || "null".equals(v)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Long t = Long.valueOf(v);
|
||||
|
||||
if (t != null) {
|
||||
return new Date(t);
|
||||
}
|
||||
} catch (Exception err) {
|
||||
//no-op
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import static org.apache.commons.lang3.math.NumberUtils.toLong;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
import org.apache.openmeetings.db.dao.IDataProviderDao;
|
||||
import org.apache.openmeetings.db.entity.IDataProviderEntity;
|
||||
|
||||
public abstract class EntityAdapter<E extends IDataProviderEntity> extends XmlAdapter<String, E> {
|
||||
private final IDataProviderDao<E> dao;
|
||||
private final Map<Long, Long> idMap;
|
||||
|
||||
protected EntityAdapter() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
protected EntityAdapter(IDataProviderDao<E> dao, Map<Long, Long> idMap) {
|
||||
this.dao = dao;
|
||||
this.idMap = idMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String marshal(E v) throws Exception {
|
||||
return "" + v.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E unmarshal(String v) throws Exception {
|
||||
long oldId = toLong(v);
|
||||
Long newId = idMap.containsKey(oldId) ? idMap.get(oldId) : oldId;
|
||||
|
||||
E r = dao.get(newId);
|
||||
return r == null ? newEntity() : r;
|
||||
}
|
||||
|
||||
protected abstract E newEntity();
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.openmeetings.db.dao.file.BaseFileItemDao;
|
||||
import org.apache.openmeetings.db.entity.file.BaseFileItem;
|
||||
import org.apache.openmeetings.db.entity.file.FileItem;
|
||||
|
||||
public class FileAdapter extends EntityAdapter<BaseFileItem> {
|
||||
public FileAdapter() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FileAdapter(BaseFileItemDao dao, Map<Long, Long> idMap) {
|
||||
super(dao, idMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseFileItem newEntity() {
|
||||
return new FileItem();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
import org.apache.openmeetings.db.entity.file.BaseFileItem;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
|
||||
public class FileTypeAdapter extends XmlAdapter<String, BaseFileItem.Type> {
|
||||
|
||||
@Override
|
||||
public String marshal(BaseFileItem.Type v) throws Exception {
|
||||
return v.name().toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseFileItem.Type unmarshal(String v) throws Exception {
|
||||
if ("PollChart".equalsIgnoreCase(v)) {
|
||||
return BaseFileItem.Type.POLL_CHART;
|
||||
}
|
||||
if ("WmlFile".equalsIgnoreCase(v)) {
|
||||
return BaseFileItem.Type.WML_FILE;
|
||||
}
|
||||
return Strings.isEmpty(v) ? null : BaseFileItem.Type.valueOf(v.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.openmeetings.db.dao.user.GroupDao;
|
||||
import org.apache.openmeetings.db.entity.user.Group;
|
||||
|
||||
public class GroupAdapter extends EntityAdapter<Group> {
|
||||
public GroupAdapter() {
|
||||
super();
|
||||
}
|
||||
|
||||
public GroupAdapter(GroupDao dao, Map<Long, Long> idMap) {
|
||||
super(dao, idMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Group newEntity() {
|
||||
return new Group();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import static org.apache.commons.lang3.math.NumberUtils.toInt;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
public class IntAdapter extends XmlAdapter<String, Integer> {
|
||||
|
||||
@Override
|
||||
public String marshal(Integer v) throws Exception {
|
||||
return (v == null ? "0" : v.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer unmarshal(String v) throws Exception {
|
||||
return toInt(v);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
import org.apache.openmeetings.util.CalendarPatterns;
|
||||
|
||||
public class LocalDateAdapter extends XmlAdapter<String, LocalDate> {
|
||||
|
||||
@Override
|
||||
public String marshal(LocalDate v) throws Exception {
|
||||
return v.format(DateTimeFormatter.ofPattern(CalendarPatterns.ISO8601_DATE_FORMAT_STRING));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate unmarshal(String v) throws Exception {
|
||||
if (v == null || "null".equals(v)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return LocalDate.parse(v, DateTimeFormatter.ofPattern(CalendarPatterns.ISO8601_DATE_FORMAT_STRING));
|
||||
} catch (Exception err) {
|
||||
//no-op
|
||||
}
|
||||
try {
|
||||
Long t = Long.valueOf(v);
|
||||
|
||||
if (t != null) {
|
||||
return Instant.ofEpochMilli(t).atZone(ZoneOffset.UTC).toLocalDate();
|
||||
}
|
||||
} catch (Exception err) {
|
||||
//no-op
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import static org.apache.commons.lang3.math.NumberUtils.toLong;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
public class LongAdapter extends XmlAdapter<String, Long> {
|
||||
|
||||
@Override
|
||||
public String marshal(Long v) throws Exception {
|
||||
return (v == null ? "0" : v.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long unmarshal(String v) throws Exception {
|
||||
return toLong(v);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
import org.apache.openmeetings.db.dto.user.OAuthUser;
|
||||
import org.apache.openmeetings.db.util.XmlHelper;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
public class OauthMapAdapter extends XmlAdapter<Object, Map<String, String>> {
|
||||
|
||||
@Override
|
||||
public Object marshal(Map<String, String> v) throws Exception {
|
||||
Document document = XmlHelper.createBuilder().newDocument();
|
||||
Element root = document.createElement("mapping");
|
||||
document.appendChild(root);
|
||||
|
||||
for (Entry<String, String> e : v.entrySet()) {
|
||||
Element entry = document.createElement("entry");
|
||||
Element key = document.createElement("key");
|
||||
key.setTextContent(e.getKey());
|
||||
entry.appendChild(key);
|
||||
Element value = document.createElement("value");
|
||||
value.setTextContent(e.getValue());
|
||||
entry.appendChild(value);
|
||||
root.appendChild(entry);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
private static void putValue(Map<String, String> map, String key, String value) {
|
||||
if (!Strings.isEmpty(value)) {
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, String> getMap(String key, String value) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
putValue(map, key, value);
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<String, String> getMap(NodeList entries) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (int i = 0; i < entries.getLength(); ++i) {
|
||||
Node entry = entries.item(i);
|
||||
NodeList children = entry.getChildNodes();
|
||||
if ("entry".equals(entry.getLocalName()) && children.getLength() > 1) {
|
||||
Node key = null;
|
||||
Node value = null;
|
||||
for (int j = 0; j < children.getLength(); ++j) {
|
||||
Node n = children.item(j);
|
||||
if (n.getNodeType() == Node.TEXT_NODE) {
|
||||
continue;
|
||||
}
|
||||
if (key == null) {
|
||||
key = n;
|
||||
continue;
|
||||
}
|
||||
value = n;
|
||||
break;
|
||||
}
|
||||
if (key != null && value != null) {
|
||||
putValue(map, key.getTextContent(), value.getTextContent());
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> unmarshal(Object v) throws Exception {
|
||||
Element el = (Element)v;
|
||||
if ("loginParamName".equals(el.getLocalName())) {
|
||||
return getMap(OAuthUser.PARAM_LOGIN, el.getTextContent());
|
||||
}
|
||||
if ("emailParamName".equals(el.getLocalName())) {
|
||||
return getMap(OAuthUser.PARAM_EMAIL, el.getTextContent());
|
||||
}
|
||||
if ("firstnameParamName".equals(el.getLocalName())) {
|
||||
return getMap(OAuthUser.PARAM_FNAME, el.getTextContent());
|
||||
}
|
||||
if ("lastnameParamName".equals(el.getLocalName())) {
|
||||
return getMap(OAuthUser.PARAM_LNAME, el.getTextContent());
|
||||
}
|
||||
if ("mapping".equals(el.getLocalName())) {
|
||||
return getMap(el.getChildNodes());
|
||||
}
|
||||
return Map.of();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.openmeetings.db.dao.calendar.OmCalendarDao;
|
||||
import org.apache.openmeetings.db.entity.calendar.OmCalendar;
|
||||
|
||||
public class OmCalendarAdapter extends EntityAdapter<OmCalendar> {
|
||||
public OmCalendarAdapter() {
|
||||
super();
|
||||
}
|
||||
|
||||
public OmCalendarAdapter(OmCalendarDao dao, Map<Long, Long> idMap) {
|
||||
super(dao, idMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OmCalendar newEntity() {
|
||||
return new OmCalendar();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import static org.apache.commons.lang3.math.NumberUtils.toInt;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
import org.apache.openmeetings.db.entity.room.RoomPoll;
|
||||
|
||||
public class PollTypeAdapter extends XmlAdapter<String, RoomPoll.Type> {
|
||||
|
||||
@Override
|
||||
public String marshal(RoomPoll.Type v) throws Exception {
|
||||
return "" + v.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoomPoll.Type unmarshal(String v) throws Exception {
|
||||
return RoomPoll.Type.get(toInt(v));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
import org.apache.openmeetings.db.entity.record.Recording;
|
||||
import org.apache.openmeetings.db.entity.record.Recording.Status;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
|
||||
public class RecordingStatusAdapter extends XmlAdapter<String, Recording.Status> {
|
||||
|
||||
@Override
|
||||
public String marshal(Recording.Status v) throws Exception {
|
||||
return "" + (v == null
|
||||
? Recording.Status.NONE.name()
|
||||
: v.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Recording.Status unmarshal(String v) throws Exception {
|
||||
if ("PROCESSING".equalsIgnoreCase(v)) {
|
||||
return Recording.Status.CONVERTING;
|
||||
}
|
||||
Recording.Status result = Status.NONE;
|
||||
if (!Strings.isEmpty(v)) {
|
||||
try {
|
||||
result = Recording.Status.valueOf(v);
|
||||
} catch (Exception e) {
|
||||
//no-op
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.openmeetings.db.dao.room.RoomDao;
|
||||
import org.apache.openmeetings.db.entity.room.Room;
|
||||
|
||||
public class RoomAdapter extends EntityAdapter<Room> {
|
||||
public RoomAdapter() {
|
||||
super();
|
||||
}
|
||||
|
||||
public RoomAdapter(RoomDao dao, Map<Long, Long> idMap) {
|
||||
super(dao, idMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Room newEntity() {
|
||||
return new Room();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
import org.apache.openmeetings.db.entity.room.Room;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
|
||||
public class RoomElementAdapter extends XmlAdapter<String, Room.RoomElement> {
|
||||
|
||||
@Override
|
||||
public String marshal(Room.RoomElement v) throws Exception {
|
||||
return v.name().toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Room.RoomElement unmarshal(String v) throws Exception {
|
||||
if ("TopBar".equals(v)) {
|
||||
return Room.RoomElement.TOP_BAR;
|
||||
}
|
||||
if ("ActionMenu".equals(v)) {
|
||||
return Room.RoomElement.ACTION_MENU;
|
||||
}
|
||||
if ("PollMenu".equals(v)) {
|
||||
return Room.RoomElement.POLL_MENU;
|
||||
}
|
||||
if ("ScreenSharing".equals(v)) {
|
||||
return Room.RoomElement.SCREEN_SHARING;
|
||||
}
|
||||
if ("MicrophoneStatus".equals(v)) {
|
||||
return Room.RoomElement.MICROPHONE_STATUS;
|
||||
}
|
||||
if ("UserCount".equals(v)) {
|
||||
return Room.RoomElement.USER_COUNT;
|
||||
}
|
||||
return Strings.isEmpty(v) ? null : Room.RoomElement.valueOf(v.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import static org.apache.commons.lang3.math.NumberUtils.toInt;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
import org.apache.openmeetings.db.entity.room.Room;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
|
||||
public class RoomTypeAdapter extends XmlAdapter<String, Room.Type> {
|
||||
|
||||
@Override
|
||||
public String marshal(Room.Type v) throws Exception {
|
||||
return "" + v.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Room.Type unmarshal(String v) throws Exception {
|
||||
return Strings.isEmpty(v) ? null : Room.Type.get(toInt(v));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import static org.apache.commons.lang3.math.NumberUtils.toInt;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
import org.apache.openmeetings.db.entity.user.User.Salutation;
|
||||
|
||||
public class SalutationAdapter extends XmlAdapter<String, Salutation> {
|
||||
|
||||
@Override
|
||||
public String marshal(Salutation v) throws Exception {
|
||||
return "" + v.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Salutation unmarshal(String v) throws Exception {
|
||||
return Salutation.get(toInt(v));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.openmeetings.db.dao.user.UserDao;
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
|
||||
public class UserAdapter extends EntityAdapter<User> {
|
||||
public UserAdapter() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UserAdapter(UserDao dao, Map<Long, Long> idMap) {
|
||||
super(dao, idMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected User newEntity() {
|
||||
return new User();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
|
||||
public class UserRightAdapter extends XmlAdapter<String, User.Right> {
|
||||
|
||||
@Override
|
||||
public String marshal(User.Right v) throws Exception {
|
||||
return v.name().toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User.Right unmarshal(String v) throws Exception {
|
||||
return Strings.isEmpty(v) ? null : User.Right.valueOf(v.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.bind.adapter;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
|
||||
public class UserTypeAdapter extends XmlAdapter<String, User.Type> {
|
||||
|
||||
@Override
|
||||
public String marshal(User.Type v) throws Exception {
|
||||
return v.name().toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User.Type unmarshal(String v) throws Exception {
|
||||
return Strings.isEmpty(v) ? null : User.Type.valueOf(v.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.openmeetings.db.entity.IDataProviderEntity;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* General interface to perform CRUD operations on entities
|
||||
*
|
||||
* @author swagner
|
||||
*
|
||||
* @param <T> entity type for this provider
|
||||
*/
|
||||
@Transactional
|
||||
public interface IDataProviderDao<T extends IDataProviderEntity> {
|
||||
/**
|
||||
* Get an instance of an {@link T}
|
||||
*
|
||||
* @param id - id of instance to retrieve
|
||||
* @return instance with the id gived
|
||||
*/
|
||||
T get(Long id);
|
||||
|
||||
default T get(long id) {
|
||||
return get(Long.valueOf(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of instances of {@link T}
|
||||
*
|
||||
* @param start - the start to range to retrieve
|
||||
* @param count - maximum instance count to retrieve
|
||||
* @return list of instances in the range specified
|
||||
*/
|
||||
List<T> get(long start, long count);
|
||||
|
||||
/**
|
||||
* Get a list of instances of {@link T}
|
||||
*
|
||||
* @param search - string search criteria to filter entities
|
||||
* @param start - the start to range to retrieve
|
||||
* @param count - maximum instance count to retrieve
|
||||
* @param order - column and sort order
|
||||
* @return list of instances in the range specified
|
||||
*/
|
||||
List<T> get(String search, long start, long count, SortParam<String> order);
|
||||
|
||||
/**
|
||||
* Count the number of instances of {@link T}
|
||||
*
|
||||
* @return count of instances
|
||||
*/
|
||||
long count();
|
||||
|
||||
/**
|
||||
* Count the number of instances of {@link T}
|
||||
*
|
||||
* @param search - string search criteria to filter entities
|
||||
* @return count of instances satisfying given search criteria
|
||||
*/
|
||||
long count(String search);
|
||||
|
||||
/**
|
||||
* Update an instance of {@link T}
|
||||
*
|
||||
* @param entity - entity to be updated
|
||||
* @param userId - user performed update
|
||||
* @return - updated entity
|
||||
*/
|
||||
T update(T entity, Long userId);
|
||||
|
||||
/**
|
||||
* Delete an instance of {@link T}
|
||||
*
|
||||
* @param entity - entity to be deleted
|
||||
* @param userId - user performed delete
|
||||
*/
|
||||
void delete(T entity, Long userId);
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.openmeetings.db.entity.IDataProviderEntity;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
|
||||
public interface IGroupAdminDataProviderDao<T extends IDataProviderEntity> extends IDataProviderDao<T> {
|
||||
/**
|
||||
* Get a list of instances of {@link T}
|
||||
*
|
||||
* @param search - string search criteria to filter entities
|
||||
* @param adminId - id of group admin user
|
||||
* @param start - the start to range to retrieve
|
||||
* @param count - maximum instance count to retrieve
|
||||
* @param order - column and sort order
|
||||
* @return list of instances in the range specified
|
||||
*/
|
||||
List<T> adminGet(String search, Long adminId, long start, long count, SortParam<String> order);
|
||||
|
||||
/**
|
||||
* Get a list of instances of {@link T}
|
||||
*
|
||||
* @param search - string search criteria to filter entities
|
||||
* @param start - the start to range to retrieve
|
||||
* @param count - maximum instance count to retrieve
|
||||
* @param order - column and sort order
|
||||
* @return list of instances in the range specified
|
||||
*/
|
||||
default List<T> adminGet(String search, long start, long count, SortParam<String> order) {
|
||||
return get(search, start, count, order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of instances of {@link T}
|
||||
*
|
||||
* @param search - string search criteria to filter entities
|
||||
* @param adminId - id of group admin user
|
||||
* @return count of instances satisfying given search criteria
|
||||
*/
|
||||
long adminCount(String search, Long adminId);
|
||||
|
||||
/**
|
||||
* Count the number of instances of {@link T}
|
||||
*
|
||||
* @param search - string search criteria to filter entities
|
||||
* @return count of instances satisfying given search criteria
|
||||
*/
|
||||
default long adminCount(String search) {
|
||||
return count(search);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.basic;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_USER_ID;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.apache.openmeetings.db.entity.basic.ChatMessage;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class ChatDao {
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
public ChatMessage get(long id) {
|
||||
return em.createNamedQuery("getChatMessageById", ChatMessage.class)
|
||||
.setParameter("id", id)
|
||||
.getSingleResult();
|
||||
}
|
||||
|
||||
//for export
|
||||
public List<ChatMessage> get(long start, long count) {
|
||||
return setLimits(em.createNamedQuery("getChatMessages", ChatMessage.class)
|
||||
, start, count).getResultList();
|
||||
}
|
||||
|
||||
public List<ChatMessage> getGlobal(long start, long count) {
|
||||
return setLimits(em.createNamedQuery("getGlobalChatMessages", ChatMessage.class)
|
||||
, start, count).getResultList();
|
||||
}
|
||||
|
||||
public List<ChatMessage> getRoom(long roomId, long start, long count, boolean all) {
|
||||
return setLimits(em.createNamedQuery("getChatMessagesByRoom", ChatMessage.class)
|
||||
.setParameter("roomId", roomId)
|
||||
.setParameter("all", all)
|
||||
, start, count).getResultList();
|
||||
}
|
||||
|
||||
public List<ChatMessage> getUser(long userId, long start, long count) {
|
||||
return setLimits(em.createNamedQuery("getChatMessagesByUser", ChatMessage.class)
|
||||
.setParameter(PARAM_USER_ID, userId)
|
||||
, start, count).getResultList();
|
||||
}
|
||||
|
||||
public List<ChatMessage> getUserRecent(long userId, Date date, long start, long count) {
|
||||
return setLimits(em.createNamedQuery("getChatMessagesByUserTime", ChatMessage.class)
|
||||
.setParameter(PARAM_USER_ID, userId)
|
||||
.setParameter("status", ChatMessage.Status.CLOSED)
|
||||
.setParameter("date", date)
|
||||
, start, count).getResultList();
|
||||
}
|
||||
|
||||
public void closeMessages(long userId) {
|
||||
em.createNamedQuery("chatCloseMessagesByUser")
|
||||
.setParameter(PARAM_USER_ID, userId)
|
||||
.setParameter("status", ChatMessage.Status.CLOSED)
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
public ChatMessage update(ChatMessage entity) {
|
||||
return update(entity, null);
|
||||
}
|
||||
|
||||
public ChatMessage update(ChatMessage entity, Date sent) {
|
||||
entity.setSent(sent == null ? new Date() : sent);
|
||||
if (entity.getId() == null) {
|
||||
em.persist(entity);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param entity - unused
|
||||
* @param userId - unused
|
||||
*/
|
||||
public void delete(ChatMessage entity, long userId) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public void deleteGlobal() {
|
||||
em.createNamedQuery("deleteChatGlobal").executeUpdate();
|
||||
}
|
||||
|
||||
public void deleteRoom(Long roomId) {
|
||||
em.createNamedQuery("deleteChatRoom").setParameter("roomId", roomId).executeUpdate();
|
||||
}
|
||||
|
||||
public void deleteUser(Long userId) {
|
||||
em.createNamedQuery("deleteChatUser").setParameter(PARAM_USER_ID, userId).executeUpdate();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,614 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.basic;
|
||||
|
||||
import static org.apache.commons.lang3.math.NumberUtils.toInt;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
|
||||
import static org.apache.openmeetings.util.OmVersion.getLine;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.*;
|
||||
import static org.apache.wicket.csp.CSPDirectiveSrcValue.SELF;
|
||||
import static org.apache.wicket.csp.CSPDirectiveSrcValue.STRICT_DYNAMIC;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.apache.openjpa.conf.OpenJPAConfiguration;
|
||||
import org.apache.openjpa.event.RemoteCommitProvider;
|
||||
import org.apache.openjpa.event.TCPRemoteCommitProvider;
|
||||
import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI;
|
||||
import org.apache.openjpa.persistence.OpenJPAPersistence;
|
||||
import org.apache.openmeetings.IApplication;
|
||||
import org.apache.openmeetings.db.dao.IDataProviderDao;
|
||||
import org.apache.openmeetings.db.dao.server.OAuth2Dao;
|
||||
import org.apache.openmeetings.db.dao.user.UserDao;
|
||||
import org.apache.openmeetings.db.entity.basic.Configuration;
|
||||
import org.apache.openmeetings.db.util.DaoHelper;
|
||||
import org.apache.openmeetings.util.crypt.CryptProvider;
|
||||
import org.apache.wicket.Application;
|
||||
import org.apache.wicket.csp.CSPDirective;
|
||||
import org.apache.wicket.csp.CSPHeaderConfiguration;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.apache.wicket.protocol.http.WebApplication;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.github.openjson.JSONObject;
|
||||
|
||||
/**
|
||||
* Insert/update/Delete on {@link Configuration}
|
||||
*
|
||||
* It provides basic mechanism to get a Conf Key:
|
||||
* {@link #getBool(String, boolean)}
|
||||
* {@link #getLong(String, Long)}
|
||||
* {@link #getInt(String, int)}
|
||||
* {@link #getString(String, String)}
|
||||
*
|
||||
* <b> {@link #get(String)} is deprecated!</b>
|
||||
*
|
||||
* @author swagner
|
||||
*
|
||||
*/
|
||||
@Repository
|
||||
@Transactional
|
||||
public class ConfigurationDao implements IDataProviderDao<Configuration> {
|
||||
private static final Logger log = LoggerFactory.getLogger(ConfigurationDao.class);
|
||||
private static final List<String> searchFields = List.of("key", "value");
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
@Autowired
|
||||
private OAuth2Dao oauthDao;
|
||||
@Autowired
|
||||
private IApplication app;
|
||||
|
||||
public void updateClusterAddresses(String addresses) throws UnknownHostException {
|
||||
OpenJPAConfiguration cfg = ((OpenJPAEntityManagerSPI)OpenJPAPersistence.cast(em)).getConfiguration();
|
||||
RemoteCommitProvider prov = cfg.getRemoteCommitEventManager().getRemoteCommitProvider();
|
||||
if (prov instanceof TCPRemoteCommitProvider tcpProv) {
|
||||
tcpProv.setAddresses(addresses);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves Configuration regardless of its deleted status
|
||||
*
|
||||
* @param key - key of the {@link Configuration} to get
|
||||
* @return correspondent {@link Configuration} or null
|
||||
*/
|
||||
public Configuration forceGet(String key) {
|
||||
try {
|
||||
List<Configuration> list = em.createNamedQuery("forceGetConfigurationByKey", Configuration.class)
|
||||
.setParameter("key", key).getResultList();
|
||||
if (list.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Configuration c = list.get(0);
|
||||
return c.getKey().equals(key) ? c : null;
|
||||
} catch (Exception e) {
|
||||
log.error("[forceGet]: ", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Configuration> get(String... keys) {
|
||||
List<Configuration> result = new ArrayList<>();
|
||||
for (String key : keys) { //iteration is necessary to fill list with all values
|
||||
List<Configuration> r = em.createNamedQuery("getConfigurationsByKeys", Configuration.class)
|
||||
.setParameter("keys", List.of(key))
|
||||
.getResultList();
|
||||
result.add(r.isEmpty() ? null : r.get(0));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Configuration get(String key) {
|
||||
List<Configuration> list = get(new String[] {key});
|
||||
|
||||
if (list == null || list.isEmpty() || list.get(0) == null) {
|
||||
log.warn("Could not find key in configurations: {}", key);
|
||||
return null;
|
||||
}
|
||||
return list.get(0);
|
||||
}
|
||||
|
||||
public boolean getBool(String key, boolean def) {
|
||||
Configuration c = get(key);
|
||||
|
||||
if (c != null) {
|
||||
try {
|
||||
return c.getValueB();
|
||||
} catch (Exception e) {
|
||||
//no-op, parsing exception
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
public Long getLong(String key, Long def) {
|
||||
Configuration c = get(key);
|
||||
|
||||
if (c != null) {
|
||||
try {
|
||||
return c.getValueN();
|
||||
} catch (Exception e) {
|
||||
//no-op, parsing exception
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
public int getInt(String key, int def) {
|
||||
Configuration c = get(key);
|
||||
|
||||
if (c != null) {
|
||||
try {
|
||||
Long val = c.getValueN();
|
||||
return val == null ? def : val.intValue();
|
||||
} catch (Exception e) {
|
||||
//no-op, parsing exception
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
public String getString(String key, String def) {
|
||||
Configuration c = get(key);
|
||||
return c != null && c.getValue() != null ? c.getValue() : def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration get(Long id) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
return em.createNamedQuery("getConfigurationById", Configuration.class)
|
||||
.setParameter("id", id).getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Configuration> get(long start, long count) {
|
||||
return setLimits(em.createNamedQuery("getNondeletedConfiguration", Configuration.class)
|
||||
, start, count).getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Configuration> get(String search, long start, long count, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, Configuration.class, true, search, searchFields, false, null, sort, start, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return em.createNamedQuery("countConfigurations", Long.class).getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String search) {
|
||||
return DaoHelper.count(em, Configuration.class, search, searchFields, true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration update(Configuration entity, Long userId) {
|
||||
return update(entity, userId, false);
|
||||
}
|
||||
|
||||
public Configuration update(Configuration entity, Long userId, boolean deleted) {
|
||||
String key = entity.getKey();
|
||||
String value = entity.getValue();
|
||||
if (entity.getId() == null || entity.getId().longValue() <= 0) {
|
||||
entity.setDeleted(deleted);
|
||||
em.persist(entity);
|
||||
} else {
|
||||
entity.setUser(userDao.get(userId));
|
||||
entity.setDeleted(deleted);
|
||||
entity = em.merge(entity);
|
||||
}
|
||||
switch (key) {
|
||||
case CONFIG_CAM_FPS, CONFIG_MIC_ECHO, CONFIG_MIC_NOISE, CONFIG_MIC_RATE, CONFIG_KEYCODE_ARRANGE
|
||||
, CONFIG_KEYCODE_MUTE_OTHERS, CONFIG_KEYCODE_MUTE, CONFIG_KEYCODE_QUICKPOLL
|
||||
, CONFIG_KEYCODE_ARRANGE_RESIZE, CONFIG_AUTO_OPEN_SHARING:
|
||||
reloadRoomSettings();
|
||||
break;
|
||||
case CONFIG_MAX_UPLOAD_SIZE:
|
||||
reloadMaxUpload();
|
||||
break;
|
||||
case CONFIG_CRYPT:
|
||||
reloadCrypt();
|
||||
break;
|
||||
case CONFIG_APPLICATION_NAME:
|
||||
setApplicationName(value);
|
||||
break;
|
||||
case CONFIG_APPLICATION_BASE_URL:
|
||||
reloadBaseUrl();
|
||||
break;
|
||||
case CONFIG_SIP_ENABLED:
|
||||
reloadSipEnabled();
|
||||
break;
|
||||
case CONFIG_EXT_PROCESS_TTL:
|
||||
setExtProcessTtl(toInt(value));
|
||||
break;
|
||||
case CONFIG_DEFAULT_LANG:
|
||||
reloadDefaultLang();
|
||||
break;
|
||||
case CONFIG_MP4_AUDIO_RATE:
|
||||
reloadAudioRate();
|
||||
break;
|
||||
case CONFIG_MP4_AUDIO_BITRATE:
|
||||
reloadAudioBitrate();
|
||||
break;
|
||||
case CONFIG_MP4_VIDEO_PRESET:
|
||||
reloadVideoPreset();
|
||||
break;
|
||||
case CONFIG_DEFAULT_TIMEZONE:
|
||||
reloadTimezone();
|
||||
break;
|
||||
case CONFIG_REST_ALLOW_ORIGIN:
|
||||
reloadRestAllowOrigin();
|
||||
break;
|
||||
case CONFIG_LOGIN_MIN_LENGTH, CONFIG_PASS_MIN_LENGTH, CONFIG_PASS_CHECK_UPPER
|
||||
, CONFIG_PASS_CHECK_DIGIT, CONFIG_PASS_CHECK_SPECIAL:
|
||||
reloadLoginPassword();
|
||||
break;
|
||||
case CONFIG_DEFAULT_GROUP_ID:
|
||||
reloadDefaultGroup();
|
||||
break;
|
||||
case CONFIG_SIP_EXTEN_CONTEXT:
|
||||
reloadSipContext();
|
||||
break;
|
||||
case CONFIG_FNAME_MIN_LENGTH:
|
||||
reloadFnameMinLength();
|
||||
break;
|
||||
case CONFIG_LNAME_MIN_LENGTH:
|
||||
reloadLnameMinLength();
|
||||
break;
|
||||
case CONFIG_CHAT_SEND_ON_ENTER:
|
||||
reloadChatSendOnEnter();
|
||||
break;
|
||||
case CONFIG_REGISTER_FRONTEND, CONFIG_REGISTER_SOAP, CONFIG_REGISTER_OAUTH:
|
||||
reloadRegister();
|
||||
break;
|
||||
case CONFIG_EMAIL_VERIFICATION:
|
||||
reloadSendVerificationEmail();
|
||||
break;
|
||||
case CONFIG_EMAIL_AT_REGISTER:
|
||||
reloadSendRegisterEmail();
|
||||
break;
|
||||
case CONFIG_DISPLAY_NAME_EDITABLE:
|
||||
reloadDisplayNameEditable();
|
||||
break;
|
||||
case CONFIG_MYROOMS_ENABLED:
|
||||
reloadMyRoomsEnabled();
|
||||
break;
|
||||
case CONFIG_GOOGLE_ANALYTICS_CODE, CONFIG_CSP_FONT, CONFIG_CSP_FRAME, CONFIG_CSP_IMAGE
|
||||
, CONFIG_CSP_MEDIA, CONFIG_CSP_SCRIPT, CONFIG_CSP_STYLE, CONFIG_CSP_ENABLED:
|
||||
updateCsp();
|
||||
break;
|
||||
case CONFIG_SMTP_SERVER, CONFIG_SMTP_PORT, CONFIG_SMTP_SYSTEM_EMAIL, CONFIG_SMTP_USER
|
||||
, CONFIG_SMTP_PASS, CONFIG_SMTP_TLS, CONFIG_SMTP_SSL, CONFIG_REPLY_TO_ORGANIZER
|
||||
, CONFIG_SMTP_TIMEOUT_CON, CONFIG_SMTP_TIMEOUT:
|
||||
reloadMailSettings();
|
||||
break;
|
||||
case CONFIG_APPOINTMENT_REMINDER_MINUTES, CONFIG_APPOINTMENT_PRE_START_MINUTES:
|
||||
reloadAppointmentSettings();
|
||||
break;
|
||||
case CONFIG_RECORDING_ENABLED:
|
||||
reloadRecordingEnabled();
|
||||
break;
|
||||
case CONFIG_THEME:
|
||||
reloadTheme();
|
||||
break;
|
||||
case CONFIG_OTP_ENABLED:
|
||||
reloadOtpEnabled();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Configuration entity, Long userId) {
|
||||
this.update(entity, userId, true);
|
||||
}
|
||||
|
||||
private void reloadMaxUpload() {
|
||||
try {
|
||||
setMaxUploadSize(getLong(CONFIG_MAX_UPLOAD_SIZE, DEFAULT_MAX_UPLOAD_SIZE));
|
||||
} catch (Exception e) {
|
||||
log.error("Invalid value saved for max_upload_size conf key: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadCrypt() {
|
||||
String cryptClass = getString(CONFIG_CRYPT, null);
|
||||
if (cryptClass != null) {
|
||||
setCryptClassName(cryptClass);
|
||||
CryptProvider.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadBaseUrl() {
|
||||
String val = getString(CONFIG_APPLICATION_BASE_URL, DEFAULT_BASE_URL);
|
||||
if (val != null && !val.endsWith("/")) {
|
||||
val += "/";
|
||||
}
|
||||
setBaseUrl(val);
|
||||
}
|
||||
|
||||
private void reloadSipEnabled() {
|
||||
setSipEnabled(getBool(CONFIG_SIP_ENABLED, false));
|
||||
}
|
||||
|
||||
private void reloadDefaultLang() {
|
||||
setDefaultLang(getLong(CONFIG_DEFAULT_LANG, 1L));
|
||||
}
|
||||
|
||||
private void reloadAudioRate() {
|
||||
setAudioRate(getInt(CONFIG_MP4_AUDIO_RATE, 22050));
|
||||
}
|
||||
|
||||
private void reloadAudioBitrate() {
|
||||
setAudioBitrate(getString(CONFIG_MP4_AUDIO_BITRATE, "32k"));
|
||||
}
|
||||
|
||||
private void reloadVideoPreset() {
|
||||
setVideoPreset(getString(CONFIG_MP4_VIDEO_PRESET, "medium"));
|
||||
}
|
||||
|
||||
private void reloadTimezone() {
|
||||
String defaultTzName = getString(CONFIG_DEFAULT_TIMEZONE, "Europe/Berlin");
|
||||
|
||||
TimeZone timeZoneByOmTimeZone = TimeZone.getTimeZone(defaultTzName);
|
||||
|
||||
if (timeZoneByOmTimeZone == null) { //this seems to be impossible
|
||||
// If everything fails take the servers default one
|
||||
log.error("There is no correct time zone set in the configuration of OpenMeetings for the key default.timezone or key is missing in table, using default locale!");
|
||||
defaultTzName = TimeZone.getDefault().getID();
|
||||
}
|
||||
setDefaultTimezone(defaultTzName);
|
||||
}
|
||||
|
||||
private void reloadRestAllowOrigin() {
|
||||
setRestAllowOrigin(getString(CONFIG_REST_ALLOW_ORIGIN, null));
|
||||
}
|
||||
|
||||
private void reloadLoginPassword() {
|
||||
setMinLoginLength(getInt(CONFIG_LOGIN_MIN_LENGTH, USER_LOGIN_MINIMUM_LENGTH));
|
||||
setMinPasswdLength(getInt(CONFIG_LOGIN_MIN_LENGTH, USER_PASSWORD_MINIMUM_LENGTH));
|
||||
setPwdCheckUpper(getBool(CONFIG_PASS_CHECK_UPPER, true));
|
||||
setPwdCheckDigit(getBool(CONFIG_PASS_CHECK_DIGIT, true));
|
||||
setPwdCheckSpecial(getBool(CONFIG_PASS_CHECK_SPECIAL, true));
|
||||
}
|
||||
|
||||
private void reloadDefaultGroup() {
|
||||
setDefaultGroup(getLong(CONFIG_DEFAULT_GROUP_ID, null));
|
||||
}
|
||||
|
||||
private void reloadSipContext() {
|
||||
setSipContext(getString(CONFIG_SIP_EXTEN_CONTEXT, DEFAULT_SIP_CONTEXT));
|
||||
}
|
||||
|
||||
private void reloadFnameMinLength() {
|
||||
setMinFnameLength(getInt(CONFIG_FNAME_MIN_LENGTH, USER_LOGIN_MINIMUM_LENGTH));
|
||||
}
|
||||
|
||||
private void reloadLnameMinLength() {
|
||||
setMinLnameLength(getInt(CONFIG_LNAME_MIN_LENGTH, USER_LOGIN_MINIMUM_LENGTH));
|
||||
}
|
||||
|
||||
private void reloadChatSendOnEnter() {
|
||||
setChatSendOnEnter(getBool(CONFIG_CHAT_SEND_ON_ENTER, false));
|
||||
}
|
||||
|
||||
private void reloadRegister() {
|
||||
setAllowRegisterFrontend(getBool(CONFIG_REGISTER_FRONTEND, false));
|
||||
setAllowRegisterSoap(getBool(CONFIG_REGISTER_SOAP, false));
|
||||
setAllowRegisterOauth(getBool(CONFIG_REGISTER_OAUTH, false));
|
||||
}
|
||||
|
||||
private void reloadSendVerificationEmail() {
|
||||
setSendVerificationEmail(getBool(CONFIG_EMAIL_VERIFICATION, false));
|
||||
}
|
||||
|
||||
private void reloadSendRegisterEmail() {
|
||||
setSendRegisterEmail(getBool(CONFIG_EMAIL_AT_REGISTER, false));
|
||||
}
|
||||
|
||||
private void reloadDisplayNameEditable() {
|
||||
setDisplayNameEditable(getBool(CONFIG_DISPLAY_NAME_EDITABLE, false));
|
||||
}
|
||||
|
||||
private void reloadMyRoomsEnabled() {
|
||||
setMyRoomsEnabled(getBool(CONFIG_MYROOMS_ENABLED, true));
|
||||
}
|
||||
|
||||
private void reloadMailSettings() {
|
||||
setSmtpServer(getString(CONFIG_SMTP_SERVER, null));
|
||||
setSmtpPort(getInt(CONFIG_SMTP_PORT, 25));
|
||||
setSmtpUser(getString(CONFIG_SMTP_USER, null));
|
||||
setSmtpPass(getString(CONFIG_SMTP_PASS, null));
|
||||
setSmtpUseTls(getBool(CONFIG_SMTP_TLS, false));
|
||||
setSmtpUseSsl(getBool(CONFIG_SMTP_SSL, false));
|
||||
setSmtpTimeOut(getInt(CONFIG_SMTP_TIMEOUT, 30000));
|
||||
setSmtpConnectionTimeOut(getInt(CONFIG_SMTP_TIMEOUT_CON, 30000));
|
||||
setMailFrom(getString(CONFIG_SMTP_SYSTEM_EMAIL, null));
|
||||
setMailAddReplyTo(getBool(CONFIG_REPLY_TO_ORGANIZER, true));
|
||||
}
|
||||
|
||||
private void reloadAppointmentSettings() {
|
||||
setAppointmentPreStartMinutes(getInt(CONFIG_APPOINTMENT_PRE_START_MINUTES, 5));
|
||||
setAppointmentReminderMinutes(getInt(CONFIG_APPOINTMENT_REMINDER_MINUTES, 15));
|
||||
}
|
||||
|
||||
private void reloadRecordingEnabled() {
|
||||
setRecordingsEnabled(getBool(CONFIG_RECORDING_ENABLED, true));
|
||||
}
|
||||
|
||||
private void reloadTheme() {
|
||||
setTheme(getString(CONFIG_THEME, ""));
|
||||
app.updateTheme();
|
||||
}
|
||||
|
||||
private void reloadOtpEnabled() {
|
||||
setOtpEnabled(getBool(CONFIG_OTP_ENABLED, false));
|
||||
}
|
||||
|
||||
public void reinit() {
|
||||
reloadMaxUpload();
|
||||
reloadCrypt();
|
||||
setApplicationName(getString(CONFIG_APPLICATION_NAME, DEFAULT_APP_NAME));
|
||||
reloadDefaultLang();
|
||||
reloadBaseUrl();
|
||||
reloadSipEnabled();
|
||||
reloadAudioRate();
|
||||
reloadAudioBitrate();
|
||||
reloadVideoPreset();
|
||||
reloadTimezone();
|
||||
reloadRestAllowOrigin();
|
||||
reloadRoomSettings();
|
||||
reloadLoginPassword();
|
||||
reloadDefaultGroup();
|
||||
reloadSipContext();
|
||||
reloadFnameMinLength();
|
||||
reloadLnameMinLength();
|
||||
reloadChatSendOnEnter();
|
||||
reloadRegister();
|
||||
reloadSendVerificationEmail();
|
||||
reloadSendRegisterEmail();
|
||||
reloadDisplayNameEditable();
|
||||
reloadMyRoomsEnabled();
|
||||
reloadMailSettings();
|
||||
reloadAppointmentSettings();
|
||||
reloadRecordingEnabled();
|
||||
reloadTheme();
|
||||
reloadOtpEnabled();
|
||||
|
||||
updateCsp();
|
||||
}
|
||||
|
||||
private static JSONObject getHotkey(String value) {
|
||||
List<String> partList = List.of(value.split("\\+"));
|
||||
Set<String> parts = new HashSet<>(partList);
|
||||
return new JSONObject()
|
||||
.put("alt", parts.contains("Alt"))
|
||||
.put("shift", parts.contains("Shift"))
|
||||
.put("ctrl", parts.contains("Ctrl"))
|
||||
.put("code", partList.get(partList.size() - 1));
|
||||
}
|
||||
|
||||
private JSONObject reloadRoomSettings() {
|
||||
try {
|
||||
setRoomSettings(new JSONObject()
|
||||
.put("keycode", new JSONObject()
|
||||
.put("arrange", getHotkey(getString(CONFIG_KEYCODE_ARRANGE, "Shift+F8")))
|
||||
.put("arrangeresize", getHotkey(getString(CONFIG_KEYCODE_ARRANGE_RESIZE, "Ctrl+Shift+KeyA")))
|
||||
.put("muteothers", getHotkey(getString(CONFIG_KEYCODE_MUTE_OTHERS, "Shift+F12")))
|
||||
.put("mute", getHotkey(getString(CONFIG_KEYCODE_MUTE, "Shift+F7")))
|
||||
.put("quickpoll", getHotkey(getString(CONFIG_KEYCODE_QUICKPOLL, "Ctrl+Alt+KeyQ")))
|
||||
)
|
||||
.put("camera", new JSONObject().put("fps", getLong(CONFIG_CAM_FPS, 30L)))
|
||||
.put("microphone", new JSONObject()
|
||||
.put("rate", getLong(CONFIG_MIC_RATE, 30L))
|
||||
.put("echo", getBool(CONFIG_MIC_ECHO, true))
|
||||
.put("noise", getBool(CONFIG_MIC_NOISE, true))
|
||||
)
|
||||
.put("autoOpenSharing", getBool(CONFIG_AUTO_OPEN_SHARING, false))
|
||||
);
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected exception while reloading room settings: ", e);
|
||||
}
|
||||
return getRoomSettings();
|
||||
}
|
||||
|
||||
private void addCspRule(CSPHeaderConfiguration cspConfig, CSPDirective key, String val) {
|
||||
addCspRule(cspConfig, key, val, true);
|
||||
}
|
||||
|
||||
private void addCspRule(CSPHeaderConfiguration cspConfig, CSPDirective key, String val, boolean remove) {
|
||||
if (!Strings.isEmpty(val)) {
|
||||
for(String str : val.split(",")) {
|
||||
if (!Strings.isEmpty(str)) {
|
||||
try {
|
||||
cspConfig.add(key, str.trim());
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected error while adding CSP rule: key '{}', value '{}', part '{}'", key, val, str, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (remove) {
|
||||
cspConfig.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateCsp() {
|
||||
setGaCode(getString(CONFIG_GOOGLE_ANALYTICS_CODE, null));
|
||||
|
||||
if (!getBool(CONFIG_CSP_ENABLED, true)) {
|
||||
StringBuilder sb = new StringBuilder("\n");
|
||||
getLine(sb, "", '#');
|
||||
getLine(sb, "CSP headers are DISABLED", ' ');
|
||||
getLine(sb, "Disabling CSP can lead to XSS attacks! Use this mode only if you must!", ' ');
|
||||
getLine(sb, "", '#');
|
||||
log.warn("{}", sb);
|
||||
WebApplication.get().getCspSettings().blocking().disabled();
|
||||
return;
|
||||
}
|
||||
|
||||
setCspFontSrc(getString(CONFIG_CSP_FONT, DEFAULT_CSP_FONT));
|
||||
setCspFrameSrc(getString(CONFIG_CSP_FRAME, SELF.getValue()));
|
||||
setCspImageSrc(getString(CONFIG_CSP_IMAGE, DEFAULT_CSP_DATA));
|
||||
setCspMediaSrc(getString(CONFIG_CSP_MEDIA, DEFAULT_CSP_DATA));
|
||||
setCspScriptSrc(getString(CONFIG_CSP_SCRIPT, STRICT_DYNAMIC.getValue()));
|
||||
setCspStyleSrc(getString(CONFIG_CSP_STYLE, DEFAULT_CSP_STYLE));
|
||||
if (Application.exists()) {
|
||||
final CSPHeaderConfiguration cspConfig = WebApplication.get().getCspSettings().blocking().strict();
|
||||
addCspRule(cspConfig, CSPDirective.FONT_SRC, getCspFontSrc());
|
||||
addCspRule(cspConfig, CSPDirective.FRAME_SRC, getCspFrameSrc());
|
||||
addCspRule(cspConfig, CSPDirective.IMG_SRC, getCspImageSrc());
|
||||
addCspRule(cspConfig, CSPDirective.MEDIA_SRC, getCspMediaSrc());
|
||||
addCspRule(cspConfig, CSPDirective.SCRIPT_SRC, getCspScriptSrc());
|
||||
addCspRule(cspConfig, CSPDirective.STYLE_SRC, getCspStyleSrc());
|
||||
app.getWsUrls().forEach(wsUrl -> addCspRule(cspConfig, CSPDirective.CONNECT_SRC, wsUrl, false)); // special code for Safari browser
|
||||
if (!Strings.isEmpty(getGaCode())) {
|
||||
// https://developers.google.com/tag-manager/web/csp#universal_analytics_google_analytics
|
||||
addCspRule(cspConfig, CSPDirective.IMG_SRC, "https://www.google-analytics.com");
|
||||
addCspRule(cspConfig, CSPDirective.CONNECT_SRC, "https://www.google-analytics.com");
|
||||
addCspRule(cspConfig, CSPDirective.SCRIPT_SRC, "https://www.googletagmanager.com");
|
||||
}
|
||||
oauthDao.getActive().forEach(oauth -> {
|
||||
if (!Strings.isEmpty(oauth.getIconUrl())) {
|
||||
addCspRule(cspConfig, CSPDirective.IMG_SRC, oauth.getIconUrl(), false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.basic;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_STATUS;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.apache.openmeetings.db.dao.IDataProviderDao;
|
||||
import org.apache.openmeetings.db.entity.basic.MailMessage;
|
||||
import org.apache.openmeetings.db.entity.basic.MailMessage.Status;
|
||||
import org.apache.openmeetings.db.util.DaoHelper;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class MailMessageDao implements IDataProviderDao<MailMessage> {
|
||||
private static final List<String> searchFields = List.of("recipients", "subject", "body", "lastError");
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Override
|
||||
public MailMessage get(Long id) {
|
||||
return em.createNamedQuery("getMailMessageById", MailMessage.class).setParameter("id", id).getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MailMessage> get(long start, long count) {
|
||||
return setLimits(em.createNamedQuery("getMailMessages", MailMessage.class)
|
||||
, start, count).getResultList();
|
||||
}
|
||||
|
||||
public List<MailMessage> get(long start, long count, Status status) {
|
||||
return setLimits(em.createNamedQuery("getMailMessagesByStatus", MailMessage.class).setParameter(PARAM_STATUS, status)
|
||||
, start, count).getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MailMessage> get(String search, long start, long count, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, MailMessage.class, false, search, searchFields, false, null, sort, start, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return em.createNamedQuery("countMailMessages", Long.class).getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String search) {
|
||||
return DaoHelper.count(em, MailMessage.class, search, searchFields, false, null);
|
||||
}
|
||||
|
||||
public void resetSendingStatus(Calendar date) {
|
||||
em.createNamedQuery("resetMailStatusByDate")
|
||||
.setParameter("noneStatus", Status.NONE)
|
||||
.setParameter("sendingStatus", Status.SENDING)
|
||||
.setParameter("date", date)
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
public void resetSendingStatus(Long id) {
|
||||
em.createNamedQuery("resetMailStatusById")
|
||||
.setParameter("noneStatus", Status.NONE)
|
||||
.setParameter("id", id)
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MailMessage update(MailMessage m, Long userId) {
|
||||
if (m.getId() == null) {
|
||||
em.persist(m);
|
||||
} else {
|
||||
m = em.merge(m);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(MailMessage m, Long userId) {
|
||||
if (m != null) {
|
||||
delete(m.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id - entity id
|
||||
*/
|
||||
public void delete(Long id) {
|
||||
if (id != null) {
|
||||
em.remove(get(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.calendar;
|
||||
|
||||
import static java.util.UUID.randomUUID;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.only;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.UNSUPPORTED;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_CALENDAR_ROOM_CAPACITY;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_USER_ID;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.openmeetings.db.dao.IDataProviderDao;
|
||||
import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
|
||||
import org.apache.openmeetings.db.dao.room.RoomDao;
|
||||
import org.apache.openmeetings.db.dto.calendar.AppointmentDTO;
|
||||
import org.apache.openmeetings.db.entity.calendar.Appointment;
|
||||
import org.apache.openmeetings.db.entity.calendar.Appointment.Reminder;
|
||||
import org.apache.openmeetings.db.entity.calendar.MeetingMember;
|
||||
import org.apache.openmeetings.db.entity.room.Invitation.MessageType;
|
||||
import org.apache.openmeetings.db.entity.room.Room;
|
||||
import org.apache.openmeetings.db.manager.IInvitationManager;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class AppointmentDao implements IDataProviderDao<Appointment>{
|
||||
private static final Logger log = LoggerFactory.getLogger(AppointmentDao.class);
|
||||
private static final String PARAM_START = "start";
|
||||
private static final String PARAM_CALID = "calId";
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
@Autowired
|
||||
private MeetingMemberDao meetingMemberDao;
|
||||
@Autowired
|
||||
private RoomDao roomDao;
|
||||
@Autowired
|
||||
private ConfigurationDao cfgDao;
|
||||
@Autowired
|
||||
private IInvitationManager invitationManager;
|
||||
|
||||
/*
|
||||
* insert, update, delete, select
|
||||
*/
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
@Override
|
||||
public Appointment get(Long id) {
|
||||
return only(em.createNamedQuery("getAppointmentById", Appointment.class)
|
||||
.setParameter("id", id).getResultList());
|
||||
}
|
||||
|
||||
public Appointment getAny(Long id) {
|
||||
return only(em.createNamedQuery("getAppointmentByIdAny", Appointment.class)
|
||||
.setParameter("id", id).getResultList());
|
||||
}
|
||||
|
||||
public List<Appointment> get() {
|
||||
return em.createNamedQuery("getAppointments", Appointment.class).getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Appointment update(Appointment a, Long userId) {
|
||||
return update(a, userId, true);
|
||||
}
|
||||
|
||||
public Appointment update(Appointment a, Long userId, boolean sendmails) {
|
||||
Room r = a.getRoom();
|
||||
if (r.getId() == null) {
|
||||
r.setName(a.getTitle());
|
||||
r.setCapacity(cfgDao.getLong(CONFIG_CALENDAR_ROOM_CAPACITY, 50L));
|
||||
}
|
||||
a.setRoom(roomDao.update(r, userId));
|
||||
final boolean newApp = a.getId() == null;
|
||||
AppointmentDTO a0 = null;
|
||||
Set<Long> mmIds = Set.of();
|
||||
if (sendmails && !newApp) {
|
||||
Appointment prev = get(a.getId());
|
||||
if (prev != null) {
|
||||
a0 = new AppointmentDTO(prev);
|
||||
}
|
||||
mmIds = meetingMemberDao.getMeetingMemberIdsByAppointment(a.getId());
|
||||
}
|
||||
if (newApp) {
|
||||
a.setIcalId(randomUUID().toString());
|
||||
em.persist(a);
|
||||
} else {
|
||||
a = em.merge(a);
|
||||
}
|
||||
if (sendmails) {
|
||||
// update meeting members
|
||||
boolean sendMail = a0 == null
|
||||
|| !StringUtils.equals(a0.getTitle(), a.getTitle())
|
||||
|| !StringUtils.equals(a0.getDescription(), a.getDescription())
|
||||
|| !StringUtils.equals(a0.getLocation(), a.getLocation())
|
||||
|| !a0.getStart().getTime().equals(a.getStart())
|
||||
|| !a0.getEnd().getTime().equals(a.getEnd());
|
||||
List<MeetingMember> mmList = a.getMeetingMembers();
|
||||
if (mmList != null){
|
||||
for (MeetingMember mm : mmList) {
|
||||
if (mm.getId() == null || !mmIds.contains(mm.getId())) {
|
||||
invitationManager.processInvitation(a, mm, MessageType.CREATE);
|
||||
} else {
|
||||
mmIds.remove(mm.getId());
|
||||
invitationManager.processInvitation(a, mm, MessageType.UPDATE, sendMail);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (long id : mmIds) {
|
||||
invitationManager.processInvitation(a, meetingMemberDao.get(id), MessageType.CANCEL);
|
||||
}
|
||||
//notify owner
|
||||
MeetingMember owner = new MeetingMember();
|
||||
owner.setUser(a.getOwner());
|
||||
if (newApp) {
|
||||
invitationManager.processInvitation(a, owner, MessageType.CREATE);
|
||||
} else if (a.isDeleted()) {
|
||||
invitationManager.processInvitation(a, owner, MessageType.CANCEL);
|
||||
} else if (sendMail) {
|
||||
invitationManager.processInvitation(a, owner, MessageType.UPDATE, sendMail);
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void delete(Appointment a, Long userId) {
|
||||
if (a == null || a.getId() == null) {
|
||||
return;
|
||||
}
|
||||
a.setDeleted(true);
|
||||
a.setMeetingMembers(null);
|
||||
if (a.getRoom().isAppointment()) {
|
||||
a.getRoom().setDeleted(true);
|
||||
}
|
||||
update(a, userId);
|
||||
}
|
||||
|
||||
public List<Appointment> getInRange(Long userId, Date start, Date end) {
|
||||
log.debug("Start {} End {}", start, end);
|
||||
|
||||
TypedQuery<Appointment> query = em.createNamedQuery("appointmentsInRange", Appointment.class);
|
||||
query.setParameter(PARAM_START, start);
|
||||
query.setParameter("end", end);
|
||||
query.setParameter(PARAM_USER_ID, userId);
|
||||
|
||||
List<Appointment> listAppoints = new ArrayList<>(query.getResultList());
|
||||
TypedQuery<Appointment> q1 = em.createNamedQuery("joinedAppointmentsInRange", Appointment.class);
|
||||
q1.setParameter(PARAM_START, start);
|
||||
q1.setParameter("end", end);
|
||||
q1.setParameter(PARAM_USER_ID, userId);
|
||||
for (Appointment a : q1.getResultList()) {
|
||||
a.setConnectedEvent(true);
|
||||
listAppoints.add(a);
|
||||
}
|
||||
|
||||
return listAppoints;
|
||||
}
|
||||
|
||||
public List<Appointment> getInRange(Calendar start, Calendar end) {
|
||||
TypedQuery<Appointment> q = em.createNamedQuery("appointmentsInRangeRemind", Appointment.class);
|
||||
q.setParameter("none", Reminder.NONE);
|
||||
q.setParameter(PARAM_START, start.getTime());
|
||||
q.setParameter("end", end.getTime());
|
||||
return q.getResultList();
|
||||
}
|
||||
|
||||
// next appointment to select date
|
||||
public Appointment getNext(Long userId, Date start) {
|
||||
List<Appointment> list = em.createNamedQuery("getNextAppointment", Appointment.class)
|
||||
.setParameter(PARAM_START, start).setParameter(PARAM_USER_ID, userId).getResultList();
|
||||
return list == null || list.isEmpty() ? null : list.get(0);
|
||||
}
|
||||
|
||||
public List<Appointment> searchByTitle(Long userId, String title) {
|
||||
return em.createNamedQuery("getAppointmentsByTitle", Appointment.class)
|
||||
.setParameter("title", title).setParameter(PARAM_USER_ID, userId).getResultList();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
public Appointment getByRoom(Long userId, Long roomId) {
|
||||
try {
|
||||
List<Appointment> list = em.createNamedQuery("getAppointmentByOwnerRoomId", Appointment.class)
|
||||
.setParameter(PARAM_USER_ID, userId)
|
||||
.setParameter("roomId", roomId)
|
||||
.getResultList();
|
||||
|
||||
return list.isEmpty() ? null : list.get(0);
|
||||
} catch (Exception e) {
|
||||
log.error("[getByRoom]", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Appointment getByRoom(Long roomId) {
|
||||
List<Appointment> list = em.createNamedQuery("getAppointmentByRoomId", Appointment.class)
|
||||
.setParameter("roomId", roomId)
|
||||
.getResultList();
|
||||
|
||||
Appointment a = list.isEmpty() ? null : list.get(0);
|
||||
if (a != null && !a.getRoom().isAppointment()) {
|
||||
throw new RuntimeException("Room " + a.getRoom().getName() + " isnt part of an appointed meeting");
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
//Calendar Related Methods
|
||||
|
||||
/**
|
||||
* Returns the Appointment HREF's belonging to the Calendar Id specified.
|
||||
*
|
||||
* @param calId Calendar to which the Appointments are related to.
|
||||
* @return <code>List</code> of Appointment HREF's
|
||||
*/
|
||||
public List<String> getHrefsbyCalendar(Long calId) {
|
||||
return em.createNamedQuery("getHrefsforAppointmentsinCalendar", String.class)
|
||||
.setParameter(PARAM_CALID, calId)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Appointments related to the Calendar ID specified.
|
||||
*
|
||||
* @param calId Calendar ID of the calendar, to which the appointment is associated
|
||||
* @return <code>List</code> of <code>Appointment</code>
|
||||
*/
|
||||
public List<Appointment> getbyCalendar(Long calId) {
|
||||
return em.createNamedQuery("getAppointmentsbyCalendar", Appointment.class)
|
||||
.setParameter(PARAM_CALID, calId)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk Deletes the Appointments related the the calId.
|
||||
* Note: Does not automatically, commit, but gets cascaded in the function which calls it.
|
||||
* If there is a need to commit during this function, use <code>em.flush()</code> and <code>em.clear()</code>
|
||||
*
|
||||
* @param calId Calendar Id of the Calendar Id to which the Appointments belong to.
|
||||
* @return Returns <code>-1</code> if the there was an error executing the query,
|
||||
* otherwise returns the number of updated rows.
|
||||
* as described here {@link Query#executeUpdate()}
|
||||
*/
|
||||
public int deletebyCalendar(Long calId) {
|
||||
return em.createNamedQuery("deleteAppointmentsbyCalendar", Appointment.class)
|
||||
.setParameter(PARAM_CALID, calId)
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Appointment> get(long start, long count) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Appointment> get(String search, long start, long count, SortParam<String> order) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String search) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.calendar;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.only;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.apache.openmeetings.db.entity.calendar.MeetingMember;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class MeetingMemberDao {
|
||||
private static final Logger log = LoggerFactory.getLogger(MeetingMemberDao.class);
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
public MeetingMember get(Long id) {
|
||||
return only(em.createNamedQuery("getMeetingMemberById", MeetingMember.class)
|
||||
.setParameter("id", id).getResultList());
|
||||
}
|
||||
|
||||
public List<MeetingMember> get() {
|
||||
return em.createNamedQuery("getMeetingMembers", MeetingMember.class).getResultList();
|
||||
}
|
||||
|
||||
public Set<Long> getMeetingMemberIdsByAppointment(Long appointmentId) {
|
||||
log.debug("getMeetingMemberIdsByAppointment: {}", appointmentId);
|
||||
|
||||
return new HashSet<>(em.createNamedQuery("getMeetingMemberIdsByAppointment", Long.class)
|
||||
.setParameter("id", appointmentId)
|
||||
.getResultList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updating MeetingMember
|
||||
*
|
||||
* @param meetingMember - entity to update
|
||||
* @return - updated entity
|
||||
*/
|
||||
// -------------------------------------------------------------------------------
|
||||
public MeetingMember update(MeetingMember meetingMember) {
|
||||
if (meetingMember.getId() == null) {
|
||||
em.persist(meetingMember);
|
||||
} else {
|
||||
if (!em.contains(meetingMember)) {
|
||||
meetingMember = em.merge(meetingMember);
|
||||
}
|
||||
}
|
||||
return meetingMember;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.calendar;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.only;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.UNSUPPORTED;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_USER_ID;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.apache.openmeetings.db.dao.IDataProviderDao;
|
||||
import org.apache.openmeetings.db.entity.calendar.OmCalendar;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class OmCalendarDao implements IDataProviderDao<OmCalendar> {
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
@Autowired
|
||||
private AppointmentDao appointmentDao;
|
||||
|
||||
public List<OmCalendar> get() {
|
||||
return em.createNamedQuery("getCalendars", OmCalendar.class).getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Calendar Specified by the Calendar ID.
|
||||
*
|
||||
* @param calId Calendar ID of the Calendar to return.
|
||||
* @return Returns the Calendar if found, else returns null
|
||||
*/
|
||||
@Override
|
||||
public OmCalendar get(Long calId) {
|
||||
return only(em.createNamedQuery("getCalendarbyId", OmCalendar.class)
|
||||
.setParameter("calId", calId).getResultList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the Calendars that belong to the User.
|
||||
*
|
||||
* @param userId User ID to whom the calendars belong.
|
||||
* @return List of Calendars
|
||||
*/
|
||||
public List<OmCalendar> getByUser(Long userId) {
|
||||
return em.createNamedQuery("getCalendarbyUser", OmCalendar.class)
|
||||
.setParameter(PARAM_USER_ID, userId)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the Google Calendars associated with the user.
|
||||
*
|
||||
* @param userId User ID of the owner of the Calendar
|
||||
* @return List of Google Calendars.
|
||||
*/
|
||||
public List<OmCalendar> getGoogleCalendars(Long userId) {
|
||||
return em.createNamedQuery("getGoogleCalendars", OmCalendar.class)
|
||||
.setParameter(PARAM_USER_ID, userId)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or Updates the given calendar.
|
||||
*
|
||||
* @param c Calendar to Update
|
||||
* @return Updated Calendar
|
||||
*/
|
||||
public OmCalendar update(OmCalendar c) {
|
||||
if (c.getId() == null) {
|
||||
em.persist(c);
|
||||
} else {
|
||||
c = em.merge(c);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the Calendar on the Database, along with all the Appointments associated with it.
|
||||
*
|
||||
* @param c Calendar to Delete
|
||||
*/
|
||||
public void delete(OmCalendar c) {
|
||||
c.setDeleted(true);
|
||||
|
||||
//Delete all appointments in calendar.
|
||||
appointmentDao.deletebyCalendar(c.getId());
|
||||
|
||||
//Cascades the Appointment Updates as well.
|
||||
update(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OmCalendar> get(long start, long count) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OmCalendar> get(String search, long start, long count, SortParam<String> order) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String search) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OmCalendar update(OmCalendar entity, Long userId) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(OmCalendar entity, Long userId) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.file;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.UNSUPPORTED;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.single;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.apache.openmeetings.db.dao.IDataProviderDao;
|
||||
import org.apache.openmeetings.db.dao.room.RoomDao;
|
||||
import org.apache.openmeetings.db.dao.user.GroupDao;
|
||||
import org.apache.openmeetings.db.dao.user.UserDao;
|
||||
import org.apache.openmeetings.db.entity.file.BaseFileItem;
|
||||
import org.apache.openmeetings.db.entity.room.Room;
|
||||
import org.apache.openmeetings.db.entity.user.Group;
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class BaseFileItemDao implements IDataProviderDao<BaseFileItem> {
|
||||
private static final Logger log = LoggerFactory.getLogger(BaseFileItemDao.class);
|
||||
@PersistenceContext
|
||||
protected EntityManager em;
|
||||
@Autowired
|
||||
private RoomDao roomDao;
|
||||
@Autowired
|
||||
private GroupDao groupDao;
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
|
||||
public <T extends BaseFileItem> T get(String hash, Class<T> clazz) {
|
||||
return getAny("getFileByHash", hash, clazz);
|
||||
}
|
||||
|
||||
public <T extends BaseFileItem> T getAny(String hash, Class<T> clazz) {
|
||||
return getAny("getAnyFileByHash", hash, clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseFileItem get(Long id) {
|
||||
return getBase(id);
|
||||
}
|
||||
|
||||
public BaseFileItem getBase(Long id) {
|
||||
if (id == null || id.longValue() <= 0) {
|
||||
return null;
|
||||
}
|
||||
return single(em.createNamedQuery("getFileById", BaseFileItem.class)
|
||||
.setParameter("id", id).getResultList());
|
||||
}
|
||||
|
||||
public BaseFileItem getAny(Long id) {
|
||||
return single(em.createNamedQuery("getAnyFileById", BaseFileItem.class)
|
||||
.setParameter("id", id).getResultList());
|
||||
}
|
||||
|
||||
private <T extends BaseFileItem> T getAny(String query, String hash, Class<T> clazz) {
|
||||
log.debug("getAny[{}]() started", query);
|
||||
return single(em.createNamedQuery(query, clazz)
|
||||
.setParameter("hash", hash).getResultList());
|
||||
}
|
||||
|
||||
public void delete(BaseFileItem f) {
|
||||
if (f == null || f.getId() == null) {
|
||||
return;
|
||||
}
|
||||
f.setDeleted(true);
|
||||
|
||||
updateBase(f);
|
||||
}
|
||||
|
||||
public BaseFileItem updateBase(BaseFileItem f) {
|
||||
f.setExternalType(null);
|
||||
BaseFileItem parent = get(f.getParentId());
|
||||
if (parent != null) {
|
||||
f.setExternalType(parent.getExternalType());
|
||||
}
|
||||
if (Strings.isEmpty(f.getExternalType())) {
|
||||
Room r = roomDao.get(f.getRoomId());
|
||||
if (r != null) {
|
||||
f.setExternalType(r.externalType());
|
||||
}
|
||||
}
|
||||
if (Strings.isEmpty(f.getExternalType())) {
|
||||
Group g = groupDao.get(f.getGroupId());
|
||||
if (g != null && g.isExternal()) {
|
||||
f.setExternalType(g.getName());
|
||||
}
|
||||
}
|
||||
if (Strings.isEmpty(f.getExternalType())) {
|
||||
User u = userDao.get(f.getOwnerId());
|
||||
if (u != null) {
|
||||
f.setExternalType(u.externalType());
|
||||
}
|
||||
}
|
||||
if (Strings.isEmpty(f.getExternalType())) {
|
||||
User u = userDao.get(f.getInsertedBy());
|
||||
if (u != null) {
|
||||
f.setExternalType(u.externalType());
|
||||
}
|
||||
}
|
||||
if (f.getId() == null) {
|
||||
em.persist(f);
|
||||
} else {
|
||||
f = em.merge(f);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BaseFileItem> get(long start, long count) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BaseFileItem> get(String search, long start, long count, SortParam<String> order) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String search) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseFileItem update(BaseFileItem entity, Long userId) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(BaseFileItem entity, Long userId) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.file;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.only;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import org.apache.openmeetings.db.entity.file.BaseFileItem;
|
||||
import org.apache.openmeetings.db.entity.file.BaseFileItem.Type;
|
||||
import org.apache.openmeetings.db.entity.file.FileItem;
|
||||
import org.apache.openmeetings.db.entity.user.Group;
|
||||
import org.apache.openmeetings.util.OmFileHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* @author sebastianwagner
|
||||
*
|
||||
*/
|
||||
@Repository
|
||||
@Transactional
|
||||
public class FileItemDao extends BaseFileItemDao {
|
||||
private static final Logger log = LoggerFactory.getLogger(FileItemDao.class);
|
||||
|
||||
public List<FileItem> getByRoom(Long roomId) {
|
||||
log.debug("getByRoom roomId :: {}", roomId);
|
||||
return em.createNamedQuery("getFilesByRoom", FileItem.class).setParameter("roomId", roomId).getResultList();
|
||||
}
|
||||
|
||||
public List<FileItem> getByOwner(Long ownerId) {
|
||||
log.debug("getByOwner() started");
|
||||
TypedQuery<FileItem> query = em.createNamedQuery("getFilesByOwner", FileItem.class);
|
||||
query.setParameter("ownerId", ownerId);
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
public List<FileItem> getByGroup(Long groupId) {
|
||||
log.debug("getByGroup() started");
|
||||
return em.createNamedQuery("getFileByGroup", FileItem.class)
|
||||
.setParameter("groupId", groupId)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public List<FileItem> getByGroup(Long groupId, List<Type> filter) {
|
||||
if (filter == null) {
|
||||
return getByGroup(groupId);
|
||||
}
|
||||
log.debug("getByGroup() started");
|
||||
return em.createNamedQuery("getFileFilteredByGroup", FileItem.class)
|
||||
.setParameter("filter", filter)
|
||||
.setParameter("groupId", groupId)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public List<FileItem> getByParent(Long parentId) {
|
||||
log.debug("getByParent() started");
|
||||
return em.createNamedQuery("getFilesByParent", FileItem.class)
|
||||
.setParameter("parentId", parentId)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public List<FileItem> getByParent(Long parentId, List<Type> filter) {
|
||||
if (filter == null) {
|
||||
return getByParent(parentId);
|
||||
}
|
||||
log.debug("getByParent(filter) started");
|
||||
return em.createNamedQuery("getFilesFilteredByParent", FileItem.class)
|
||||
.setParameter("filter", filter)
|
||||
.setParameter("parentId", parentId)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileItem get(Long id) {
|
||||
BaseFileItem bf = super.get(id);
|
||||
return bf instanceof FileItem fi ? fi : null;
|
||||
}
|
||||
|
||||
public FileItem get(String externalId, String externalType) {
|
||||
log.debug("get started");
|
||||
|
||||
return only(em.createNamedQuery("getFileExternal", FileItem.class)
|
||||
.setParameter("externalFileId", externalId).setParameter("externalType", externalType)
|
||||
.getResultList());
|
||||
}
|
||||
|
||||
public List<FileItem> get() {
|
||||
log.debug("get started");
|
||||
|
||||
return em.createNamedQuery("getAllFiles", FileItem.class).getResultList();
|
||||
}
|
||||
|
||||
public List<FileItem> getExternal(String externalType) {
|
||||
log.debug("get external started");
|
||||
|
||||
return em.createNamedQuery("getFileAllExternal", FileItem.class)
|
||||
.setParameter("externalType", externalType)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public void delete(String externalId, String externalType) {
|
||||
log.debug("delete started");
|
||||
|
||||
delete(get(externalId, externalType));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id - id of file item to rename
|
||||
* @param name - new name
|
||||
*
|
||||
* @return renamed item
|
||||
*/
|
||||
public FileItem rename(Long id, String name) {
|
||||
log.debug("rename started");
|
||||
|
||||
FileItem f = get(id);
|
||||
if (f == null) {
|
||||
return null;
|
||||
}
|
||||
f.setName(name);
|
||||
return update(f);
|
||||
}
|
||||
|
||||
public FileItem update(FileItem f) {
|
||||
return (FileItem)super.updateBase(f);
|
||||
}
|
||||
|
||||
private void updateChilds(FileItem f) {
|
||||
for (FileItem child : getByParent(f.getId())) {
|
||||
child.setOwnerId(f.getOwnerId());
|
||||
child.setRoomId(f.getRoomId());
|
||||
update(child);
|
||||
if (Type.FOLDER == f.getType()) {
|
||||
updateChilds(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id - id of file item to move
|
||||
* @param parentId - id of parent item
|
||||
* @param ownerId - id of item owner
|
||||
* @param roomId - id of room
|
||||
*
|
||||
* @return moved item
|
||||
*/
|
||||
public FileItem move(long id, long parentId, long ownerId, long roomId) {
|
||||
log.debug(".move() started");
|
||||
|
||||
FileItem f = get(Long.valueOf(id));
|
||||
if (f == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parentId < 0) {
|
||||
if (parentId == -1) {
|
||||
// move to personal Folder
|
||||
f.setOwnerId(ownerId);
|
||||
f.setRoomId(null);
|
||||
} else {
|
||||
// move to public room folder
|
||||
f.setOwnerId(null);
|
||||
f.setRoomId(roomId);
|
||||
}
|
||||
f.setParentId(null);
|
||||
} else {
|
||||
f.setParentId(parentId);
|
||||
f.setOwnerId(null);
|
||||
}
|
||||
if (Type.FOLDER == f.getType()) {
|
||||
updateChilds(f);
|
||||
}
|
||||
return update(f);
|
||||
}
|
||||
|
||||
public List<BaseFileItem> getAllRoomFiles(String search, long start, long count, Long roomId, List<Group> groups) {
|
||||
return setLimits(em.createNamedQuery("getAllFileItemsForRoom", BaseFileItem.class)
|
||||
.setParameter("folder", Type.FOLDER)
|
||||
.setParameter("roomId", roomId)
|
||||
.setParameter("groups", groups.parallelStream().map(Group::getId).toList())
|
||||
.setParameter("name", String.format("%%%s%%", search == null ? "" : search))
|
||||
, start, count).getResultList();
|
||||
}
|
||||
|
||||
public List<BaseFileItem> get(Collection<String> ids) {
|
||||
return em.createNamedQuery("getFileItemsByIds", BaseFileItem.class)
|
||||
.setParameter("ids", ids.parallelStream().map(Long::valueOf).toList())
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public long getOwnSize(Long userId) {
|
||||
return getSize(getByOwner(userId));
|
||||
}
|
||||
|
||||
public long getRoomSize(Long roomId) {
|
||||
return getSize(getByRoom(roomId));
|
||||
}
|
||||
|
||||
public long getSize(List<FileItem> list) {
|
||||
long size = 0;
|
||||
for (FileItem f : list) {
|
||||
size += getSize(f);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public long getSize(FileItem f) {
|
||||
long size = 0;
|
||||
try {
|
||||
if (f.exists()) {
|
||||
File base = OmFileHelper.getUploadFilesDir();
|
||||
switch (f.getType()) {
|
||||
case IMAGE, PRESENTATION, VIDEO:
|
||||
File tFolder = new File(base, f.getHash());
|
||||
|
||||
if (tFolder.exists()) {
|
||||
size += OmFileHelper.getSize(tFolder);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Type.FOLDER == f.getType()) {
|
||||
for (FileItem child : getByParent(f.getId())) {
|
||||
size += getSize(child);
|
||||
}
|
||||
}
|
||||
} catch (Exception err) {
|
||||
log.error("[getSize] ", err);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.file;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.apache.openmeetings.db.entity.file.BaseFileItem;
|
||||
import org.apache.openmeetings.db.entity.file.FileItemLog;
|
||||
import org.apache.openmeetings.util.process.ProcessResult;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class FileItemLogDao {
|
||||
private static final Logger log = LoggerFactory.getLogger(FileItemLogDao.class);
|
||||
private static final String PARAM_FILEID = "fileId";
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
public long countErrors(BaseFileItem f) {
|
||||
return em.createNamedQuery("countErrorFileLogsByFile", Long.class).setParameter(PARAM_FILEID, f.getId())
|
||||
.getSingleResult();
|
||||
}
|
||||
|
||||
public List<FileItemLog> get(BaseFileItem f) {
|
||||
return em.createNamedQuery("getFileLogsByFile", FileItemLog.class).setParameter(PARAM_FILEID, f.getId())
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public void delete(BaseFileItem f) {
|
||||
em.createNamedQuery("deleteErrorFileLogsByFile").setParameter(PARAM_FILEID, f.getId())
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
public FileItemLog add(String name, BaseFileItem f, ProcessResult r) {
|
||||
log.trace("Adding log: {}, {}, {}", name, f, r);
|
||||
FileItemLog l = new FileItemLog()
|
||||
.setInserted(new Date())
|
||||
.setExitCode(r.getExitCode())
|
||||
.setFileId(f.getId())
|
||||
.setMessage(r.buildLogMessage())
|
||||
.setName(name)
|
||||
.setOptional(r.isOptional());
|
||||
|
||||
em.persist(l);
|
||||
return l;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.label;
|
||||
|
||||
import static java.util.Locale.ENGLISH;
|
||||
import static java.util.Locale.ROOT;
|
||||
import static java.util.ResourceBundle.Control.FORMAT_PROPERTIES;
|
||||
import static java.util.ResourceBundle.Control.getControl;
|
||||
import static org.apache.openmeetings.db.util.ApplicationHelper.ensureApplication;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.UNSUPPORTED;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.getAppClass;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.openmeetings.db.dao.IDataProviderDao;
|
||||
import org.apache.openmeetings.db.entity.label.OmLanguage;
|
||||
import org.apache.openmeetings.db.entity.label.StringLabel;
|
||||
import org.apache.openmeetings.db.util.XmlHelper;
|
||||
import org.apache.openmeetings.util.OmFileHelper;
|
||||
import org.apache.openmeetings.util.XmlExport;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.io.SAXReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* CRUD operations for {@link StringLabel}
|
||||
*
|
||||
* @author swagner
|
||||
*
|
||||
*/
|
||||
public class LabelDao implements IDataProviderDao<StringLabel>{
|
||||
private static final Logger log = LoggerFactory.getLogger(LabelDao.class);
|
||||
private static final String RANGE_START = "rangeStart";
|
||||
private static final String RANGE_END = "rangeEnd";
|
||||
public static final String ENTRY_ELEMENT = "entry";
|
||||
public static final String KEY_ATTR = "key";
|
||||
public static final String APP_RESOURCES_PREFIX = "Application";
|
||||
public static final String APP_RESOURCES_SUFFIX = ".properties.xml";
|
||||
private static final OmLanguage LNG_ENGLISH = new OmLanguage(1L, Locale.ENGLISH);
|
||||
private static final LinkedHashMap<Long, OmLanguage> languages = new LinkedHashMap<>();
|
||||
private static final ConcurrentHashMap<Locale, List<StringLabel>> labelCache = new ConcurrentHashMap<>();
|
||||
private static final Set<String> keys = new HashSet<>();
|
||||
private static Class<?> appClass = null;
|
||||
|
||||
private static void storeLanguages() throws Exception {
|
||||
Document d = XmlExport.createDocument();
|
||||
Element r = XmlExport.createRoot(d, "language");
|
||||
for (Entry<Long, OmLanguage> e : languages.entrySet()) {
|
||||
r.addElement("lang")
|
||||
.addAttribute("id", "" + e.getKey())
|
||||
.addAttribute("code", e.getValue().getLocale().toLanguageTag())
|
||||
.addAttribute("tip", e.getValue().getTip())
|
||||
.addAttribute(RANGE_START, "" + e.getValue().getRangeStart())
|
||||
.addAttribute(RANGE_END, "" + e.getValue().getRangeEnd());
|
||||
}
|
||||
XmlExport.toXml(getLangFile(), d);
|
||||
}
|
||||
|
||||
public static void add(Locale l) throws Exception {
|
||||
long id = 0L;
|
||||
for (Entry<Long, OmLanguage> e : languages.entrySet()) {
|
||||
id = e.getKey();
|
||||
}
|
||||
languages.put(id + 1, new OmLanguage(id + 1, l));
|
||||
storeLanguages();
|
||||
labelCache.put(l, new ArrayList<>());
|
||||
}
|
||||
|
||||
public static String getString(String key, long langId) {
|
||||
return ensureApplication().getOmString(key, langId);
|
||||
}
|
||||
|
||||
private static File getLangFile() {
|
||||
return new File(OmFileHelper.getLanguagesDir(), OmFileHelper.LANG_FILE_NAME);
|
||||
}
|
||||
|
||||
public static void initLanguageMap() {
|
||||
try {
|
||||
SAXReader reader = XmlHelper.createSaxReader();
|
||||
appClass = getAppClass();
|
||||
Document document = reader.read(getLangFile());
|
||||
Element root = document.getRootElement();
|
||||
languages.clear();
|
||||
for (Iterator<Element> it = root.elementIterator("lang"); it.hasNext();) {
|
||||
Element item = it.next();
|
||||
Long id = Long.valueOf(item.attributeValue("id"));
|
||||
String code = item.attributeValue("code");
|
||||
if (id == 3L) {
|
||||
continue;
|
||||
}
|
||||
languages.put(id, new OmLanguage(id, Locale.forLanguageTag(code))
|
||||
.setTip(item.attributeValue("tip"))
|
||||
.setRangeStart(Optional.ofNullable(item.attributeValue(RANGE_START)).map(s -> s.charAt(0)).orElse('A'))
|
||||
.setRangeEnd(Optional.ofNullable(item.attributeValue(RANGE_END)).map(s -> s.charAt(0)).orElse('Z'))
|
||||
);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error while building language map");
|
||||
}
|
||||
}
|
||||
|
||||
public static String getLabelFileName(Locale l) {
|
||||
String name = getControl(FORMAT_PROPERTIES).toBundleName(APP_RESOURCES_PREFIX, ENGLISH.equals(l) ? ROOT : l);
|
||||
return String.format("%s%s", name, APP_RESOURCES_SUFFIX);
|
||||
}
|
||||
|
||||
private static void storeLabels(Locale l) throws Exception {
|
||||
Document d = XmlExport.createDocument();
|
||||
Element r = XmlExport.createRoot(d);
|
||||
List<StringLabel> labels = new ArrayList<>(labelCache.get(l));
|
||||
Collections.sort(labels, new LabelComparator());
|
||||
for (StringLabel sl : labels) {
|
||||
r.addElement(ENTRY_ELEMENT).addAttribute(KEY_ATTR, sl.getKey()).addCDATA(sl.getValue());
|
||||
}
|
||||
URL u = appClass.getResource(getLabelFileName(l));
|
||||
XmlExport.toXml(new File(u.toURI()), d);
|
||||
}
|
||||
|
||||
public static void upload(Locale l, InputStream is) throws Exception {
|
||||
List<StringLabel> labels = getLabels(is);
|
||||
URL u = appClass.getResource(getLabelFileName(Locale.ENGLISH)); //get the URL of existing resource
|
||||
File el = new File(u.toURI());
|
||||
File f = new File(el.getParentFile(), getLabelFileName(l));
|
||||
if (!f.exists()) {
|
||||
f.createNewFile();
|
||||
}
|
||||
labelCache.put(l, labels);
|
||||
storeLabels(l);
|
||||
}
|
||||
|
||||
private static List<StringLabel> getLabels(Locale l) {
|
||||
List<StringLabel> labels = new ArrayList<>();
|
||||
try (InputStream is = appClass.getResourceAsStream(getLabelFileName(l))) {
|
||||
labels = getLabels(is);
|
||||
} catch (Exception e) {
|
||||
log.error("Error reading resources document", e);
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
private static List<StringLabel> getLabels(InputStream is) throws IOException {
|
||||
final List<StringLabel> labels = new ArrayList<>();
|
||||
Properties props = new Properties();
|
||||
props.loadFromXML(is);
|
||||
props.forEach((k, v) -> labels.add(new StringLabel((String)k, (String)v)));
|
||||
return labels;
|
||||
}
|
||||
|
||||
private static List<StringLabel> getLabels(Locale l, final String search) {
|
||||
if (!labelCache.containsKey(l)) {
|
||||
List<StringLabel> ll = getLabels(l);
|
||||
labelCache.putIfAbsent(l, ll);
|
||||
}
|
||||
List<StringLabel> result = new ArrayList<>(labelCache.containsKey(l) ? labelCache.get(l) : new ArrayList<StringLabel>());
|
||||
if (!Strings.isEmpty(search)) {
|
||||
CollectionUtils.filter(result, o -> o != null && (o.getKey().contains(search) || o.getValue().contains(search)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringLabel get(long id) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringLabel get(Long id) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StringLabel> get(long start, long count) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StringLabel> get(String search, long start, long count, SortParam<String> order) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
public static OmLanguage getLanguage(Long id) {
|
||||
return languages.getOrDefault(id == null ? 1L : id, LNG_ENGLISH);
|
||||
}
|
||||
|
||||
public static Locale getLocale(Long id) {
|
||||
return getLanguage(id).getLocale();
|
||||
}
|
||||
|
||||
public static OmLanguage getOmLanguage(Locale loc, Long def) {
|
||||
Optional<OmLanguage> lang = languages.entrySet().stream()
|
||||
.map(Entry::getValue)
|
||||
.filter(l -> l.getLocale().equals(loc)
|
||||
|| Locale.forLanguageTag(l.getLocale().getCountry()).equals(loc))
|
||||
.findFirst();
|
||||
return lang.orElse(getLanguage(def));
|
||||
}
|
||||
|
||||
public static Long getLanguage(Locale loc, Long def) {
|
||||
return getOmLanguage(loc, def).getId();
|
||||
}
|
||||
|
||||
public static Set<Entry<Long, Locale>> getLanguages() {
|
||||
return languages.entrySet().stream()
|
||||
.collect(Collectors.toMap(Entry::getKey, e -> e.getValue().getLocale()))
|
||||
.entrySet();
|
||||
}
|
||||
|
||||
public static List<StringLabel> get(Locale l, final String search, long start, long count, final SortParam<String> sort) {
|
||||
List<StringLabel> result = getLabels(l, search);
|
||||
if (sort != null) {
|
||||
Collections.sort(result, new LabelComparator(sort));
|
||||
}
|
||||
return result.subList((int)start, (int)(start + count > result.size() ? result.size() : start + count));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String search) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
public static long count(Locale l, final String search) {
|
||||
return getLabels(l, search).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringLabel update(StringLabel entity, Long userId) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
public static StringLabel update(Locale l, StringLabel entity) throws Exception {
|
||||
List<StringLabel> labels = labelCache.get(l);
|
||||
if (!labels.contains(entity)) {
|
||||
labels.add(entity);
|
||||
keys.add(entity.getKey());
|
||||
}
|
||||
storeLabels(l);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(StringLabel entity, Long userId) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
public static void delete(Locale l, StringLabel entity) throws Exception {
|
||||
List<StringLabel> labels = labelCache.get(l);
|
||||
if (labels.contains(entity)) {
|
||||
labels.remove(entity);
|
||||
keys.remove(entity.getKey());
|
||||
storeLabels(l);
|
||||
}
|
||||
}
|
||||
|
||||
public static void delete(Locale l) {
|
||||
for (Entry<Long, OmLanguage> e : languages.entrySet()) {
|
||||
if (e.getValue().getLocale().equals(l)) {
|
||||
languages.remove(e.getKey());
|
||||
break;
|
||||
}
|
||||
}
|
||||
labelCache.remove(l);
|
||||
try {
|
||||
URL u = appClass.getResource(getLabelFileName(l));
|
||||
if (u != null) {
|
||||
File f = new File(u.toURI());
|
||||
Files.deleteIfExists(f.toPath());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected error while deleting language", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LabelComparator implements Comparator<StringLabel>, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
final SortParam<String> sort;
|
||||
|
||||
LabelComparator() {
|
||||
this.sort = new SortParam<>(KEY_ATTR, true);
|
||||
}
|
||||
|
||||
LabelComparator(SortParam<String> sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(StringLabel o1, StringLabel o2) {
|
||||
int val;
|
||||
if (KEY_ATTR.equals(sort.getProperty())) {
|
||||
try {
|
||||
int i1 = Integer.parseInt(o1.getKey()), i2 = Integer.parseInt(o2.getKey());
|
||||
val = i1 - i2;
|
||||
} catch (Exception e) {
|
||||
val = o1.getKey().compareTo(o2.getKey());
|
||||
}
|
||||
} else {
|
||||
val = o1.getValue().compareTo(o2.getValue());
|
||||
}
|
||||
return (sort.isAscending() ? 1 : -1) * val;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.log;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.apache.openmeetings.db.entity.log.ConferenceLog;
|
||||
import org.apache.openmeetings.db.entity.log.ConferenceLog.Type;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class ConferenceLogDao {
|
||||
private static final Logger log = LoggerFactory.getLogger(ConferenceLogDao.class);
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
public ConferenceLog add(Type type, Long userId, String streamid, Long roomId, String userip, String scopeName) {
|
||||
ConferenceLog confLog = new ConferenceLog();
|
||||
confLog.setType(type);
|
||||
confLog.setInserted(new Date());
|
||||
confLog.setUserId(userId);
|
||||
confLog.setStreamid(streamid);
|
||||
confLog.setScopeName(scopeName);
|
||||
confLog.setRoomId(roomId);
|
||||
confLog.setUserip(userip);
|
||||
|
||||
em.persist(confLog);
|
||||
log.debug("[add]: {}", confLog);
|
||||
return confLog;
|
||||
}
|
||||
|
||||
public int clear(long ttl) {
|
||||
return em.createNamedQuery("clearLogUserIp")
|
||||
.setParameter("date", new Date(System.currentTimeMillis() - ttl))
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.record;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.only;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.apache.openmeetings.db.entity.record.RecordingChunk;
|
||||
import org.apache.openmeetings.db.entity.record.RecordingChunk.Status;
|
||||
import org.apache.openmeetings.db.entity.record.RecordingChunk.Type;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class RecordingChunkDao {
|
||||
private static final Logger log = LoggerFactory.getLogger(RecordingChunkDao.class);
|
||||
private static final String PARAM_RECID = "recordingId";
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
@Autowired
|
||||
private RecordingDao recordingDao;
|
||||
|
||||
public RecordingChunk get(Long id) {
|
||||
return only(em.createNamedQuery("getChunkById", RecordingChunk.class)
|
||||
.setParameter("id", id).getResultList());
|
||||
}
|
||||
|
||||
public List<RecordingChunk> getByRecording(Long recordingId) {
|
||||
return em.createNamedQuery("getChunkByRecording", RecordingChunk.class)
|
||||
.setParameter(PARAM_RECID, recordingId)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public List<RecordingChunk> getNotScreenChunksByRecording(Long recordingId) {
|
||||
return em.createNamedQuery("getNotScreenChunkByRecording", RecordingChunk.class)
|
||||
.setParameter(PARAM_RECID, recordingId)
|
||||
.setParameter("screen", Type.SCREEN)
|
||||
.setParameter("none", Status.NONE)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public RecordingChunk getScreenByRecording(Long recordingId) {
|
||||
List<RecordingChunk> list = em.createNamedQuery("getScreenChunkByRecording", RecordingChunk.class)
|
||||
.setParameter("screen", Type.SCREEN)
|
||||
.setParameter(PARAM_RECID, recordingId)
|
||||
.getResultList();
|
||||
return list.isEmpty() ? null : list.get(0);
|
||||
}
|
||||
|
||||
public Long start(Long recordingId, Type type, String streamName, String sid) {
|
||||
RecordingChunk chunk = new RecordingChunk();
|
||||
chunk.setRecording(recordingDao.get(recordingId));
|
||||
chunk.setStart(new Date());
|
||||
chunk.setType(type);
|
||||
chunk.setStreamName(streamName);
|
||||
chunk.setStreamStatus(Status.STARTED);
|
||||
chunk.setSid(sid);
|
||||
chunk = update(chunk);
|
||||
return chunk.getId();
|
||||
}
|
||||
|
||||
public void stop(Long chunkId) {
|
||||
RecordingChunk chunk = get(chunkId);
|
||||
if (chunk != null) {
|
||||
chunk.setEnd(new Date());
|
||||
chunk.setStreamStatus(Status.STOPPED);
|
||||
update(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
public RecordingChunk update(RecordingChunk chunk) {
|
||||
log.debug("[update]: ");
|
||||
if (chunk.getId() == null) {
|
||||
em.persist(chunk);
|
||||
} else {
|
||||
chunk = em.merge(chunk);
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.record;
|
||||
|
||||
import static org.apache.openmeetings.db.dao.user.UserDao.FETCH_GROUP_BACKUP;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.fillLazy;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_MP4;
|
||||
import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PNG;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import org.apache.openmeetings.db.dao.file.BaseFileItemDao;
|
||||
import org.apache.openmeetings.db.dao.user.UserDao;
|
||||
import org.apache.openmeetings.db.dto.record.RecordingContainerData;
|
||||
import org.apache.openmeetings.db.entity.file.BaseFileItem;
|
||||
import org.apache.openmeetings.db.entity.record.Recording;
|
||||
import org.apache.openmeetings.db.entity.record.Recording.Status;
|
||||
import org.apache.openmeetings.db.entity.user.GroupUser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* @author sebastianwagner
|
||||
*
|
||||
*/
|
||||
@Repository
|
||||
@Transactional
|
||||
public class RecordingDao extends BaseFileItemDao {
|
||||
private static final Logger log = LoggerFactory.getLogger(RecordingDao.class);
|
||||
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
|
||||
@Override
|
||||
public Recording get(Long id) {
|
||||
BaseFileItem bf = super.get(id);
|
||||
return bf instanceof Recording rec ? rec : null;
|
||||
}
|
||||
|
||||
public List<Recording> getByExternalUser(String externalId, String externalType) {
|
||||
log.debug("getByExternalUser :externalId: {}; externalType: {}", externalId, externalType);
|
||||
|
||||
return em.createNamedQuery("getRecordingsByExternalUser", Recording.class)
|
||||
.setParameter("externalId", externalId)
|
||||
.setParameter("externalType", externalType)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public List<Recording> get() {
|
||||
return fillLazy(em
|
||||
, oem -> oem.createNamedQuery("getRecordingsAll", Recording.class)
|
||||
, FETCH_GROUP_BACKUP);
|
||||
}
|
||||
|
||||
public List<Recording> getByExternalType(String externalType) {
|
||||
log.debug("getByExternalType :externalType: {}", externalType);
|
||||
|
||||
return em.createNamedQuery("getRecordingsByExternalType", Recording.class)
|
||||
.setParameter("externalType", externalType)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public List<Recording> getRootByPublic(Long groupId) {
|
||||
TypedQuery<Recording> q = em.createNamedQuery(groupId == null ? "getRecordingsPublic" : "getRecordingsByGroup", Recording.class);
|
||||
if (groupId != null) {
|
||||
q.setParameter("groupId", groupId);
|
||||
}
|
||||
return q.getResultList();
|
||||
}
|
||||
|
||||
public List<Recording> getRootByOwner(Long ownerId) {
|
||||
return em.createNamedQuery("getRecordingsByOwner", Recording.class).setParameter("ownerId", ownerId).getResultList();
|
||||
}
|
||||
|
||||
public List<Recording> getByRoomId(Long roomId) {
|
||||
return em.createNamedQuery("getRecordingsByRoom", Recording.class)
|
||||
.setParameter("roomId", roomId)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public List<Recording> getExpiring(Long groupId, int reminderDays, boolean notified) {
|
||||
Instant date = Instant.now().minus(Duration.ofDays(reminderDays));
|
||||
return em.createNamedQuery("getExpiringRecordings", Recording.class)
|
||||
.setParameter("groupId", groupId)
|
||||
.setParameter("date", Date.from(date))
|
||||
.setParameter("notified", notified)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public List<Recording> getByParent(Long parentId) {
|
||||
return em.createNamedQuery("getRecordingsByParent", Recording.class)
|
||||
.setParameter("parentId", parentId)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public Recording update(Recording f) {
|
||||
return (Recording)updateBase(f);
|
||||
}
|
||||
|
||||
public void resetProcessingStatus() {
|
||||
em.createNamedQuery("resetRecordingProcessingStatus")
|
||||
.setParameter("error", Status.ERROR)
|
||||
.setParameter("recording", Status.RECORDING)
|
||||
.setParameter("converting", Status.CONVERTING)
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
public RecordingContainerData getContainerData(long userId) {
|
||||
try {
|
||||
RecordingContainerData containerData = new RecordingContainerData();
|
||||
|
||||
// User Home Recordings
|
||||
List<Recording> homes = getRootByOwner(userId);
|
||||
long homeFileSize = 0;
|
||||
|
||||
for (Recording home : homes) {
|
||||
homeFileSize += getSize(home);
|
||||
}
|
||||
|
||||
containerData.setUserHomeSize(homeFileSize);
|
||||
|
||||
// Public Recordings
|
||||
long publicFileSize = 0;
|
||||
|
||||
//get all groups the user can view
|
||||
for (GroupUser ou : userDao.get(userId).getGroupUsers()) {
|
||||
List<Recording> publicRecordings = getRootByPublic(ou.getGroup().getId());
|
||||
//get sizes
|
||||
for (Recording r : publicRecordings) {
|
||||
publicFileSize += getSize(r);
|
||||
}
|
||||
}
|
||||
containerData.setPublicFileSize(publicFileSize);
|
||||
|
||||
return containerData;
|
||||
} catch (Exception ex2) {
|
||||
log.error("[getRecordingContainerData]: ", ex2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private long getSize(Recording r) {
|
||||
long size = 0;
|
||||
|
||||
if (r.exists(EXTENSION_PNG)) {
|
||||
size += r.getFile(EXTENSION_PNG).length();
|
||||
}
|
||||
if (r.exists(EXTENSION_MP4)) {
|
||||
size += r.getFile(EXTENSION_MP4).length();
|
||||
}
|
||||
for (Recording rec : getByParent(r.getId())) {
|
||||
size += getSize(rec);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.room;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.getRoot;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.single;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.apache.openmeetings.db.dao.IGroupAdminDataProviderDao;
|
||||
import org.apache.openmeetings.db.entity.room.ExtraMenu;
|
||||
import org.apache.openmeetings.db.util.DaoHelper;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class ExtraMenuDao implements IGroupAdminDataProviderDao<ExtraMenu> {
|
||||
private static final List<String> searchFields = List.of("name", "link", "description");
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Override
|
||||
public ExtraMenu get(Long id) {
|
||||
return single(em.createNamedQuery("getExtraMenuById", ExtraMenu.class)
|
||||
.getResultList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtraMenu get(long id) {
|
||||
return get(Long.valueOf(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ExtraMenu> get(long start, long count) {
|
||||
return setLimits(em.createNamedQuery("getExtraMenus", ExtraMenu.class), start, count).getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ExtraMenu> get(String search, long start, long count, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, ExtraMenu.class, false, search, searchFields, false, null, sort, start, count);
|
||||
}
|
||||
|
||||
public List<ExtraMenu> getByGroups(List<Long> groups) {
|
||||
return em.createNamedQuery("getExtraMenuByGroups", ExtraMenu.class)
|
||||
.setParameter("ids", groups)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return em.createNamedQuery("countExtraMenus", Long.class).getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String search) {
|
||||
return DaoHelper.count(em, ExtraMenu.class, search, searchFields, false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtraMenu update(ExtraMenu entity, Long userId) {
|
||||
if (entity.getId() == null) {
|
||||
em.persist(entity);
|
||||
} else {
|
||||
entity = em.merge(entity);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(ExtraMenu entity, Long userId) {
|
||||
em.remove(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ExtraMenu> adminGet(String search, Long adminId, long start, long count, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, ExtraMenu.class, true, search, searchFields, false
|
||||
, (builder, query) -> getGroupFilter(adminId, builder, query)
|
||||
, sort, start, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long adminCount(String search, Long adminId) {
|
||||
return DaoHelper.count(em, ExtraMenu.class, search, searchFields, false
|
||||
, (builder, query) -> getGroupFilter(adminId, builder, query));
|
||||
}
|
||||
|
||||
private Predicate getGroupFilter(Long adminId, CriteriaBuilder builder, CriteriaQuery<?> query) {
|
||||
Root<ExtraMenu> root = getRoot(query, ExtraMenu.class);
|
||||
return builder.in(root.get("groups")).value(DaoHelper.groupAdminQuery(adminId, builder, query));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.room;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.getRoot;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.only;
|
||||
import static org.apache.openmeetings.util.CalendarHelper.getZoneId;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Subquery;
|
||||
|
||||
import org.apache.openmeetings.db.dao.IDataProviderDao;
|
||||
import org.apache.openmeetings.db.entity.record.Recording;
|
||||
import org.apache.openmeetings.db.entity.room.Invitation;
|
||||
import org.apache.openmeetings.db.entity.room.Invitation.Valid;
|
||||
import org.apache.openmeetings.db.entity.room.Room;
|
||||
import org.apache.openmeetings.db.entity.user.GroupUser;
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
import org.apache.openmeetings.db.util.DaoHelper;
|
||||
import org.apache.openmeetings.util.CalendarHelper;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class InvitationDao implements IDataProviderDao<Invitation> {
|
||||
private static final List<String> searchFields = List.of("invitee.firstname", "invitee.lastname", "invitee.login");
|
||||
private static final Logger log = LoggerFactory.getLogger(InvitationDao.class);
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Override
|
||||
public Invitation get(Long invId) {
|
||||
return only(em.createNamedQuery("getInvitationbyId", Invitation.class)
|
||||
.setParameter("id", invId).getResultList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Invitation> get(long start, long count) {
|
||||
return get(null, start, count, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Invitation> get(String search, long start, long count, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, Invitation.class, false, search, searchFields, true, null, sort, start, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return count(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String search) {
|
||||
return DaoHelper.count(em, Invitation.class, search, searchFields, true, null);
|
||||
}
|
||||
|
||||
private Predicate getGroupFilter(Long userId, CriteriaBuilder builder, CriteriaQuery<?> query) {
|
||||
Subquery<Long> subquery = query.subquery(Long.class);
|
||||
Root<GroupUser> root = subquery.from(GroupUser.class);
|
||||
subquery.select(root.get("user").get("id"));
|
||||
subquery.where(builder.in(root.get("group").get("id")).value(DaoHelper.groupAdminQuery(userId, builder, subquery)));
|
||||
|
||||
Root<Invitation> mainRoot = getRoot(query, Invitation.class);
|
||||
return builder.in(mainRoot.get("invitedBy").get("id")).value(subquery);
|
||||
}
|
||||
|
||||
public List<Invitation> getGroup(String search, long start, long count, Long userId, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, Invitation.class, false, search, searchFields, true
|
||||
, (builder, query) -> getGroupFilter(userId, builder, query)
|
||||
, sort, start, count);
|
||||
}
|
||||
|
||||
public long countGroup(String search, Long userId) {
|
||||
return DaoHelper.count(em, Invitation.class, search, searchFields, true
|
||||
, (builder, query) -> getGroupFilter(userId, builder, query));
|
||||
}
|
||||
|
||||
private Predicate getUserFilter(Long userId, CriteriaBuilder builder, CriteriaQuery<?> query) {
|
||||
Root<Invitation> root = getRoot(query, Invitation.class);
|
||||
return builder.equal(root.get("invitedBy").get("id"), userId);
|
||||
}
|
||||
|
||||
public List<Invitation> getUser(String search, long start, long count, Long userId, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, Invitation.class, false, search, searchFields, true
|
||||
, (builder, query) -> getUserFilter(userId, builder, query)
|
||||
, sort, start, count);
|
||||
}
|
||||
|
||||
public long countUser(String search, Long userId) {
|
||||
return DaoHelper.count(em, Invitation.class, search, searchFields, true
|
||||
, (builder, query) -> getUserFilter(userId, builder, query));
|
||||
}
|
||||
|
||||
public Invitation update(Invitation invitation) {
|
||||
// [OPENMEETINGS-2441] in life cycle state unmanaged while cascading persistence via field
|
||||
invitation.setInvitedBy(em.find(User.class, invitation.getInvitedBy().getId()));
|
||||
if (invitation.getRoom() != null) {
|
||||
invitation.setRoom(em.find(Room.class, invitation.getRoom().getId()));
|
||||
}
|
||||
if (invitation.getRecording() != null) {
|
||||
invitation.setRecording(em.find(Recording.class, invitation.getRecording().getId()));
|
||||
}
|
||||
if (invitation.getId() == null) {
|
||||
em.persist(invitation);
|
||||
} else {
|
||||
invitation = em.merge(invitation);
|
||||
}
|
||||
return invitation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Invitation update(Invitation entity, Long userId) {
|
||||
return update(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Invitation entity, Long userId) {
|
||||
entity.setDeleted(true);
|
||||
update(entity, userId);
|
||||
}
|
||||
|
||||
public void markUsed(Invitation i) {
|
||||
if (Valid.ONE_TIME == i.getValid()) {
|
||||
i.setUsed(true);
|
||||
update(i);
|
||||
em.flush(); // flash is required to eliminate 'detach' effect
|
||||
}
|
||||
}
|
||||
|
||||
private Invitation get(String hash) {
|
||||
Invitation i = only(em.createNamedQuery("getInvitationByHashCode", Invitation.class)
|
||||
.setParameter("hashCode", hash).getResultList());
|
||||
return i != null && i.getHash().equals(hash) ? i : null;
|
||||
}
|
||||
|
||||
public Invitation getByHash(String hash, boolean hidePass) {
|
||||
Invitation i = get(hash);
|
||||
if (i != null) {
|
||||
switch (i.getValid()) {
|
||||
case ONE_TIME:
|
||||
// one-time invitation
|
||||
i.setAllowEntry(!i.isUsed());
|
||||
break;
|
||||
case PERIOD:
|
||||
String tzId = i.getInvitee().getTimeZoneId();
|
||||
if (Strings.isEmpty(tzId) && i.getAppointment() != null) {
|
||||
log.warn("User with NO timezone found: {}", i.getInvitee().getId());
|
||||
tzId = i.getAppointment().getOwner().getTimeZoneId();
|
||||
}
|
||||
if (Strings.isEmpty(tzId)) {
|
||||
log.warn("Unable to obtain valid timezone, setting SYSTEM TZ");
|
||||
tzId = TimeZone.getDefault().getID();
|
||||
}
|
||||
LocalDateTime now = ZonedDateTime.now(getZoneId(tzId)).toLocalDateTime();
|
||||
LocalDateTime from = CalendarHelper.getDateTime(i.getValidFrom(), tzId);
|
||||
LocalDateTime to = CalendarHelper.getDateTime(i.getValidTo(), tzId);
|
||||
i.setAllowEntry(now.isAfter(from) && now.isBefore(to));
|
||||
break;
|
||||
case ENDLESS:
|
||||
default:
|
||||
i.setAllowEntry(true);
|
||||
break;
|
||||
}
|
||||
em.detach(i); // required to disable password update
|
||||
if (hidePass) {
|
||||
i.setPassword(null);
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.room;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.only;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_USER_ID;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.apache.openmeetings.db.entity.room.RoomPoll;
|
||||
import org.apache.openmeetings.db.entity.room.RoomPollAnswer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class PollDao {
|
||||
private static final Logger log = LoggerFactory.getLogger(PollDao.class);
|
||||
private static final String PARAM_ROOMID = "roomId";
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
public RoomPoll update(RoomPoll p) {
|
||||
if (p.getId() == null) {
|
||||
p.setCreated(new Date());
|
||||
em.persist(p);
|
||||
} else {
|
||||
p = em.merge(p);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public boolean close(Long roomId) {
|
||||
try {
|
||||
log.debug(" :: close :: ");
|
||||
Query q = em.createNamedQuery("closePoll");
|
||||
q.setParameter(PARAM_ROOMID, roomId);
|
||||
q.setParameter("archived", true);
|
||||
return q.executeUpdate() > 0;
|
||||
} catch (Exception err) {
|
||||
log.error("[close]", err);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean delete(RoomPoll p) {
|
||||
try {
|
||||
log.debug(" :: delete :: ");
|
||||
Query q = em.createNamedQuery("deletePoll");
|
||||
q.setParameter("id", p.getId());
|
||||
return q.executeUpdate() > 0;
|
||||
} catch (Exception err) {
|
||||
log.error("[delete]", err);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public RoomPoll get(Long id) {
|
||||
List<RoomPoll> list = em.createNamedQuery("getPollById", RoomPoll.class).setParameter("id", id).getResultList();
|
||||
return list.isEmpty() ? null : list.get(0);
|
||||
}
|
||||
|
||||
public RoomPoll getByRoom(Long roomId) {
|
||||
log.debug(" :: getPoll :: {}", roomId);
|
||||
return only(em.createNamedQuery("getPoll", RoomPoll.class)
|
||||
.setParameter(PARAM_ROOMID, roomId).getResultList());
|
||||
}
|
||||
|
||||
public List<RoomPoll> get() {
|
||||
return em.createNamedQuery("getPollListBackup", RoomPoll.class).getResultList();
|
||||
}
|
||||
|
||||
public List<RoomPoll> getArchived(Long roomId) {
|
||||
log.debug(" :: getArchived :: {}", roomId);
|
||||
return em.createNamedQuery("getArchivedPollList",RoomPoll.class)
|
||||
.setParameter(PARAM_ROOMID, roomId).getResultList();
|
||||
}
|
||||
|
||||
public boolean hasPoll(Long roomId) {
|
||||
log.debug(" :: hasPoll :: {}", roomId);
|
||||
return em.createNamedQuery("hasPoll", Long.class)
|
||||
.setParameter(PARAM_ROOMID, roomId)
|
||||
.setParameter("archived", false)
|
||||
.getSingleResult() > 0;
|
||||
}
|
||||
|
||||
public boolean notVoted(Long roomId, Long userId) {
|
||||
return em.createNamedQuery("notVoted", RoomPollAnswer.class)
|
||||
.setParameter(PARAM_ROOMID, roomId)
|
||||
.setParameter(PARAM_USER_ID, userId)
|
||||
.getResultList().isEmpty();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.room;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.fillLazy;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.getRoot;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.single;
|
||||
import static org.apache.openmeetings.db.util.TimezoneUtil.getTimeZone;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_SIP_ROOM_PREFIX;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_USER_ID;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.RECENT_ROOMS_COUNT;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.isSipEnabled;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.apache.openmeetings.db.dao.IGroupAdminDataProviderDao;
|
||||
import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
|
||||
import org.apache.openmeetings.db.dao.user.UserDao;
|
||||
import org.apache.openmeetings.db.entity.log.ConferenceLog;
|
||||
import org.apache.openmeetings.db.entity.room.Room;
|
||||
import org.apache.openmeetings.db.entity.room.Room.RoomElement;
|
||||
import org.apache.openmeetings.db.entity.room.Room.Type;
|
||||
import org.apache.openmeetings.db.entity.room.RoomFile;
|
||||
import org.apache.openmeetings.db.entity.room.RoomGroup;
|
||||
import org.apache.openmeetings.db.manager.ISipManager;
|
||||
import org.apache.openmeetings.db.util.DaoHelper;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.apache.wicket.util.string.Strings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class RoomDao implements IGroupAdminDataProviderDao<Room> {
|
||||
private static final Logger log = LoggerFactory.getLogger(RoomDao.class);
|
||||
private static final List<String> searchFields = List.of("name", "comment");
|
||||
public static final String GRP_MODERATORS = "roomModerators";
|
||||
public static final String GRP_GROUPS = "roomGroups";
|
||||
public static final String GRP_FILES = "roomFiles";
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
@Autowired
|
||||
private ConfigurationDao cfgDao;
|
||||
@Autowired
|
||||
private ISipManager sipManager;
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
|
||||
@Override
|
||||
public Room get(Long id) {
|
||||
Room r = null;
|
||||
if (id != null && id.longValue() > 0) {
|
||||
r = single(fillLazy(em
|
||||
, oem -> oem.createNamedQuery("getRoomById", Room.class)
|
||||
.setParameter("id", id)
|
||||
, GRP_MODERATORS, GRP_GROUPS, GRP_FILES));
|
||||
} else {
|
||||
log.info("[get]: No room id given");
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public Room get(String tag) {
|
||||
Room r = null;
|
||||
if (!Strings.isEmpty(tag)) {
|
||||
r = single(fillLazy(em
|
||||
, oem -> oem.createNamedQuery("getRoomByTag", Room.class).setParameter("tag", tag)
|
||||
, GRP_MODERATORS, GRP_GROUPS, GRP_FILES));
|
||||
} else {
|
||||
log.info("[get]: No room tag given");
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public List<Room> get() {
|
||||
return fillLazy(em
|
||||
, oem -> oem.createNamedQuery("getBackupRooms", Room.class)
|
||||
, GRP_MODERATORS, GRP_GROUPS, GRP_FILES);
|
||||
}
|
||||
|
||||
public List<Room> get(List<Long> ids) {
|
||||
return em.createNamedQuery("getRoomsByIds", Room.class).setParameter("ids", ids).getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Room> get(long start, long count) {
|
||||
return setLimits(em.createNamedQuery("getNondeletedRooms", Room.class)
|
||||
, start, count).getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Room> get(String search, long start, long count, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, Room.class, false, search, searchFields, true, null, sort, start, count);
|
||||
}
|
||||
|
||||
private Predicate getAdminFilter(Long adminId, CriteriaBuilder builder, CriteriaQuery<?> query) {
|
||||
Root<RoomGroup> root = getRoot(query, RoomGroup.class);
|
||||
return builder.in(root.get("group").get("id")).value(DaoHelper.groupAdminQuery(adminId, builder, query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Room> adminGet(String search, Long adminId, long start, long count, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, RoomGroup.class, Room.class
|
||||
, (builder, root) -> root.get("room")
|
||||
, true, search, searchFields, false
|
||||
, (b, q) -> getAdminFilter(adminId, b, q)
|
||||
, sort, start, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return em.createNamedQuery("countRooms", Long.class)
|
||||
.getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String search) {
|
||||
return DaoHelper.count(em, Room.class, search, searchFields, true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long adminCount(String search, Long adminId) {
|
||||
return DaoHelper.count(em, RoomGroup.class
|
||||
, (builder, root) -> builder.countDistinct(root.get("room"))
|
||||
, search, searchFields, false
|
||||
, (b, q) -> getAdminFilter(adminId, b, q));
|
||||
}
|
||||
|
||||
public List<Room> getPublicRooms() {
|
||||
return em.createNamedQuery("getPublicRoomsOrdered", Room.class).getResultList();
|
||||
}
|
||||
|
||||
public List<Room> getPublicRooms(Type type) {
|
||||
return type == null ? getPublicRooms()
|
||||
: em.createNamedQuery("getPublicRooms", Room.class).setParameter("type", type).getResultList();
|
||||
}
|
||||
|
||||
public List<Long> getSipRooms(List<Long> ids) {
|
||||
TypedQuery<Long> q = em.createNamedQuery("getSipRoomIdsByIds", Long.class);
|
||||
q.setParameter("ids", ids);
|
||||
return q.getResultList();
|
||||
}
|
||||
|
||||
public List<Room> getGroupRooms(long groupId) {
|
||||
TypedQuery<Room> q = em.createNamedQuery("getGroupRooms", Room.class);
|
||||
q.setParameter("groupId", groupId);
|
||||
return q.getResultList();
|
||||
}
|
||||
|
||||
public List<Room> getAppointedRoomsByUser(long userId) {
|
||||
log.debug("getAppointedRoomsByUser : UserID - {}", userId);
|
||||
|
||||
TimeZone timeZone = getTimeZone(userDao.get(userId));
|
||||
|
||||
Calendar startCal = Calendar.getInstance(timeZone);
|
||||
startCal.set(Calendar.MINUTE, 0);
|
||||
startCal.set(Calendar.HOUR, 0);
|
||||
startCal.set(Calendar.SECOND, 1);
|
||||
|
||||
Calendar endCal = Calendar.getInstance(timeZone);
|
||||
endCal.set(Calendar.MINUTE, 23);
|
||||
endCal.set(Calendar.HOUR, 59);
|
||||
endCal.set(Calendar.SECOND, 59);
|
||||
|
||||
return em.createNamedQuery("appointedRoomsInRangeByUser", Room.class)
|
||||
.setParameter(PARAM_USER_ID, userId)
|
||||
.setParameter("start", startCal.getTime())
|
||||
.setParameter("end", endCal.getTime())
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
private String getSipNumber(long roomId) {
|
||||
if (isSipEnabled()) {
|
||||
return cfgDao.getString(CONFIG_SIP_ROOM_PREFIX, "400") + roomId;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Room update(Room entity, Long userId) {
|
||||
if (entity.getId() == null) {
|
||||
em.persist(entity);
|
||||
}
|
||||
if (entity.isSipEnabled() && isSipEnabled()) {
|
||||
String sipNumber = getSipNumber(entity.getId());
|
||||
if (sipNumber != null && !sipNumber.equals(entity.getConfno())) {
|
||||
entity.setConfno(sipNumber);
|
||||
}
|
||||
sipManager.update(sipNumber, entity.getPin());
|
||||
} else {
|
||||
sipManager.delete(entity.getConfno());
|
||||
entity.setConfno(null);
|
||||
entity.setPin(null);
|
||||
}
|
||||
entity = em.merge(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Room entity, Long userId) {
|
||||
entity.setDeleted(true);
|
||||
entity.setSipEnabled(false);
|
||||
update(entity, userId);
|
||||
}
|
||||
|
||||
public Room getUserRoom(Long ownerId, Room.Type type, String name) {
|
||||
log.debug("getUserRoom : {} || {}", ownerId, type);
|
||||
Room room = single(em.createNamedQuery("getRoomByOwnerAndTypeId", Room.class)
|
||||
.setParameter("ownerId", ownerId)
|
||||
.setParameter("type", type)
|
||||
.getResultList());
|
||||
|
||||
if (room == null) {
|
||||
log.debug("Could not find room {} || {}", ownerId, type);
|
||||
|
||||
room = new Room();
|
||||
room.setName(name);
|
||||
room.setType(type);
|
||||
room.setComment("My Rooms of ownerId " + ownerId);
|
||||
room.setCapacity(Room.Type.CONFERENCE == type ? 25L : 120L);
|
||||
room.setAllowUserQuestions(true);
|
||||
room.setOwnerId(ownerId);
|
||||
room.setAllowRecording(true);
|
||||
room.setModerated(true);
|
||||
room.hide(RoomElement.MICROPHONE_STATUS);
|
||||
|
||||
room = update(room, ownerId);
|
||||
if (room.getId() != null) {
|
||||
return room;
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
room.setName(name);
|
||||
room = update(room, ownerId);
|
||||
return room;
|
||||
}
|
||||
}
|
||||
|
||||
public Room getExternal(String externalType, String externalId) {
|
||||
log.debug("getExternal : {} - {}", externalType, externalId);
|
||||
return single(fillLazy(em
|
||||
, oem -> oem.createNamedQuery("getExternalRoomNoType", Room.class)
|
||||
.setParameter("externalId", externalId)
|
||||
.setParameter("externalType", externalType)
|
||||
, GRP_GROUPS));
|
||||
}
|
||||
|
||||
public Room getExternal(Type type, String externalType, String externalId) {
|
||||
log.debug("getExternal : {} - {} - {}", type, externalType, externalId);
|
||||
return single(fillLazy(em
|
||||
, oem -> oem.createNamedQuery("getExternalRoom", Room.class)
|
||||
.setParameter("externalId", externalId)
|
||||
.setParameter("externalType", externalType)
|
||||
.setParameter("type", type)
|
||||
, GRP_GROUPS));
|
||||
}
|
||||
|
||||
public List<Room> getRecent(Long userId) {
|
||||
List<Room> result = new ArrayList<>();
|
||||
Set<Long> ids = new HashSet<>();
|
||||
//(RECENT_ROOMS_COUNT + 1) passes required to preserve the order :(
|
||||
for (ConferenceLog l : em.createNamedQuery("getLogRecentRooms", ConferenceLog.class)
|
||||
.setParameter("roomEnter", ConferenceLog.Type.ROOM_ENTER)
|
||||
.setParameter(PARAM_USER_ID, userId)
|
||||
.getResultList())
|
||||
{
|
||||
if (!ids.contains(l.getRoomId())) {
|
||||
Room r = get(l.getRoomId());
|
||||
if (r != null && !r.isDeleted()) {
|
||||
result.add(r);
|
||||
ids.add(r.getId());
|
||||
}
|
||||
}
|
||||
if (ids.size() == RECENT_ROOMS_COUNT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Methods for backup export
|
||||
public List<RoomFile> getFiles() {
|
||||
return em.createQuery("SELECT rf FROM RoomFile rf", RoomFile.class)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
public List<RoomGroup> getGroups() {
|
||||
return em.createNamedQuery("getAllRoomGroups", RoomGroup.class).getResultList();
|
||||
}
|
||||
|
||||
public List<Room> getMyRooms(Long userId, String confLbl, String restrLbl) {
|
||||
List<Room> result = new ArrayList<>();
|
||||
result.add(getUserRoom(userId, Room.Type.CONFERENCE, confLbl));
|
||||
result.add(getUserRoom(userId, Room.Type.PRESENTATION, restrLbl));
|
||||
result.addAll(getAppointedRoomsByUser(userId));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.server;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.only;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import org.apache.openmeetings.db.dao.IDataProviderDao;
|
||||
import org.apache.openmeetings.db.dao.user.UserDao;
|
||||
import org.apache.openmeetings.db.entity.server.LdapConfig;
|
||||
import org.apache.openmeetings.db.util.DaoHelper;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Insert/Update/Delete {@link LdapConfig}
|
||||
*
|
||||
*
|
||||
* @author swagner
|
||||
*
|
||||
*/
|
||||
@Repository
|
||||
@Transactional
|
||||
public class LdapConfigDao implements IDataProviderDao<LdapConfig> {
|
||||
private static final Logger log = LoggerFactory.getLogger(LdapConfigDao.class);
|
||||
private static final List<String> searchFields = List.of("name", "configFileName", "domain", "comment");
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
|
||||
@Override
|
||||
public LdapConfig get(Long id) {
|
||||
return only(em.createNamedQuery("getLdapConfigById", LdapConfig.class)
|
||||
.setParameter("id", id).getResultList());
|
||||
}
|
||||
|
||||
public List<LdapConfig> getActive() {
|
||||
log.debug("getActiveLdapConfigs");
|
||||
|
||||
// get all users
|
||||
TypedQuery<LdapConfig> query = em.createNamedQuery("getActiveLdapConfigs", LdapConfig.class);
|
||||
query.setParameter("isActive", true);
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
public List<LdapConfig> get() {
|
||||
//Add localhost Domain
|
||||
LdapConfig ldapConfig = new LdapConfig();
|
||||
|
||||
ldapConfig.setName("local DB [internal]");
|
||||
ldapConfig.setId(-1L);
|
||||
|
||||
List<LdapConfig> result = new ArrayList<>();
|
||||
result.add(ldapConfig);
|
||||
result.addAll(getActive());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LdapConfig> get(long start, long count) {
|
||||
return setLimits(em.createNamedQuery("getNondeletedLdapConfigs", LdapConfig.class)
|
||||
, start, count).getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LdapConfig> get(String search, long start, long count, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, LdapConfig.class, false, search, searchFields, true, null, sort, start, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
try {
|
||||
TypedQuery<Long> query = em.createNamedQuery("countNondeletedLdapConfigs", Long.class);
|
||||
List<Long> ll = query.getResultList();
|
||||
log.debug("selectMaxFromLdapConfig {}", ll.get(0));
|
||||
return ll.get(0);
|
||||
} catch (Exception ex2) {
|
||||
log.error("[selectMaxFromLdapConfig] ", ex2);
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String search) {
|
||||
return DaoHelper.count(em, LdapConfig.class, search, searchFields, true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LdapConfig update(LdapConfig entity, Long userId) {
|
||||
try {
|
||||
entity.setDeleted(false);
|
||||
if (entity.getId() == null) {
|
||||
if (userId != null) {
|
||||
entity.setInsertedby(userDao.get(userId));
|
||||
}
|
||||
em.persist(entity);
|
||||
} else {
|
||||
if (userId != null) {
|
||||
entity.setUpdatedby(userDao.get(userId));
|
||||
}
|
||||
entity = em.merge(entity);
|
||||
}
|
||||
} catch (PersistenceException ex) {
|
||||
log.error("[update LdapConfig]", ex);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(LdapConfig entity, Long userId) {
|
||||
if (entity.getId() != null) {
|
||||
if (userId != null) {
|
||||
entity.setUpdatedby(userDao.get(userId));
|
||||
}
|
||||
entity.setDeleted(true);
|
||||
em.merge(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.server;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.only;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.isAllowRegisterOauth;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.apache.openmeetings.db.dao.IDataProviderDao;
|
||||
import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
|
||||
import org.apache.openmeetings.db.entity.server.LdapConfig;
|
||||
import org.apache.openmeetings.db.entity.server.OAuthServer;
|
||||
import org.apache.openmeetings.db.util.DaoHelper;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class OAuth2Dao implements IDataProviderDao<OAuthServer> {
|
||||
private static final List<String> searchFields = List.of("name");
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
@Autowired
|
||||
private ConfigurationDao cfgDao;
|
||||
|
||||
public List<OAuthServer> getActive() {
|
||||
if (!isAllowRegisterOauth()) {
|
||||
return List.of();
|
||||
}
|
||||
return em.createNamedQuery("getEnabledOAuthServers", OAuthServer.class)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuthServer get(Long id) {
|
||||
return only(em.createNamedQuery("getOAuthServerById", OAuthServer.class)
|
||||
.setParameter("id", id).getResultList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OAuthServer> get(long start, long count) {
|
||||
return setLimits(em.createNamedQuery("getAllOAuthServers", OAuthServer.class)
|
||||
, start, count).getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OAuthServer> get(String search, long start, long count, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, OAuthServer.class, false, search, searchFields, true, null, sort, start, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return em.createNamedQuery("countOAuthServers", Long.class)
|
||||
.getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String search) {
|
||||
return DaoHelper.count(em, LdapConfig.class, search, searchFields, true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuthServer update(OAuthServer server, Long userId) {
|
||||
if (server.getId() == null) {
|
||||
em.persist(server);
|
||||
} else {
|
||||
server = em.merge(server);
|
||||
}
|
||||
cfgDao.updateCsp();
|
||||
return server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(OAuthServer server, Long userId) {
|
||||
server.setDeleted(true);
|
||||
update(server, userId);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.server;
|
||||
|
||||
import static java.util.UUID.randomUUID;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.only;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.apache.openmeetings.db.dto.room.RoomOptionsDTO;
|
||||
import org.apache.openmeetings.db.entity.server.SOAPLogin;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class SOAPLoginDao {
|
||||
private static final Logger log = LoggerFactory.getLogger(SOAPLoginDao.class);
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
public String addSOAPLogin(String sessionHash, RoomOptionsDTO options) {
|
||||
SOAPLogin soapLogin = new SOAPLogin();
|
||||
soapLogin.setCreated(new Date());
|
||||
soapLogin.setUsed(false);
|
||||
soapLogin.setRoomId(options.getRoomId());
|
||||
soapLogin.setExternalRoomId(options.getExternalRoomId());
|
||||
soapLogin.setExternalType(options.getExternalType());
|
||||
soapLogin.setAllowSameURLMultipleTimes(options.isAllowSameURLMultipleTimes());
|
||||
soapLogin.setHash(randomUUID().toString());
|
||||
soapLogin.setRecordingId(options.getRecordingId());
|
||||
soapLogin.setSessionHash(sessionHash);
|
||||
soapLogin.setModerator(options.isModerator());
|
||||
soapLogin.setShowAudioVideoTest(options.isShowAudioVideoTest());
|
||||
soapLogin.setAllowRecording(options.isAllowRecording());
|
||||
|
||||
em.persist(soapLogin);
|
||||
em.flush();
|
||||
Long soapLoginId = soapLogin.getId();
|
||||
|
||||
if (soapLoginId != null) {
|
||||
return soapLogin.getHash();
|
||||
} else {
|
||||
log.error("[addSOAPLogin]: Could not store SOAPLogin");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public SOAPLogin get(String hash) {
|
||||
if (hash == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
//MSSql find nothing in case SID is passed as-is without wildcarting '%hash%'
|
||||
SOAPLogin sl = only(em.createNamedQuery("getSoapLoginByHash", SOAPLogin.class)
|
||||
.setParameter("hash", '%' + hash + '%')
|
||||
.getResultList());
|
||||
|
||||
if (sl != null) {
|
||||
if (hash.equals(sl.getHash())) {
|
||||
return sl;
|
||||
} else {
|
||||
log.error("[get]: Wrong SOAPLogin was found by hash! {}", hash);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex2) {
|
||||
log.error("[get]: ", ex2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void update(SOAPLogin soapLogin) {
|
||||
try {
|
||||
if (soapLogin.getId() == null) {
|
||||
em.persist(soapLogin);
|
||||
} else {
|
||||
if (!em.contains(soapLogin)) {
|
||||
em.merge(soapLogin);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex2) {
|
||||
log.error("[update]: ", ex2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.server;
|
||||
|
||||
import static java.util.UUID.randomUUID;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.apache.openmeetings.db.entity.server.Sessiondata;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author swagner This Class handles all session management
|
||||
*
|
||||
*/
|
||||
@Repository
|
||||
@Transactional
|
||||
public class SessiondataDao {
|
||||
private static final Logger log = LoggerFactory.getLogger(SessiondataDao.class);
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
private static Sessiondata newInstance() {
|
||||
log.debug("startsession :: startsession");
|
||||
|
||||
Sessiondata sd = new Sessiondata();
|
||||
sd.setSessionId(randomUUID().toString());
|
||||
sd.setCreated(new Date());
|
||||
sd.setUserId(null);
|
||||
|
||||
return sd;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a new session-object in the database
|
||||
*
|
||||
* @param userId the id of the user to be set on this session
|
||||
* @param languageId language id to be set on this session
|
||||
* @return newly create {@link Sessiondata}
|
||||
*/
|
||||
public Sessiondata create(Long userId, long languageId) {
|
||||
return create(userId, null, languageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a new session-object in the database
|
||||
*
|
||||
* @param userId the id of the user to be set on this session
|
||||
* @param roomId the id of the room to be set on this session
|
||||
* @param languageId language id to be set on this session
|
||||
* @return newly create {@link Sessiondata}
|
||||
*/
|
||||
public Sessiondata create(Long userId, Long roomId, long languageId) {
|
||||
log.debug("create :: create");
|
||||
Sessiondata sd = newInstance();
|
||||
sd.setUserId(userId);
|
||||
sd.setRoomId(roomId);
|
||||
sd.setLanguageId(languageId);
|
||||
return update(sd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serches {@link Sessiondata} object by sessionId
|
||||
*
|
||||
* @param sid - sessionId
|
||||
* @return {@link Sessiondata} with sessionId == SID, or null if not found
|
||||
*/
|
||||
public Sessiondata find(String sid) {
|
||||
if (sid == null) {
|
||||
return null;
|
||||
}
|
||||
//MSSql find nothing in case SID is passed as-is without wildcarting '%SID%'
|
||||
List<Sessiondata> sessions = em.createNamedQuery("getSessionById", Sessiondata.class)
|
||||
.setParameter("sessionId", String.format("%%%s%%", sid)).getResultList();
|
||||
|
||||
if (sessions.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Sessiondata sd = sessions.get(0);
|
||||
if (sd == null || sd.getUserId() == null || sd.getUserId().equals(Long.valueOf(0)) || !sid.equals(sd.getSessionId())) {
|
||||
return null;
|
||||
}
|
||||
return sd;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sid - sid of {@link Sessiondata} to check
|
||||
* @return - {@link Sessiondata} for given sid or new {@link Sessiondata}
|
||||
*/
|
||||
public Sessiondata check(String sid) {
|
||||
Sessiondata sd = find(sid);
|
||||
// Checks if wether the Session or the User Object of that Session is set yet
|
||||
if (sd == null) {
|
||||
return newInstance();
|
||||
}
|
||||
return update(sd);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param refreshed - date to compare session update time with
|
||||
* @return - the list of all expired session data
|
||||
*/
|
||||
private List<Sessiondata> getSessionToDelete(Date refreshed) {
|
||||
return em.createNamedQuery("getSessionToDelete", Sessiondata.class)
|
||||
.setParameter("refreshed", refreshed)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param timeout - timeout in millis to check expired sessions
|
||||
*/
|
||||
public void clearSessionTable(long timeout) {
|
||||
try {
|
||||
log.trace("****** clearSessionTable: ");
|
||||
List<Sessiondata> l = getSessionToDelete(new Date(System.currentTimeMillis() - timeout));
|
||||
if (!l.isEmpty()) {
|
||||
log.debug("clearSessionTable: {}", l.size());
|
||||
for (Sessiondata sData : l) {
|
||||
sData = em.find(Sessiondata.class, sData.getId());
|
||||
em.remove(sData);
|
||||
}
|
||||
}
|
||||
} catch (Exception err) {
|
||||
log.error("clearSessionTable", err);
|
||||
}
|
||||
}
|
||||
|
||||
public Sessiondata update(Sessiondata sd) {
|
||||
sd.setRefreshed(new Date());
|
||||
|
||||
if (sd.getId() == null) {
|
||||
em.persist(sd);
|
||||
} else {
|
||||
sd = em.merge(sd);
|
||||
}
|
||||
return sd;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.user;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.getRoot;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.only;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.apache.openmeetings.db.dao.IGroupAdminDataProviderDao;
|
||||
import org.apache.openmeetings.db.entity.user.Group;
|
||||
import org.apache.openmeetings.db.entity.user.GroupUser;
|
||||
import org.apache.openmeetings.db.util.DaoHelper;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class GroupDao implements IGroupAdminDataProviderDao<Group> {
|
||||
private static final List<String> searchFields = List.of("name");
|
||||
private static final List<String> guSearchFields = List.copyOf(searchFields.stream().map(n -> "group." + n).toList());
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Override
|
||||
public Group get(Long id) {
|
||||
return only(em.createNamedQuery("getGroupById", Group.class)
|
||||
.setParameter("id", id).getResultList());
|
||||
}
|
||||
|
||||
public Group get(String name) {
|
||||
List<Group> groups = em.createNamedQuery("getGroupByName", Group.class).setParameter("name", name).getResultList();
|
||||
return groups == null || groups.isEmpty() ? null : groups.get(0);
|
||||
}
|
||||
|
||||
public Group getExternal(String name) {
|
||||
List<Group> groups = em.createNamedQuery("getExtGroupByName", Group.class).setParameter("name", name).getResultList();
|
||||
Group g = groups == null || groups.isEmpty() ? null : groups.get(0);
|
||||
if (g == null) {
|
||||
g = update(new Group().setExternal(true).setName(name), null);
|
||||
}
|
||||
return g;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Group> get(long start, long count) {
|
||||
return setLimits(em.createNamedQuery("getNondeletedGroups", Group.class), start, count)
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Group> get(String search, long start, long count, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, Group.class, false, search, searchFields, true
|
||||
, null
|
||||
, sort, start, count);
|
||||
}
|
||||
|
||||
private Predicate getAdminFilter(Long adminId, CriteriaBuilder builder, CriteriaQuery<?> query) {
|
||||
Root<GroupUser> root = getRoot(query, GroupUser.class);
|
||||
return builder.and(builder.equal(root.get("user").get("id"), adminId)
|
||||
, builder.isTrue(root.get("moderator")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Group> adminGet(String search, Long adminId, long start, long count, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, GroupUser.class, Group.class
|
||||
, (builder, root) -> root.get("group")
|
||||
, true, search, guSearchFields, true
|
||||
, (b, q) -> getAdminFilter(adminId, b, q)
|
||||
, sort, start, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return em.createNamedQuery("countGroups", Long.class).getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String search) {
|
||||
return DaoHelper.count(em, Group.class, search, searchFields, true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long adminCount(String search, Long adminId) {
|
||||
return DaoHelper.count(em, GroupUser.class
|
||||
, (builder, root) -> builder.countDistinct(root.get("group"))
|
||||
, search, guSearchFields, false
|
||||
, (b, q) -> getAdminFilter(adminId, b, q.distinct(true)));
|
||||
}
|
||||
|
||||
public List<Group> get(Collection<Long> ids) {
|
||||
return em.createNamedQuery("getGroupsByIds", Group.class).setParameter("ids", ids).getResultList();
|
||||
}
|
||||
|
||||
public List<Group> getLimited() {
|
||||
return em.createNamedQuery("getLimitedGroups", Group.class).getResultList();
|
||||
}
|
||||
|
||||
public List<Group> getGroupsForUserNotifications() {
|
||||
return em.createNamedQuery("getGroupsForUserNotifications", Group.class).getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group update(Group entity, Long userId) {
|
||||
if (entity.getId() == null) {
|
||||
if (userId != null) {
|
||||
entity.setInsertedby(userId);
|
||||
}
|
||||
em.persist(entity);
|
||||
} else {
|
||||
if (userId != null) {
|
||||
entity.setUpdatedby(userId);
|
||||
}
|
||||
entity = em.merge(entity);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Group g, Long userId) {
|
||||
em.createNamedQuery("deleteGroupUsersByGroup").setParameter("id", g.getId()).executeUpdate();
|
||||
|
||||
g.setDeleted(true);
|
||||
if (userId != null) {
|
||||
g.setUpdatedby(userId);
|
||||
}
|
||||
em.merge(g);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.user;
|
||||
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.UNSUPPORTED;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.getRoot;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.setLimits;
|
||||
import static org.apache.openmeetings.db.util.DaoHelper.single;
|
||||
import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_USER_ID;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.apache.openmeetings.db.dao.IDataProviderDao;
|
||||
import org.apache.openmeetings.db.entity.user.GroupUser;
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
import org.apache.openmeetings.db.util.DaoHelper;
|
||||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Repository
|
||||
@Transactional
|
||||
public class GroupUserDao implements IDataProviderDao<GroupUser> {
|
||||
private static final List<String> searchFields = List.of("user.lastname", "user.firstname", "user.login", "user.address.email");
|
||||
private static final String PARAM_GROUPID = "groupId";
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@Override
|
||||
public GroupUser get(Long id) {
|
||||
return em.createNamedQuery("getGroupUsersById", GroupUser.class)
|
||||
.setParameter("id", id)
|
||||
.getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupUser> get(long start, long count) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupUser> get(String search, long start, long count, SortParam<String> sort) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
private Predicate getGroupFilter(Long groupId, CriteriaBuilder builder, CriteriaQuery<?> query) {
|
||||
Root<GroupUser> root = getRoot(query, GroupUser.class);
|
||||
return builder.equal(root.get("group").get("id"), groupId);
|
||||
}
|
||||
|
||||
public List<GroupUser> get(long groupId, String search, long start, long count, SortParam<String> sort) {
|
||||
return DaoHelper.get(em, GroupUser.class, false, search, searchFields, false
|
||||
, (builder, query) -> getGroupFilter(groupId, builder, query)
|
||||
, sort, start, count);
|
||||
}
|
||||
|
||||
public List<GroupUser> get(long groupId, long start, long count) {
|
||||
return setLimits(
|
||||
em.createNamedQuery("getGroupUsersByGroupId", GroupUser.class).setParameter("id", groupId)
|
||||
, start, count).getResultList();
|
||||
}
|
||||
|
||||
public GroupUser getByGroupAndUser(Long groupId, Long userId) {
|
||||
return single(em.createNamedQuery("isUserInGroup", GroupUser.class)
|
||||
.setParameter(PARAM_GROUPID, groupId)
|
||||
.setParameter(PARAM_USER_ID, userId)
|
||||
.getResultList());
|
||||
}
|
||||
|
||||
public boolean isUserInGroup(long groupId, long userId) {
|
||||
return !em.createNamedQuery("isUserInGroup", GroupUser.class)
|
||||
.setParameter(PARAM_GROUPID, groupId).setParameter(PARAM_USER_ID, userId)
|
||||
.getResultList()
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(String search) {
|
||||
return DaoHelper.count(em, GroupUser.class, search, searchFields, false, null);
|
||||
}
|
||||
|
||||
public long count(long groupId) {
|
||||
return em.createNamedQuery("countGroupUsers", Long.class)
|
||||
.setParameter("id", groupId)
|
||||
.getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupUser update(GroupUser entity, Long userId) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(GroupUser entity, Long userId) {
|
||||
throw UNSUPPORTED;
|
||||
}
|
||||
|
||||
public long getGroupUserCountAddedAfter(Long id, Date date) {
|
||||
return em.createNamedQuery("getGroupUserCountAddedAfter", Long.class)
|
||||
.setParameter("id", id)
|
||||
.setParameter("inserted", date)
|
||||
.getSingleResult();
|
||||
}
|
||||
|
||||
public List<User> getGroupModerators(Long id) {
|
||||
return em.createNamedQuery("getGroupModerators", User.class)
|
||||
.setParameter("id", id)
|
||||
.getResultList();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License") + you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openmeetings.db.dao.user;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.openmeetings.db.entity.user.User;
|
||||
import org.apache.openmeetings.util.OmException;
|
||||
|
||||
//HACK to bypass cross project compilation
|
||||
public interface IUserManager {
|
||||
Object registerUser(String login, String userpass, String lastname,
|
||||
String firstname, String email, String country,
|
||||
long languageId, String tzId);
|
||||
|
||||
Object registerUser(User u, String password, String hash) throws OmException, NoSuchAlgorithmException;
|
||||
|
||||
Long getLanguage(Locale loc);
|
||||
|
||||
boolean kickExternal(Long roomId, String externalType, String externalId);
|
||||
boolean kickUsersByRoomId(Long roomId);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue