mirror of https://github.com/OpenVidu/openvidu.git
openvidu-sample-app moved to its own repo (OpenVidu/classroom-demo)
parent
7310f9279e
commit
9e90367297
|
@ -1,255 +0,0 @@
|
||||||
## Ignore Visual Studio temporary files, build results, and
|
|
||||||
## files generated by popular Visual Studio add-ons.
|
|
||||||
|
|
||||||
target/*
|
|
||||||
src/main/resources/static/*
|
|
||||||
|
|
||||||
# User-specific files
|
|
||||||
*.suo
|
|
||||||
*.user
|
|
||||||
*.userosscache
|
|
||||||
*.sln.docstates
|
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
|
||||||
*.userprefs
|
|
||||||
|
|
||||||
# Build results
|
|
||||||
[Dd]ebug/
|
|
||||||
[Dd]ebugPublic/
|
|
||||||
[Rr]elease/
|
|
||||||
[Rr]eleases/
|
|
||||||
x64/
|
|
||||||
x86/
|
|
||||||
bld/
|
|
||||||
[Bb]in/
|
|
||||||
[Oo]bj/
|
|
||||||
[Ll]og/
|
|
||||||
|
|
||||||
# Visual Studio 2015 cache/options directory
|
|
||||||
.vs/
|
|
||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
|
||||||
#wwwroot/
|
|
||||||
|
|
||||||
# MSTest test Results
|
|
||||||
[Tt]est[Rr]esult*/
|
|
||||||
[Bb]uild[Ll]og.*
|
|
||||||
|
|
||||||
# NUNIT
|
|
||||||
*.VisualState.xml
|
|
||||||
TestResult.xml
|
|
||||||
|
|
||||||
# Build Results of an ATL Project
|
|
||||||
[Dd]ebugPS/
|
|
||||||
[Rr]eleasePS/
|
|
||||||
dlldata.c
|
|
||||||
|
|
||||||
# DNX
|
|
||||||
project.lock.json
|
|
||||||
artifacts/
|
|
||||||
|
|
||||||
*_i.c
|
|
||||||
*_p.c
|
|
||||||
*_i.h
|
|
||||||
*.ilk
|
|
||||||
*.meta
|
|
||||||
*.obj
|
|
||||||
*.pch
|
|
||||||
*.pdb
|
|
||||||
*.pgc
|
|
||||||
*.pgd
|
|
||||||
*.rsp
|
|
||||||
*.sbr
|
|
||||||
*.tlb
|
|
||||||
*.tli
|
|
||||||
*.tlh
|
|
||||||
*.tmp
|
|
||||||
*.tmp_proj
|
|
||||||
*.log
|
|
||||||
*.vspscc
|
|
||||||
*.vssscc
|
|
||||||
.builds
|
|
||||||
*.pidb
|
|
||||||
*.svclog
|
|
||||||
*.scc
|
|
||||||
|
|
||||||
# Chutzpah Test files
|
|
||||||
_Chutzpah*
|
|
||||||
|
|
||||||
# Visual C++ cache files
|
|
||||||
ipch/
|
|
||||||
*.aps
|
|
||||||
*.ncb
|
|
||||||
*.opendb
|
|
||||||
*.opensdf
|
|
||||||
*.sdf
|
|
||||||
*.cachefile
|
|
||||||
*.VC.db
|
|
||||||
*.VC.VC.opendb
|
|
||||||
|
|
||||||
# Visual Studio profiler
|
|
||||||
*.psess
|
|
||||||
*.vsp
|
|
||||||
*.vspx
|
|
||||||
*.sap
|
|
||||||
|
|
||||||
# TFS 2012 Local Workspace
|
|
||||||
$tf/
|
|
||||||
|
|
||||||
# Guidance Automation Toolkit
|
|
||||||
*.gpState
|
|
||||||
|
|
||||||
# ReSharper is a .NET coding add-in
|
|
||||||
_ReSharper*/
|
|
||||||
*.[Rr]e[Ss]harper
|
|
||||||
*.DotSettings.user
|
|
||||||
|
|
||||||
# JustCode is a .NET coding add-in
|
|
||||||
.JustCode
|
|
||||||
|
|
||||||
# TeamCity is a build add-in
|
|
||||||
_TeamCity*
|
|
||||||
|
|
||||||
# DotCover is a Code Coverage Tool
|
|
||||||
*.dotCover
|
|
||||||
|
|
||||||
# NCrunch
|
|
||||||
_NCrunch_*
|
|
||||||
.*crunch*.local.xml
|
|
||||||
nCrunchTemp_*
|
|
||||||
|
|
||||||
# MightyMoose
|
|
||||||
*.mm.*
|
|
||||||
AutoTest.Net/
|
|
||||||
|
|
||||||
# Web workbench (sass)
|
|
||||||
.sass-cache/
|
|
||||||
|
|
||||||
# Installshield output folder
|
|
||||||
[Ee]xpress/
|
|
||||||
|
|
||||||
# DocProject is a documentation generator add-in
|
|
||||||
DocProject/buildhelp/
|
|
||||||
DocProject/Help/*.HxT
|
|
||||||
DocProject/Help/*.HxC
|
|
||||||
DocProject/Help/*.hhc
|
|
||||||
DocProject/Help/*.hhk
|
|
||||||
DocProject/Help/*.hhp
|
|
||||||
DocProject/Help/Html2
|
|
||||||
DocProject/Help/html
|
|
||||||
|
|
||||||
# Click-Once directory
|
|
||||||
publish/
|
|
||||||
|
|
||||||
# Publish Web Output
|
|
||||||
*.[Pp]ublish.xml
|
|
||||||
*.azurePubxml
|
|
||||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
|
||||||
# but database connection strings (with potential passwords) will be unencrypted
|
|
||||||
*.pubxml
|
|
||||||
*.publishproj
|
|
||||||
|
|
||||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
|
||||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
|
||||||
# in these scripts will be unencrypted
|
|
||||||
PublishScripts/
|
|
||||||
|
|
||||||
# NuGet Packages
|
|
||||||
*.nupkg
|
|
||||||
# The packages folder can be ignored because of Package Restore
|
|
||||||
**/packages/*
|
|
||||||
# except build/, which is used as an MSBuild target.
|
|
||||||
!**/packages/build/
|
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
|
||||||
#!**/packages/repositories.config
|
|
||||||
# NuGet v3's project.json files produces more ignoreable files
|
|
||||||
*.nuget.props
|
|
||||||
*.nuget.targets
|
|
||||||
|
|
||||||
# Microsoft Azure Build Output
|
|
||||||
csx/
|
|
||||||
*.build.csdef
|
|
||||||
|
|
||||||
# Microsoft Azure Emulator
|
|
||||||
ecf/
|
|
||||||
rcf/
|
|
||||||
|
|
||||||
# Windows Store app package directories and files
|
|
||||||
AppPackages/
|
|
||||||
BundleArtifacts/
|
|
||||||
Package.StoreAssociation.xml
|
|
||||||
_pkginfo.txt
|
|
||||||
|
|
||||||
# Visual Studio cache files
|
|
||||||
# files ending in .cache can be ignored
|
|
||||||
*.[Cc]ache
|
|
||||||
# but keep track of directories ending in .cache
|
|
||||||
!*.[Cc]ache/
|
|
||||||
|
|
||||||
# Others
|
|
||||||
ClientBin/
|
|
||||||
~$*
|
|
||||||
*~
|
|
||||||
*.dbmdl
|
|
||||||
*.dbproj.schemaview
|
|
||||||
*.pfx
|
|
||||||
*.publishsettings
|
|
||||||
node_modules/
|
|
||||||
orleans.codegen.cs
|
|
||||||
|
|
||||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
|
||||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
|
||||||
#bower_components/
|
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
|
||||||
Generated_Code/
|
|
||||||
|
|
||||||
# Backup & report files from converting an old project file
|
|
||||||
# to a newer Visual Studio version. Backup files are not needed,
|
|
||||||
# because we have git ;-)
|
|
||||||
_UpgradeReport_Files/
|
|
||||||
Backup*/
|
|
||||||
UpgradeLog*.XML
|
|
||||||
UpgradeLog*.htm
|
|
||||||
|
|
||||||
# SQL Server files
|
|
||||||
*.mdf
|
|
||||||
*.ldf
|
|
||||||
|
|
||||||
# Business Intelligence projects
|
|
||||||
*.rdl.data
|
|
||||||
*.bim.layout
|
|
||||||
*.bim_*.settings
|
|
||||||
|
|
||||||
# Microsoft Fakes
|
|
||||||
FakesAssemblies/
|
|
||||||
|
|
||||||
# GhostDoc plugin setting file
|
|
||||||
*.GhostDoc.xml
|
|
||||||
|
|
||||||
# Node.js Tools for Visual Studio
|
|
||||||
.ntvs_analysis.dat
|
|
||||||
|
|
||||||
# Visual Studio 6 build log
|
|
||||||
*.plg
|
|
||||||
|
|
||||||
# Visual Studio 6 workspace options file
|
|
||||||
*.opt
|
|
||||||
|
|
||||||
# Visual Studio LightSwitch build output
|
|
||||||
**/*.HTMLClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/ModelManifest.xml
|
|
||||||
**/*.Server/GeneratedArtifacts
|
|
||||||
**/*.Server/ModelManifest.xml
|
|
||||||
_Pvt_Extensions
|
|
||||||
|
|
||||||
# Paket dependency manager
|
|
||||||
.paket/paket.exe
|
|
||||||
paket-files/
|
|
||||||
|
|
||||||
# FAKE - F# Make
|
|
||||||
.fake/
|
|
||||||
|
|
||||||
# JetBrains Rider
|
|
||||||
.idea/
|
|
||||||
*.sln.iml
|
|
|
@ -1,201 +0,0 @@
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright {yyyy} {name of copyright owner}
|
|
||||||
|
|
||||||
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.
|
|
|
@ -1,147 +0,0 @@
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<groupId>io.openvidu</groupId>
|
|
||||||
<artifactId>openvidu-sample-app</artifactId>
|
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
|
||||||
<packaging>war</packaging>
|
|
||||||
|
|
||||||
<name>openvidu-sample-app</name>
|
|
||||||
<url>http://maven.apache.org</url>
|
|
||||||
|
|
||||||
<parent>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
|
||||||
<version>1.4.1.RELEASE</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
<java.version>1.8</java.version>
|
|
||||||
<start-class>io.openvidu.sample.app.App</start-class>
|
|
||||||
<docker.image.prefix>openvidu</docker.image.prefix>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
|
|
||||||
<finalName>${project.artifactId}-${project.version}</finalName>
|
|
||||||
|
|
||||||
<plugins>
|
|
||||||
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework</groupId>
|
|
||||||
<artifactId>springloaded</artifactId>
|
|
||||||
<version>1.2.6.RELEASE</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
|
||||||
<artifactId>exec-maven-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<mainClass>${start-class}</mainClass>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-war-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<webResources>
|
|
||||||
<resource>
|
|
||||||
<directory>src/main/ebextensions</directory>
|
|
||||||
<targetPath>.ebextensions</targetPath>
|
|
||||||
<filtering>true</filtering>
|
|
||||||
</resource>
|
|
||||||
</webResources>
|
|
||||||
<packagingExcludes>src/main/resources/frontend/**</packagingExcludes>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
<plugin>
|
|
||||||
<groupId>com.spotify</groupId>
|
|
||||||
<artifactId>docker-maven-plugin</artifactId>
|
|
||||||
<version>0.2.3</version>
|
|
||||||
<configuration>
|
|
||||||
<imageName>${docker.image.prefix}/${project.artifactId}</imageName>
|
|
||||||
<dockerDirectory>src/main/docker</dockerDirectory>
|
|
||||||
<resources>
|
|
||||||
<resource>
|
|
||||||
<targetPath>/</targetPath>
|
|
||||||
<directory>${project.build.directory}</directory>
|
|
||||||
<include>${project.build.finalName}.war</include>
|
|
||||||
</resource>
|
|
||||||
</resources>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-security</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-devtools</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>mysql</groupId>
|
|
||||||
<artifactId>mysql-connector-java</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>commons-validator</groupId>
|
|
||||||
<artifactId>commons-validator</artifactId>
|
|
||||||
<version>1.5.1</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.googlecode.json-simple</groupId>
|
|
||||||
<artifactId>json-simple</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
|
||||||
<artifactId>httpclient</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.openvidu</groupId>
|
|
||||||
<artifactId>openvidu-java-client</artifactId>
|
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
</dependencies>
|
|
||||||
</project>
|
|
|
@ -1,6 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
cd frontend
|
|
||||||
ng build --output-path ../../main/resources/static
|
|
||||||
cd ../../../
|
|
||||||
mvn clean package
|
|
||||||
java -jar target/openvidu-sample-app-0.0.1-SNAPSHOT.war
|
|
|
@ -1,57 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
|
||||||
"project": {
|
|
||||||
"name": "openvidu-sample-app"
|
|
||||||
},
|
|
||||||
"apps": [
|
|
||||||
{
|
|
||||||
"root": "src",
|
|
||||||
"outDir": "dist",
|
|
||||||
"assets": [
|
|
||||||
"assets",
|
|
||||||
"favicon.ico"
|
|
||||||
],
|
|
||||||
"index": "index.html",
|
|
||||||
"main": "main.ts",
|
|
||||||
"polyfills": "polyfills.ts",
|
|
||||||
"test": "test.ts",
|
|
||||||
"tsconfig": "tsconfig.app.json",
|
|
||||||
"testTsconfig": "tsconfig.spec.json",
|
|
||||||
"prefix": "app",
|
|
||||||
"styles": [
|
|
||||||
"styles.css"
|
|
||||||
],
|
|
||||||
"scripts": [],
|
|
||||||
"environmentSource": "environments/environment.ts",
|
|
||||||
"environments": {
|
|
||||||
"dev": "environments/environment.ts",
|
|
||||||
"prod": "environments/environment.prod.ts"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"e2e": {
|
|
||||||
"protractor": {
|
|
||||||
"config": "./protractor.conf.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": [
|
|
||||||
{
|
|
||||||
"project": "src/tsconfig.app.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"project": "src/tsconfig.spec.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"project": "e2e/tsconfig.e2e.json"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"test": {
|
|
||||||
"karma": {
|
|
||||||
"config": "./karma.conf.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaults": {
|
|
||||||
"styleExt": "css",
|
|
||||||
"component": {}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
# Editor configuration, see http://editorconfig.org
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
max_line_length = off
|
|
||||||
trim_trailing_whitespace = false
|
|
|
@ -1,42 +0,0 @@
|
||||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# compiled output
|
|
||||||
/dist
|
|
||||||
/tmp
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
|
|
||||||
# IDEs and editors
|
|
||||||
/.idea
|
|
||||||
.project
|
|
||||||
.classpath
|
|
||||||
.c9/
|
|
||||||
*.launch
|
|
||||||
.settings/
|
|
||||||
*.sublime-workspace
|
|
||||||
|
|
||||||
# IDE - VSCode
|
|
||||||
.vscode/
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/settings.json
|
|
||||||
!.vscode/tasks.json
|
|
||||||
!.vscode/launch.json
|
|
||||||
!.vscode/extensions.json
|
|
||||||
|
|
||||||
# misc
|
|
||||||
/.sass-cache
|
|
||||||
/connect.lock
|
|
||||||
/coverage/*
|
|
||||||
/libpeerconnection.log
|
|
||||||
npm-debug.log
|
|
||||||
testem.log
|
|
||||||
/typings
|
|
||||||
|
|
||||||
# e2e
|
|
||||||
/e2e/*.js
|
|
||||||
/e2e/*.map
|
|
||||||
|
|
||||||
#System Files
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
|
@ -1,27 +0,0 @@
|
||||||
# OpenviduSampleApp
|
|
||||||
|
|
||||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.0.0-rc.1.
|
|
||||||
|
|
||||||
## Development server
|
|
||||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
|
||||||
|
|
||||||
## Code scaffolding
|
|
||||||
|
|
||||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`.
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
|
|
||||||
|
|
||||||
## Running unit tests
|
|
||||||
|
|
||||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
|
||||||
|
|
||||||
## Running end-to-end tests
|
|
||||||
|
|
||||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
|
||||||
Before running the tests make sure you are serving the app via `ng serve`.
|
|
||||||
|
|
||||||
## Further help
|
|
||||||
|
|
||||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
|
|
@ -1,14 +0,0 @@
|
||||||
import { OpenviduSampleAppPage } from './app.po';
|
|
||||||
|
|
||||||
describe('openvidu-sample-app App', () => {
|
|
||||||
let page: OpenviduSampleAppPage;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
page = new OpenviduSampleAppPage();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display message saying app works', () => {
|
|
||||||
page.navigateTo();
|
|
||||||
expect(page.getParagraphText()).toEqual('app works!');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { browser, element, by } from 'protractor';
|
|
||||||
|
|
||||||
export class OpenviduSampleAppPage {
|
|
||||||
navigateTo() {
|
|
||||||
return browser.get('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
getParagraphText() {
|
|
||||||
return element(by.css('app-root h1')).getText();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"sourceMap": true,
|
|
||||||
"declaration": false,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"lib": [
|
|
||||||
"es2016"
|
|
||||||
],
|
|
||||||
"outDir": "../dist/out-tsc-e2e",
|
|
||||||
"module": "commonjs",
|
|
||||||
"target": "es6",
|
|
||||||
"types":[
|
|
||||||
"jasmine",
|
|
||||||
"node"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
// Karma configuration file, see link for more information
|
|
||||||
// https://karma-runner.github.io/0.13/config/configuration-file.html
|
|
||||||
|
|
||||||
module.exports = function (config) {
|
|
||||||
config.set({
|
|
||||||
basePath: '',
|
|
||||||
frameworks: ['jasmine', '@angular/cli'],
|
|
||||||
plugins: [
|
|
||||||
require('karma-jasmine'),
|
|
||||||
require('karma-chrome-launcher'),
|
|
||||||
require('karma-jasmine-html-reporter'),
|
|
||||||
require('karma-coverage-istanbul-reporter'),
|
|
||||||
require('@angular/cli/plugins/karma')
|
|
||||||
],
|
|
||||||
client:{
|
|
||||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
|
||||||
},
|
|
||||||
files: [
|
|
||||||
{ pattern: './src/test.ts', watched: false }
|
|
||||||
],
|
|
||||||
preprocessors: {
|
|
||||||
'./src/test.ts': ['@angular/cli']
|
|
||||||
},
|
|
||||||
mime: {
|
|
||||||
'text/x-typescript': ['ts','tsx']
|
|
||||||
},
|
|
||||||
coverageIstanbulReporter: {
|
|
||||||
reports: [ 'html', 'lcovonly' ],
|
|
||||||
fixWebpackSourcePaths: true
|
|
||||||
},
|
|
||||||
angularCli: {
|
|
||||||
environment: 'dev'
|
|
||||||
},
|
|
||||||
reporters: config.angularCli && config.angularCli.codeCoverage
|
|
||||||
? ['progress', 'coverage-istanbul']
|
|
||||||
: ['progress', 'kjhtml'],
|
|
||||||
port: 9876,
|
|
||||||
colors: true,
|
|
||||||
logLevel: config.LOG_INFO,
|
|
||||||
autoWatch: true,
|
|
||||||
browsers: ['Chrome'],
|
|
||||||
singleRun: false
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,52 +0,0 @@
|
||||||
{
|
|
||||||
"name": "openvidu-sample-app",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"license": "MIT",
|
|
||||||
"author": "Pablo Fuente",
|
|
||||||
"scripts": {
|
|
||||||
"ng": "ng",
|
|
||||||
"start": "ng serve",
|
|
||||||
"build": "ng build",
|
|
||||||
"test": "ng test",
|
|
||||||
"lint": "ng lint",
|
|
||||||
"e2e": "ng e2e"
|
|
||||||
},
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@angular/common": "^2.4.0",
|
|
||||||
"@angular/compiler": "^2.4.0",
|
|
||||||
"@angular/core": "^2.4.0",
|
|
||||||
"@angular/flex-layout": "^2.0.0-rc.1",
|
|
||||||
"@angular/forms": "^2.4.0",
|
|
||||||
"@angular/http": "^2.4.0",
|
|
||||||
"@angular/material": "2.0.0-beta.2",
|
|
||||||
"@angular/platform-browser": "^2.4.0",
|
|
||||||
"@angular/platform-browser-dynamic": "^2.4.0",
|
|
||||||
"@angular/router": "^3.4.0",
|
|
||||||
"core-js": "^2.4.1",
|
|
||||||
"hammerjs": "^2.0.8",
|
|
||||||
"jquery": "^2.2.4",
|
|
||||||
"openvidu-browser": "1.0.2",
|
|
||||||
"rxjs": "^5.1.0",
|
|
||||||
"zone.js": "^0.7.6"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@angular/cli": "1.0.0-rc.1",
|
|
||||||
"@angular/compiler-cli": "^2.4.0",
|
|
||||||
"@types/jasmine": "2.5.38",
|
|
||||||
"@types/node": "~6.0.60",
|
|
||||||
"codelyzer": "~2.0.0",
|
|
||||||
"jasmine-core": "~2.5.2",
|
|
||||||
"jasmine-spec-reporter": "~3.2.0",
|
|
||||||
"karma": "~1.4.1",
|
|
||||||
"karma-chrome-launcher": "~2.0.0",
|
|
||||||
"karma-cli": "~1.0.1",
|
|
||||||
"karma-jasmine": "~1.1.0",
|
|
||||||
"karma-jasmine-html-reporter": "^0.2.2",
|
|
||||||
"karma-coverage-istanbul-reporter": "^0.2.0",
|
|
||||||
"protractor": "~5.1.0",
|
|
||||||
"ts-node": "~2.0.0",
|
|
||||||
"tslint": "~4.4.2",
|
|
||||||
"typescript": "~2.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
// Protractor configuration file, see link for more information
|
|
||||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
|
||||||
|
|
||||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
|
||||||
|
|
||||||
exports.config = {
|
|
||||||
allScriptsTimeout: 11000,
|
|
||||||
specs: [
|
|
||||||
'./e2e/**/*.e2e-spec.ts'
|
|
||||||
],
|
|
||||||
capabilities: {
|
|
||||||
'browserName': 'chrome'
|
|
||||||
},
|
|
||||||
directConnect: true,
|
|
||||||
baseUrl: 'http://localhost:4200/',
|
|
||||||
framework: 'jasmine',
|
|
||||||
jasmineNodeOpts: {
|
|
||||||
showColors: true,
|
|
||||||
defaultTimeoutInterval: 30000,
|
|
||||||
print: function() {}
|
|
||||||
},
|
|
||||||
beforeLaunch: function() {
|
|
||||||
require('ts-node').register({
|
|
||||||
project: 'e2e/tsconfig.e2e.json'
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onPrepare() {
|
|
||||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,31 +0,0 @@
|
||||||
md-sidenav {
|
|
||||||
width: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
md-sidenav-container {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer.page-footer {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer h2 {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidenav-button {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .fill-remaining-space {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
header #navbar-logo {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer ul {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
<md-sidenav-container>
|
|
||||||
|
|
||||||
<md-sidenav #sidenav>
|
|
||||||
<button md-button (click)="router.navigate(['/lessons']); sidenav.close()" class="sidenav-button">Lessons</button>
|
|
||||||
<button md-button (click)="router.navigate(['/profile']); sidenav.close()" class="sidenav-button">Profile</button>
|
|
||||||
<button md-button (click)="sidenav.close(); authenticationService.directLogOut()" class="sidenav-button">Logout</button>
|
|
||||||
</md-sidenav>
|
|
||||||
|
|
||||||
<header *ngIf="!isVideoSessionUrl()">
|
|
||||||
<md-toolbar color="primary" class="mat-elevation-z6">
|
|
||||||
<button md-button routerLink="/" id="navbar-logo">
|
|
||||||
OpenVidu Sample App
|
|
||||||
</button>
|
|
||||||
<span class="fill-remaining-space"></span>
|
|
||||||
<div *ngIf="authenticationService.isLoggedIn()" fxLayout="row" fxShow="false" fxShow.gt-sm>
|
|
||||||
<button md-button routerLink="/lessons">Lessons</button>
|
|
||||||
<button md-button routerLink="/profile">Profile</button>
|
|
||||||
<button md-button (click)="authenticationService.directLogOut()">LOGOUT</button>
|
|
||||||
</div>
|
|
||||||
<button *ngIf="authenticationService.isLoggedIn()" md-button fxHide="false" fxHide.gt-sm (click)="sidenav.open()">
|
|
||||||
<md-icon>menu</md-icon>
|
|
||||||
</button>
|
|
||||||
</md-toolbar>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer *ngIf="!isVideoSessionUrl()" class="page-footer back-primary color-secondary mat-elevation-z5">
|
|
||||||
<div class="container">
|
|
||||||
<div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="start start" fxLayoutAlign.xs="start">
|
|
||||||
<div fxFlex="50%" fxFlex.xs="100%">
|
|
||||||
<h2>This is a sample application</h2>
|
|
||||||
<p class="grey-text text-lighten-4">Implementing a secure real time app with OpenVidu</p>
|
|
||||||
</div>
|
|
||||||
<div fxFlex="50%" fxFlex.xs="100%">
|
|
||||||
<div fxLayout="row" fxLayoutAlign="end start" fxLayoutAlign.xs="start">
|
|
||||||
<div fxFlex="50%">
|
|
||||||
<h2>Technologies</h2>
|
|
||||||
<ul>
|
|
||||||
<li><a class="hover-link" href="https://angular.io/" target="_blank">Angular</a></li>
|
|
||||||
<li><a class="hover-link" href="https://material.angular.io/" target="_blank">Angular Material</a></li>
|
|
||||||
<li><a class="hover-link" href="https://spring.io/" target="_blank">Spring Framework</a></li>
|
|
||||||
<li><a class="hover-link" href="https://www.kurento.org/" target="_blank">Kurento</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div fxFlex="50%">
|
|
||||||
<h2>Connect</h2>
|
|
||||||
<ul>
|
|
||||||
<li><a class="hover-link" href="https://github.com/OpenVidu" target="_blank">GitHub repository</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
</md-sidenav-container>
|
|
|
@ -1,32 +0,0 @@
|
||||||
import { TestBed, async } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [
|
|
||||||
AppComponent
|
|
||||||
],
|
|
||||||
}).compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should create the app', async(() => {
|
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
|
||||||
const app = fixture.debugElement.componentInstance;
|
|
||||||
expect(app).toBeTruthy();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it(`should have as title 'app works!'`, async(() => {
|
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
|
||||||
const app = fixture.debugElement.componentInstance;
|
|
||||||
expect(app.title).toEqual('app works!');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should render title in a h1 tag', async(() => {
|
|
||||||
const fixture = TestBed.createComponent(AppComponent);
|
|
||||||
fixture.detectChanges();
|
|
||||||
const compiled = fixture.debugElement.nativeElement;
|
|
||||||
expect(compiled.querySelector('h1').textContent).toContain('app works!');
|
|
||||||
}));
|
|
||||||
});
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
|
|
||||||
import { AuthenticationService } from './services/authentication.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-root',
|
|
||||||
templateUrl: './app.component.html',
|
|
||||||
styleUrls: ['./app.component.css']
|
|
||||||
})
|
|
||||||
export class AppComponent {
|
|
||||||
|
|
||||||
constructor(private router: Router, private authenticationService: AuthenticationService) { }
|
|
||||||
|
|
||||||
isVideoSessionUrl() {
|
|
||||||
return (this.router.url.substring(0, '/lesson/'.length) === '/lesson/');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { HttpModule } from '@angular/http';
|
|
||||||
|
|
||||||
import { routing } from './app.routing';
|
|
||||||
|
|
||||||
import { MaterialModule } from '@angular/material';
|
|
||||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
|
||||||
import 'hammerjs';
|
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
|
||||||
import { PresentationComponent } from './components/presentation/presentation.component';
|
|
||||||
import { DashboardComponent } from './components/dashboard/dahsboard.component';
|
|
||||||
import { LessonDetailsComponent } from './components/lesson-details/lesson-details.component';
|
|
||||||
import { ProfileComponent } from './components/profile/profile.component';
|
|
||||||
import { VideoSessionComponent } from './components/video-session/video-session.component';
|
|
||||||
import { ErrorMessageComponent } from './components/error-message/error-message.component';
|
|
||||||
import { JoinSessionDialogComponent } from './components/dashboard/join-session-dialog.component';
|
|
||||||
|
|
||||||
import { AuthenticationService } from './services/authentication.service';
|
|
||||||
import { UserService } from './services/user.service';
|
|
||||||
import { LessonService } from './services/lesson.service';
|
|
||||||
import { VideoSessionService } from './services/video-session.service';
|
|
||||||
import { AuthGuard } from './auth.guard';
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [
|
|
||||||
AppComponent,
|
|
||||||
PresentationComponent,
|
|
||||||
DashboardComponent,
|
|
||||||
LessonDetailsComponent,
|
|
||||||
ProfileComponent,
|
|
||||||
VideoSessionComponent,
|
|
||||||
ErrorMessageComponent,
|
|
||||||
JoinSessionDialogComponent,
|
|
||||||
],
|
|
||||||
imports: [
|
|
||||||
BrowserModule,
|
|
||||||
FormsModule,
|
|
||||||
HttpModule,
|
|
||||||
MaterialModule,
|
|
||||||
FlexLayoutModule.forRoot(),
|
|
||||||
routing,
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
AuthenticationService,
|
|
||||||
UserService,
|
|
||||||
LessonService,
|
|
||||||
VideoSessionService,
|
|
||||||
AuthGuard,
|
|
||||||
],
|
|
||||||
entryComponents: [
|
|
||||||
JoinSessionDialogComponent,
|
|
||||||
],
|
|
||||||
bootstrap: [AppComponent]
|
|
||||||
})
|
|
||||||
export class AppModule { }
|
|
|
@ -1,40 +0,0 @@
|
||||||
import { ModuleWithProviders } from '@angular/core';
|
|
||||||
import { Routes, RouterModule } from '@angular/router';
|
|
||||||
|
|
||||||
import { PresentationComponent } from './components/presentation/presentation.component';
|
|
||||||
import { DashboardComponent } from './components/dashboard/dahsboard.component';
|
|
||||||
import { LessonDetailsComponent } from './components/lesson-details/lesson-details.component';
|
|
||||||
import { ProfileComponent } from './components/profile/profile.component';
|
|
||||||
import { VideoSessionComponent } from './components/video-session/video-session.component';
|
|
||||||
|
|
||||||
import { AuthGuard } from './auth.guard';
|
|
||||||
|
|
||||||
const appRoutes: Routes = [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: PresentationComponent,
|
|
||||||
pathMatch: 'full',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'lessons',
|
|
||||||
component: DashboardComponent,
|
|
||||||
canActivate: [AuthGuard]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'lesson-details/:id',
|
|
||||||
component: LessonDetailsComponent,
|
|
||||||
canActivate: [AuthGuard]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'profile',
|
|
||||||
component: ProfileComponent,
|
|
||||||
canActivate: [AuthGuard]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'lesson/:id',
|
|
||||||
component: VideoSessionComponent,
|
|
||||||
canActivate: [AuthGuard]
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Router, CanActivate } from '@angular/router';
|
|
||||||
|
|
||||||
import { AuthenticationService } from './services/authentication.service';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class AuthGuard implements CanActivate {
|
|
||||||
|
|
||||||
constructor(private router: Router, private authenticationService: AuthenticationService) { }
|
|
||||||
|
|
||||||
canActivate() {
|
|
||||||
if (localStorage.getItem('login') && localStorage.getItem('rol') && this.authenticationService.isLoggedIn()) {
|
|
||||||
// logged in so return true
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// not logged in so redirect to login page
|
|
||||||
this.router.navigate(['']);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
import { Component, OnInit, EventEmitter } from '@angular/core';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { MdDialog, MdDialogRef, MdSnackBar } from '@angular/material';
|
|
||||||
|
|
||||||
import { Lesson } from '../../models/lesson';
|
|
||||||
|
|
||||||
import { LessonService } from '../../services/lesson.service';
|
|
||||||
import { VideoSessionService } from '../../services/video-session.service';
|
|
||||||
import { AuthenticationService } from '../../services/authentication.service';
|
|
||||||
|
|
||||||
import { JoinSessionDialogComponent } from './join-session-dialog.component';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-dashboard',
|
|
||||||
templateUrl: './dashboard.component.html',
|
|
||||||
styleUrls: ['./dashboard.component.css'],
|
|
||||||
})
|
|
||||||
export class DashboardComponent implements OnInit {
|
|
||||||
|
|
||||||
lessons: Lesson[];
|
|
||||||
|
|
||||||
addingLesson: false;
|
|
||||||
lessonTitle: string;
|
|
||||||
sumbitNewLesson: boolean;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private lessonService: LessonService,
|
|
||||||
private videoSessionService: VideoSessionService,
|
|
||||||
private authenticationService: AuthenticationService,
|
|
||||||
private router: Router,
|
|
||||||
public snackBar: MdSnackBar,
|
|
||||||
public dialog: MdDialog
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.authenticationService.checkCredentials();
|
|
||||||
this.getLessons();
|
|
||||||
}
|
|
||||||
|
|
||||||
logout() {
|
|
||||||
this.authenticationService.logOut();
|
|
||||||
}
|
|
||||||
|
|
||||||
getLessons(): void {
|
|
||||||
this.lessonService.getLessons(this.authenticationService.getCurrentUser()).subscribe(
|
|
||||||
lessons => {
|
|
||||||
console.log('User\'s lessons: ');
|
|
||||||
console.log(lessons);
|
|
||||||
this.lessons = lessons;
|
|
||||||
this.authenticationService.updateUserLessons(this.lessons);
|
|
||||||
},
|
|
||||||
error => console.log(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
goToLesson(lesson: Lesson) {
|
|
||||||
let dialogRef: MdDialogRef<JoinSessionDialogComponent>;
|
|
||||||
dialogRef = this.dialog.open(JoinSessionDialogComponent);
|
|
||||||
dialogRef.componentInstance.myReference = dialogRef;
|
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe(cameraOptions => {
|
|
||||||
if (cameraOptions != null) {
|
|
||||||
console.log('Joining session with options:');
|
|
||||||
console.log(cameraOptions);
|
|
||||||
this.videoSessionService.lesson = lesson;
|
|
||||||
this.videoSessionService.cameraOptions = cameraOptions;
|
|
||||||
this.router.navigate(['/lesson/' + lesson.id]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
goToLessonDetails(lesson: Lesson) {
|
|
||||||
this.router.navigate(['/lesson-details/' + lesson.id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
newLesson() {
|
|
||||||
this.sumbitNewLesson = true;
|
|
||||||
this.lessonService.newLesson(new Lesson(this.lessonTitle)).subscribe(
|
|
||||||
lesson => {
|
|
||||||
console.log('New lesson added: ');
|
|
||||||
console.log(lesson);
|
|
||||||
this.lessons.push(lesson);
|
|
||||||
this.authenticationService.updateUserLessons(this.lessons);
|
|
||||||
this.sumbitNewLesson = false;
|
|
||||||
this.snackBar.open('Lesson added!', undefined, { duration: 3000 });
|
|
||||||
this.addingLesson = false;
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.log(error);
|
|
||||||
this.sumbitNewLesson = false;
|
|
||||||
this.snackBar.open('There has been a problem...', undefined, { duration: 3000 });
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
createSession(lessonId: number){
|
|
||||||
this.videoSessionService.createSession(lessonId).subscribe(
|
|
||||||
response => {
|
|
||||||
console.log(response.text());
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
generateToken(lessonId: number) {
|
|
||||||
this.videoSessionService.generateToken(lessonId).subscribe(
|
|
||||||
response => {
|
|
||||||
console.log(response.text());
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
md-card {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
md-card md-icon {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.teacher {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
<div *ngIf="!this.lessons" class="cssload-container">
|
|
||||||
<div class="cssload-tube-tunnel"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="this.lessons" fxLayout="row" fxLayoutAlign="center center">
|
|
||||||
<div class="div-inner-main" [style.xs]="{'width': '100%'}">
|
|
||||||
|
|
||||||
<div *ngIf="!addingLesson" fxLayout="row" fxLayoutAlign="center center">
|
|
||||||
<div fxFlex="80%">MY LESSONS</div>
|
|
||||||
<md-icon fxFlex="20%" fxLayoutAlign="end center" *ngIf="authenticationService.isTeacher()" (click)="addingLesson = true"
|
|
||||||
[title]="'Add lesson'">add_circle_outline</md-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="addingLesson">
|
|
||||||
<div>NEW LESSON</div>
|
|
||||||
<form #newLessonForm (ngSubmit)="newLesson(); newLessonForm.reset()" [class.filtered]="sumbitNewLesson">
|
|
||||||
<md-input-container>
|
|
||||||
<input mdInput placeholder="Title" [(ngModel)]="lessonTitle" name="lessonTitle" id="lessonTitle" type="text" autocomplete="off"
|
|
||||||
required>
|
|
||||||
</md-input-container>
|
|
||||||
<div class="block-btn">
|
|
||||||
<button md-button type="submit" [disabled]="sumbitNewLesson">Send</button>
|
|
||||||
<button md-button (click)="addingLesson = false; newLessonForm.reset()" [disabled]="sumbitNewLesson">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<md-card *ngFor="let lesson of lessons">
|
|
||||||
<div fxLayout="row" fxLayoutAlign="center center" fxLayoutGap="10px">
|
|
||||||
<span fxFlex="70%" class="title">{{lesson.title}}</span>
|
|
||||||
<span fxFlex="70%" *ngIf="this.authenticationService.isStudent()" class="teacher">{{lesson.teacher.nickName}}</span>
|
|
||||||
<md-icon fxFlex="15%" *ngIf="this.authenticationService.isTeacher()" (click)="goToLessonDetails(lesson)" [title]="'Modify lesson'">mode_edit</md-icon>
|
|
||||||
<md-icon fxFlex="15%" (click)="goToLesson(lesson)" [title]="'Go to lesson!'">play_circle_filled</md-icon>
|
|
||||||
</div>
|
|
||||||
</md-card>
|
|
||||||
|
|
||||||
<div *ngIf="lessons.length === 0 && authenticationService.isStudent() && !addingLesson">
|
|
||||||
<app-error-message [errorTitle]="'You do not have any lessons'" [errorContent]="'Your teacher must invite you'" [customClass]="'warning'"
|
|
||||||
[closable]="false"></app-error-message>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="lessons.length === 0 && authenticationService.isTeacher() && !addingLesson">
|
|
||||||
<app-error-message [errorTitle]="'You do not have any lessons'" [errorContent]="'You can add one by clicking on the icon above'"
|
|
||||||
[customClass]="'warning'" [closable]="false"></app-error-message>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,111 +0,0 @@
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import { MdDialogRef } from '@angular/material';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-join-session-dialog',
|
|
||||||
template: `
|
|
||||||
<div>
|
|
||||||
<h1 md-dialog-title>
|
|
||||||
Video options
|
|
||||||
</h1>
|
|
||||||
<form #dialogForm (ngSubmit)="joinSession()">
|
|
||||||
<md-dialog-content>
|
|
||||||
<div id="quality-div">
|
|
||||||
<h5>Quality</h5>
|
|
||||||
<md-radio-group [(ngModel)]="quality" name="quality" id="quality">
|
|
||||||
<md-radio-button value='low' title="320x240">Low</md-radio-button>
|
|
||||||
<md-radio-button value='medium' title="640x480">Medium</md-radio-button>
|
|
||||||
<md-radio-button value='high' title="1280x720">High</md-radio-button>
|
|
||||||
<md-radio-button value='veryhigh' title="1920x1080">Very high</md-radio-button>
|
|
||||||
</md-radio-group>
|
|
||||||
</div>
|
|
||||||
<div id="join-div">
|
|
||||||
<h5>Enter with active...</h5>
|
|
||||||
<md-checkbox [(ngModel)]="joinWithVideo" name="joinWithVideo" id="joinWithVideo">Video</md-checkbox>
|
|
||||||
<md-checkbox [(ngModel)]="joinWithAudio" name="joinWithAudio">Audio</md-checkbox>
|
|
||||||
</div>
|
|
||||||
</md-dialog-content>
|
|
||||||
<md-dialog-actions>
|
|
||||||
<button md-button md-dialog-close>CANCEL</button>
|
|
||||||
<button md-button id="join-btn" type="submit">JOIN</button>
|
|
||||||
</md-dialog-actions>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
styles: [`
|
|
||||||
#quality-div {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
#join-div {
|
|
||||||
margin-top: 25px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
#quality-tag {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
h5 {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
#joinWithVideo {
|
|
||||||
margin-right: 50px;
|
|
||||||
}
|
|
||||||
md-dialog-actions {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
#join-btn {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
`],
|
|
||||||
})
|
|
||||||
export class JoinSessionDialogComponent {
|
|
||||||
|
|
||||||
public myReference: MdDialogRef<JoinSessionDialogComponent>;
|
|
||||||
private quality = 'medium';
|
|
||||||
private joinWithVideo = true;
|
|
||||||
private joinWithAudio = true;
|
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
|
|
||||||
joinSession() {
|
|
||||||
let cameraOptions = {
|
|
||||||
audio: this.joinWithAudio,
|
|
||||||
video: this.joinWithVideo,
|
|
||||||
data: true,
|
|
||||||
mediaConstraints: this.generateMediaConstraints()
|
|
||||||
};
|
|
||||||
this.myReference.close(cameraOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
generateMediaConstraints() {
|
|
||||||
let mediaConstraints = {
|
|
||||||
audio: true,
|
|
||||||
video: {}
|
|
||||||
}
|
|
||||||
let w = 640;
|
|
||||||
let h = 480;
|
|
||||||
switch (this.quality) {
|
|
||||||
case 'low':
|
|
||||||
w = 320;
|
|
||||||
h = 240;
|
|
||||||
break;
|
|
||||||
case 'medium':
|
|
||||||
w = 640;
|
|
||||||
h = 480;
|
|
||||||
break;
|
|
||||||
case 'high':
|
|
||||||
w = 1280;
|
|
||||||
h = 720;
|
|
||||||
break;
|
|
||||||
case 'veryhigh':
|
|
||||||
w = 1920;
|
|
||||||
h = 1080;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mediaConstraints.video['width'] = { exact: w };
|
|
||||||
mediaConstraints.video['height'] = { exact: h };
|
|
||||||
//mediaConstraints.video['frameRate'] = { ideal: Number((<HTMLInputElement>document.getElementById('frameRate')).value) };
|
|
||||||
|
|
||||||
return mediaConstraints;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
.fail {
|
|
||||||
background-color: rgba(167, 56, 65, 0.2);
|
|
||||||
color: #a73841;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning {
|
|
||||||
background-color: rgba(175, 110, 0, 0.2);
|
|
||||||
color: #af6e00;
|
|
||||||
}
|
|
||||||
|
|
||||||
.correct {
|
|
||||||
background-color: rgba(55, 86, 70, 0.25);
|
|
||||||
color: #375546;
|
|
||||||
}
|
|
||||||
|
|
||||||
md-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
md-card {
|
|
||||||
max-width: 400px;
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
<md-card [ngClass]="customClass">
|
|
||||||
<md-icon *ngIf="closable" (click)="closeAlert()">clear</md-icon>
|
|
||||||
<md-card-title>{{this.errorTitle}}</md-card-title>
|
|
||||||
<md-card-subtitle [innerHTML]="this.errorContent"></md-card-subtitle>
|
|
||||||
</md-card>
|
|
|
@ -1,30 +0,0 @@
|
||||||
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-error-message',
|
|
||||||
templateUrl: './error-message.component.html',
|
|
||||||
styleUrls: ['./error-message.component.css']
|
|
||||||
})
|
|
||||||
export class ErrorMessageComponent {
|
|
||||||
|
|
||||||
@Input()
|
|
||||||
errorTitle: string;
|
|
||||||
@Input()
|
|
||||||
errorContent: string;
|
|
||||||
@Input()
|
|
||||||
customClass: string;
|
|
||||||
@Input()
|
|
||||||
closable: boolean;
|
|
||||||
@Input()
|
|
||||||
timeable: number;
|
|
||||||
|
|
||||||
@Output()
|
|
||||||
eventShowable = new EventEmitter<boolean>();
|
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
|
|
||||||
public closeAlert() {
|
|
||||||
this.eventShowable.emit(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
.attender-email {
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-margin-bottom {
|
|
||||||
margin-bottom: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.attender-row {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 20px;
|
|
||||||
min-height: 27px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#new-attender-title {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*Rotating animation*/
|
|
||||||
|
|
||||||
@-webkit-keyframes rotating
|
|
||||||
/* Safari and Chrome */
|
|
||||||
|
|
||||||
{
|
|
||||||
from {
|
|
||||||
-ms-transform: rotate(360deg);
|
|
||||||
-moz-transform: rotate(360deg);
|
|
||||||
-webkit-transform: rotate(360deg);
|
|
||||||
-o-transform: rotate(360deg);
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
-ms-transform: rotate(0deg);
|
|
||||||
-moz-transform: rotate(0deg);
|
|
||||||
-webkit-transform: rotate(0deg);
|
|
||||||
-o-transform: rotate(0deg);
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes rotating {
|
|
||||||
from {
|
|
||||||
-ms-transform: rotate(360deg);
|
|
||||||
-moz-transform: rotate(360deg);
|
|
||||||
-webkit-transform: rotate(360deg);
|
|
||||||
-o-transform: rotate(360deg);
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
-ms-transform: rotate(0deg);
|
|
||||||
-moz-transform: rotate(0deg);
|
|
||||||
-webkit-transform: rotate(0deg);
|
|
||||||
-o-transform: rotate(0deg);
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.rotating {
|
|
||||||
-webkit-animation: rotating 1s linear infinite;
|
|
||||||
-moz-animation: rotating 1s linear infinite;
|
|
||||||
-ms-animation: rotating 1s linear infinite;
|
|
||||||
-o-animation: rotating 1s linear infinite;
|
|
||||||
animation: rotating 1s linear infinite;
|
|
||||||
cursor: default !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rotating:hover {
|
|
||||||
color: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*End rotating animation*/
|
|
|
@ -1,66 +0,0 @@
|
||||||
<div *ngIf="lesson" fxLayout="row" fxLayoutAlign="center center">
|
|
||||||
<div class="div-inner-main" [style.xs]="{'width': '100%'}">
|
|
||||||
|
|
||||||
<div *ngIf="!editingTitle" fxLayout="row" fxLayoutAlign="center center">
|
|
||||||
<md-icon fxFlex="15%" fxLayoutAlign="start center" (click)="router.navigate(['/lessons'])" [title]="'Back to lessons'">keyboard_arrow_left</md-icon>
|
|
||||||
<h2 fxFlex="70%">{{lesson.title}}</h2>
|
|
||||||
<md-icon fxFlex="15%" fxLayoutAlign="end center" (click)="editingTitle = true; titleEdited = lesson.title" [title]="'Edit lesson'">mode_edit</md-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="editingTitle" fxLayout="row" fxLayoutAlign="start center">
|
|
||||||
<form #editLessonForm (ngSubmit)="editLesson(); editLessonForm.reset()" [class.filtered]="sumbitEditLesson">
|
|
||||||
<md-input-container>
|
|
||||||
<input mdInput placeholder="Title" [(ngModel)]="titleEdited" name="lessonTitle" type="text" autocomplete="off" required>
|
|
||||||
</md-input-container>
|
|
||||||
<div class="block-btn">
|
|
||||||
<button md-button type="submit" [disabled]="sumbitEditLesson">Send</button>
|
|
||||||
<a md-button (click)="editingTitle = false; titleEdited = ''" [disabled]="sumbitEditLesson">Cancel</a>
|
|
||||||
<a md-button (click)="deleteLesson()" [disabled]="sumbitEditLesson">Delete lesson</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form #addAttendersForm (ngSubmit)="addLessonAttenders(); addAttendersForm.reset()" [class.filtered]="sumbitAddAttenders">
|
|
||||||
<h4 id="new-attender-title">New attender</h4>
|
|
||||||
<md-input-container>
|
|
||||||
<input mdInput placeholder="Email" [(ngModel)]="emailAttender" name="attenderEmail" type="text" autocomplete="off" required>
|
|
||||||
</md-input-container>
|
|
||||||
<div class="block-btn">
|
|
||||||
<button md-button type="submit" [disabled]="sumbitAddAttenders">Send</button>
|
|
||||||
<a md-button (click)="addAttendersForm.reset()" [disabled]="sumbitAddAttenders || emailAttender == null">Cancel</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<app-error-message *ngIf="addAttendersCorrect" (eventShowable)="addAttendersCorrect = false" [errorTitle]="attCorrectTitle"
|
|
||||||
[errorContent]="attCorrectContent" [customClass]="'correct'" [closable]="true"></app-error-message>
|
|
||||||
<app-error-message *ngIf="addAttendersError" (eventShowable)="addAttendersError = false" [errorTitle]="attErrorTitle" [errorContent]="attErrorContent"
|
|
||||||
[customClass]="'fail'" [closable]="true"></app-error-message>
|
|
||||||
|
|
||||||
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="20px" fxLayoutAlign="space-between center" fxLayoutAlign.xs="start"
|
|
||||||
class="attender-row">
|
|
||||||
<div fxFlex="90%" class="no-margin-bottom">
|
|
||||||
<div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="space-between center" fxLayoutAlign.xs="start" fxLayoutGap="20px">
|
|
||||||
<div class="no-margin-bottom" fxFlex>{{authenticationService.getCurrentUser().nickName}}</div>
|
|
||||||
<div class="attender-email" fxFlex>{{authenticationService.getCurrentUser().name}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div fxFlex="10%"></div>
|
|
||||||
</div>
|
|
||||||
<div *ngFor="let attender of lesson.attenders; let i = index">
|
|
||||||
<div *ngIf="attender.id != authenticationService.getCurrentUser().id" fxLayout="row" fxLayoutAlign.xs="start" fxLayoutGap="20px"
|
|
||||||
class="attender-row">
|
|
||||||
<div fxFlex="90%">
|
|
||||||
<div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="space-between center" fxLayoutAlign.xs="start" fxLayoutGap="20px">
|
|
||||||
<div class="no-margin-bottom" fxFlex>{{attender.nickName}}</div>
|
|
||||||
<div class="attender-email" fxFlex>{{attender.name}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div fxFlex="10%">
|
|
||||||
<md-icon *ngIf="!this.arrayOfAttDels[i]" (click)="deleteLessonAttender(i, attender)" [title]="'Remove attender'">clear</md-icon>
|
|
||||||
<md-icon *ngIf="this.arrayOfAttDels[i]" class="rotating">cached</md-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,186 +0,0 @@
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
|
||||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
|
||||||
import { Location } from '@angular/common';
|
|
||||||
import { MdSnackBar } from '@angular/material';
|
|
||||||
|
|
||||||
import { Lesson } from '../../models/lesson';
|
|
||||||
import { User } from '../../models/user';
|
|
||||||
|
|
||||||
import { LessonService } from '../../services/lesson.service';
|
|
||||||
import { AuthenticationService } from '../../services/authentication.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-lesson-details',
|
|
||||||
templateUrl: './lesson-details.component.html',
|
|
||||||
styleUrls: ['./lesson-details.component.css'],
|
|
||||||
})
|
|
||||||
export class LessonDetailsComponent implements OnInit {
|
|
||||||
|
|
||||||
lesson: Lesson;
|
|
||||||
|
|
||||||
editingTitle = false;
|
|
||||||
titleEdited: string;
|
|
||||||
sumbitEditLesson = false;
|
|
||||||
emailAttender: string;
|
|
||||||
sumbitAddAttenders = false;
|
|
||||||
arrayOfAttDels = [];
|
|
||||||
|
|
||||||
// Feedback message parameters
|
|
||||||
addAttendersCorrect = false;
|
|
||||||
addAttendersError = false;
|
|
||||||
attErrorTitle: string;
|
|
||||||
attErrorContent: string;
|
|
||||||
attCorrectTitle: string;
|
|
||||||
attCorrectContent: string;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private lessonService: LessonService,
|
|
||||||
private authenticationService: AuthenticationService,
|
|
||||||
private router: Router,
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private location: Location,
|
|
||||||
public snackBar: MdSnackBar
|
|
||||||
) { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.route.params
|
|
||||||
.map((params: Params) => this.lessonService.obtainLocalLesson(+params['id']))
|
|
||||||
.subscribe(lesson => this.lesson = lesson);
|
|
||||||
}
|
|
||||||
|
|
||||||
editLesson() {
|
|
||||||
if (this.titleEdited !== this.lesson.title) {
|
|
||||||
this.sumbitEditLesson = true;
|
|
||||||
let l = new Lesson(this.titleEdited);
|
|
||||||
l.id = this.lesson.id;
|
|
||||||
this.lessonService.editLesson(l).subscribe(
|
|
||||||
lesson => {
|
|
||||||
// Lesson has been updated
|
|
||||||
console.log('Lesson edited: ');
|
|
||||||
console.log(lesson);
|
|
||||||
this.lesson = lesson;
|
|
||||||
this.sumbitEditLesson = false;
|
|
||||||
this.editingTitle = false;
|
|
||||||
this.snackBar.open('Lesson edited!', undefined, { duration: 3000 });
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.log(error);
|
|
||||||
this.sumbitEditLesson = false;
|
|
||||||
this.snackBar.open('There has been a problem...', undefined, { duration: 3000 });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.editingTitle = false; // The user has not modified the title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteLesson() {
|
|
||||||
this.sumbitEditLesson = true;
|
|
||||||
this.lessonService.deleteLesson(this.lesson.id).subscribe(
|
|
||||||
lesson => {
|
|
||||||
// Lesson has been deleted
|
|
||||||
console.log('Lesson deleted');
|
|
||||||
console.log(lesson);
|
|
||||||
this.sumbitEditLesson = false;
|
|
||||||
this.router.navigate(['/lessons']);
|
|
||||||
this.snackBar.open('Lesson deleted!', undefined, { duration: 3000 });
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.log(error);
|
|
||||||
this.sumbitEditLesson = false;
|
|
||||||
this.snackBar.open('There has been a problem...', undefined, { duration: 3000 });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addLessonAttenders() {
|
|
||||||
this.sumbitAddAttenders = true;
|
|
||||||
this.lessonService.addLessonAttenders(this.lesson.id, [this.emailAttender]).subscribe(
|
|
||||||
response => { // response: attendersAdded, attendersAlreadyAdded, emailsInvalid, emailsValidNotRegistered
|
|
||||||
console.log('Attender added');
|
|
||||||
console.log(response);
|
|
||||||
this.sumbitAddAttenders = false;
|
|
||||||
let newAttenders = response.attendersAdded as User[];
|
|
||||||
this.lesson.attenders = this.lesson.attenders.concat(newAttenders);
|
|
||||||
this.handleAttendersMessage(response);
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.log(error);
|
|
||||||
this.sumbitAddAttenders = false;
|
|
||||||
this.snackBar.open('There has been a problem...', undefined, { duration: 3000 });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteLessonAttender(i: number, attender: User) {
|
|
||||||
this.arrayOfAttDels[i] = true;
|
|
||||||
let l = new Lesson(this.lesson.title);
|
|
||||||
l.id = this.lesson.id;
|
|
||||||
for (let i = 0; i < this.lesson.attenders.length; i++) {
|
|
||||||
if (this.lesson.attenders[i].id !== attender.id) {
|
|
||||||
l.attenders.push(new User(this.lesson.attenders[i])); //Inserting a new User object equal to the attender but "lessons" array empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.lessonService.deleteLessonAttenders(l).subscribe(
|
|
||||||
attenders => {
|
|
||||||
console.log('Attender removed');
|
|
||||||
console.log(attenders);
|
|
||||||
this.arrayOfAttDels[i] = false;
|
|
||||||
this.lesson.attenders = attenders;
|
|
||||||
this.snackBar.open('Attender removed!', undefined, { duration: 3000 });
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.log(error);
|
|
||||||
this.arrayOfAttDels[i] = false;
|
|
||||||
this.snackBar.open('There has been a problem...', undefined, { duration: 3000 });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates an error message when there is any problem during the process of adding Users to a Lesson
|
|
||||||
// It also generates a correct feedback message when any student has been correctly added to the Lesson
|
|
||||||
handleAttendersMessage(response) {
|
|
||||||
let isError: boolean = false;
|
|
||||||
let isCorrect: boolean = false;
|
|
||||||
this.attErrorContent = "";
|
|
||||||
this.attCorrectContent = "";
|
|
||||||
|
|
||||||
if (response.attendersAdded.length > 0) {
|
|
||||||
for (let user of response.attendersAdded) {
|
|
||||||
this.attCorrectContent += "<span class='feedback-list'>" + user.name + "</span>";
|
|
||||||
}
|
|
||||||
isCorrect = true;
|
|
||||||
}
|
|
||||||
if (response.attendersAlreadyAdded.length > 0) {
|
|
||||||
this.attErrorContent += "<span>The following users were already added to the lesson</span>";
|
|
||||||
for (let user of response.attendersAlreadyAdded) {
|
|
||||||
this.attErrorContent += "<span class='feedback-list'>" + user.name + "</span>";
|
|
||||||
}
|
|
||||||
isError = true;
|
|
||||||
}
|
|
||||||
if (response.emailsValidNotRegistered.length > 0) {
|
|
||||||
this.attErrorContent += "<span>The following users are not registered</span>";
|
|
||||||
for (let email of response.emailsValidNotRegistered) {
|
|
||||||
this.attErrorContent += "<span class='feedback-list'>" + email + "</span>";
|
|
||||||
}
|
|
||||||
isError = true;
|
|
||||||
}
|
|
||||||
if (response.emailsInvalid) {
|
|
||||||
if (response.emailsInvalid.length > 0) {
|
|
||||||
this.attErrorContent += "<span>These are not valid emails</span>";
|
|
||||||
for (let email of response.emailsInvalid) {
|
|
||||||
this.attErrorContent += "<span class='feedback-list'>" + email + "</span>";
|
|
||||||
}
|
|
||||||
isError = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isError) {
|
|
||||||
this.attErrorTitle = "There have been some problems";
|
|
||||||
this.addAttendersError = true;
|
|
||||||
} else if (response.attendersAdded.length == 0) {
|
|
||||||
this.attErrorTitle = "No emails there!";
|
|
||||||
this.addAttendersError = true;
|
|
||||||
}
|
|
||||||
if (isCorrect) {
|
|
||||||
this.attCorrectTitle = "The following users where properly added";
|
|
||||||
this.addAttendersCorrect = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
md-input-container {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
md-card-actions {
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 10px;
|
|
||||||
color: #9e9e9e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-container {
|
|
||||||
text-align: center;
|
|
||||||
padding-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-button {
|
|
||||||
margin-left: 10px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio-button-div {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sign-up-as {
|
|
||||||
color: #9e9e9e;
|
|
||||||
display: block;
|
|
||||||
margin-top: 15px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
<div fxLayout="row" fxLayoutAlign="center center">
|
|
||||||
<div class="div-inner-main" [style.xs]="{'width': '100%'}">
|
|
||||||
|
|
||||||
<h1>OpenVidu Demo</h1>
|
|
||||||
|
|
||||||
<div fxLayout="column" fxLayoutAlign="space-around center">
|
|
||||||
|
|
||||||
<md-card>
|
|
||||||
<md-card-content>
|
|
||||||
|
|
||||||
<div *ngIf="submitProcessing" class="cssload-container">
|
|
||||||
<div class="cssload-tube-tunnel"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form #myForm (ngSubmit)="onSubmit()" [class.filtered]="submitProcessing">
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<md-input-container>
|
|
||||||
<input mdInput placeholder="Email" [(ngModel)]="email" name="email" id="email" type="email" required>
|
|
||||||
</md-input-container>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="!loginView">
|
|
||||||
<md-input-container>
|
|
||||||
<input mdInput placeholder="Name" [(ngModel)]="nickName" name="nickName" id="nickName" type="text" autocomplete="off" required>
|
|
||||||
</md-input-container>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<md-input-container>
|
|
||||||
<input mdInput placeholder="Password" [(ngModel)]="password" name="password" id="password" type="password" required>
|
|
||||||
</md-input-container>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="!loginView">
|
|
||||||
<md-input-container>
|
|
||||||
<input mdInput placeholder="Confirm password" [(ngModel)]="confirmPassword" name="confirmPassword" id="confirmPassword" type="password"
|
|
||||||
autocomplete="off" required>
|
|
||||||
</md-input-container>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="!loginView" class="radio-button-div">
|
|
||||||
<span id="sign-up-as">Sign up as...</span>
|
|
||||||
<md-radio-group [(ngModel)]="roleUserSignup" name="roleUserSignup" id="roleUserSignup">
|
|
||||||
<md-radio-button value='student'>Student</md-radio-button>
|
|
||||||
<md-radio-button value='teacher'>Teacher</md-radio-button>
|
|
||||||
</md-radio-group>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<app-error-message *ngIf="fieldsIncorrect" (eventShowable)="fieldsIncorrect = false" [errorTitle]="errorTitle" [errorContent]="errorContent"
|
|
||||||
[customClass]="customClass" [closable]="true"></app-error-message>
|
|
||||||
|
|
||||||
<div class="btn-container">
|
|
||||||
<button md-raised-button color="accent" type="submit" *ngIf="loginView" id="log-in-btn">Log in</button>
|
|
||||||
<button md-raised-button color="primary" type="submit" *ngIf="!loginView" id="sign-up-btn">Sign up</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</md-card-content>
|
|
||||||
|
|
||||||
<md-card-actions>
|
|
||||||
<div *ngIf="loginView">Not registered yet?<button md-button (click)="setLoginView(false); myForm.reset()" class="card-button">Sign up</button></div>
|
|
||||||
<div *ngIf="!loginView">Already registered?<button md-button (click)="setLoginView(true); myForm.reset()" class="card-button">Log in</button></div>
|
|
||||||
</md-card-actions>
|
|
||||||
|
|
||||||
</md-card>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,134 +0,0 @@
|
||||||
import { Component, OnInit, AfterViewChecked } from '@angular/core';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { AuthenticationService } from '../../services/authentication.service';
|
|
||||||
import { UserService } from '../../services/user.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-presentation',
|
|
||||||
templateUrl: './presentation.component.html',
|
|
||||||
styleUrls: ['./presentation.component.css']
|
|
||||||
})
|
|
||||||
|
|
||||||
export class PresentationComponent implements OnInit, AfterViewChecked {
|
|
||||||
|
|
||||||
private email: string;
|
|
||||||
private password: string;
|
|
||||||
private confirmPassword: string;
|
|
||||||
private nickName: string;
|
|
||||||
private roleUserSignup = 'student';
|
|
||||||
|
|
||||||
private loginView = true;
|
|
||||||
private fieldsIncorrect: boolean;
|
|
||||||
private submitProcessing: boolean;
|
|
||||||
|
|
||||||
// Error message content
|
|
||||||
private errorTitle: string;
|
|
||||||
private errorContent: string;
|
|
||||||
private customClass: string;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private authenticationService: AuthenticationService,
|
|
||||||
private userService: UserService,
|
|
||||||
private router: Router) { }
|
|
||||||
|
|
||||||
ngOnInit() { }
|
|
||||||
|
|
||||||
// If the user is loggedIn, navigates to dashboard
|
|
||||||
ngAfterViewChecked() {
|
|
||||||
if (this.authenticationService.isLoggedIn()) {
|
|
||||||
this.router.navigate(['/lessons']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoginView(option: boolean) {
|
|
||||||
this.fieldsIncorrect = false;
|
|
||||||
this.loginView = option;
|
|
||||||
}
|
|
||||||
|
|
||||||
onSubmit() {
|
|
||||||
console.log('Submit: email = ' + this.email + ' , password = ' + this.password + ', confirmPassword = ' + this.confirmPassword);
|
|
||||||
this.submitProcessing = true;
|
|
||||||
|
|
||||||
if (this.loginView) {
|
|
||||||
// If login view is activated
|
|
||||||
console.log('Logging in...');
|
|
||||||
this.logIn(this.email, this.password);
|
|
||||||
} else {
|
|
||||||
// If signup view is activated
|
|
||||||
console.log('Signing up...');
|
|
||||||
this.signUp();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logIn(user: string, pass: string) {
|
|
||||||
this.authenticationService.logIn(user, pass).subscribe(
|
|
||||||
result => {
|
|
||||||
this.submitProcessing = false;
|
|
||||||
|
|
||||||
console.log('Login succesful! LOGGED AS ' + this.authenticationService.getCurrentUser().name);
|
|
||||||
|
|
||||||
// Login successful
|
|
||||||
this.fieldsIncorrect = false;
|
|
||||||
this.router.navigate(['/lessons']);
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
|
|
||||||
console.log('Login failed (error): ' + error);
|
|
||||||
|
|
||||||
this.errorTitle = 'Invalid field';
|
|
||||||
this.errorContent = 'Please check your email or password';
|
|
||||||
this.customClass = 'fail';
|
|
||||||
|
|
||||||
// Login failed
|
|
||||||
this.handleError();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
signUp() {
|
|
||||||
|
|
||||||
// Passwords don't match
|
|
||||||
if (this.password !== this.confirmPassword) {
|
|
||||||
this.errorTitle = 'Your passwords don\'t match!';
|
|
||||||
this.errorContent = '';
|
|
||||||
this.customClass = 'fail';
|
|
||||||
this.handleError();
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
|
|
||||||
let userNameFixed = this.email;
|
|
||||||
let userPasswordFixed = this.password;
|
|
||||||
|
|
||||||
this.userService.newUser(userNameFixed, userPasswordFixed, this.nickName, this.roleUserSignup).subscribe(
|
|
||||||
result => {
|
|
||||||
|
|
||||||
// Sign up succesful
|
|
||||||
this.logIn(userNameFixed, userPasswordFixed);
|
|
||||||
console.log('Sign up succesful!');
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
|
|
||||||
console.log('Sign up failed (error): ' + error);
|
|
||||||
if (error === 409) { // CONFLICT: Email already in use
|
|
||||||
this.errorTitle = 'Invalid email';
|
|
||||||
this.errorContent = 'That email is already in use';
|
|
||||||
this.customClass = 'fail';
|
|
||||||
} else if (error === 403) { // FORBIDDEN: Invalid email format
|
|
||||||
this.errorTitle = 'Invalid email format';
|
|
||||||
this.errorContent = 'Our server has rejected that email';
|
|
||||||
this.customClass = 'fail';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign up failed
|
|
||||||
this.handleError();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleError() {
|
|
||||||
this.submitProcessing = false;
|
|
||||||
this.fieldsIncorrect = true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
table {
|
|
||||||
margin-top: 15px;
|
|
||||||
border-collapse: separate;
|
|
||||||
border-spacing: 15px 17px;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
<div fxLayout="row" fxLayoutAlign="center center">
|
|
||||||
<div class="div-inner-main" [style.xs]="{'width': '100%'}">
|
|
||||||
|
|
||||||
<div>MY PROFILE</div>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td>Name</td>
|
|
||||||
<th>{{authenticationService.getCurrentUser().nickName}}</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Email</td>
|
|
||||||
<th>{{authenticationService.getCurrentUser().name}}</th>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
|
|
||||||
import { AuthenticationService } from '../../services/authentication.service';
|
|
||||||
import { User } from '../../models/user';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-profile',
|
|
||||||
templateUrl: './profile.component.html',
|
|
||||||
styleUrls: ['./profile.component.css']
|
|
||||||
})
|
|
||||||
export class ProfileComponent implements OnInit {
|
|
||||||
|
|
||||||
private user: User;
|
|
||||||
|
|
||||||
constructor(private authenticationService: AuthenticationService) { }
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.user = this.authenticationService.getCurrentUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
margin: 0;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header-div {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1000;
|
|
||||||
width: 100%;
|
|
||||||
background: rgba(0, 0, 0, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
md-icon {
|
|
||||||
font-size: 38px;
|
|
||||||
width: 38px;
|
|
||||||
height: 38px;
|
|
||||||
color: white;
|
|
||||||
-webkit-transition: color .2s linear;
|
|
||||||
-moz-transition: color .2s linear;
|
|
||||||
-ms-transition: color .2s linear;
|
|
||||||
-o-transition: color .2s linear;
|
|
||||||
transition: color .2s linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
md-icon:hover {
|
|
||||||
color: #ffd740;
|
|
||||||
}
|
|
||||||
|
|
||||||
#back-btn {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-btn {
|
|
||||||
float: right;
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
<div id="header-div">
|
|
||||||
<md-icon id="back-btn" (click)="location.back()" [title]="'Back to lessons'">keyboard_arrow_left</md-icon>
|
|
||||||
<md-icon class="right-btn" (click)="toggleFullScreen()" [title]="'Fullscreen'">{{fullscreenIcon}}</md-icon>
|
|
||||||
<md-icon class="right-btn" (click)="toggleLocalVideo()" [title]="'Toggle video'">{{videoIcon}}</md-icon>
|
|
||||||
<md-icon class="right-btn" (click)="toggleLocalAudio()" [title]="'Toggle audio'">{{audioIcon}}</md-icon>
|
|
||||||
<h1>{{lesson?.title}}</h1>
|
|
||||||
</div>
|
|
||||||
<div id="publisher"></div>
|
|
||||||
<div id="subscriber"></div>
|
|
|
@ -1,260 +0,0 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { Location } from '@angular/common';
|
|
||||||
import { OpenVidu, Session, Publisher } from 'openvidu-browser';
|
|
||||||
|
|
||||||
import { VideoSessionService } from '../../services/video-session.service';
|
|
||||||
import { AuthenticationService } from '../../services/authentication.service';
|
|
||||||
|
|
||||||
import { Lesson } from '../../models/lesson';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-video-session',
|
|
||||||
templateUrl: './video-session.component.html',
|
|
||||||
styleUrls: ['./video-session.component.css']
|
|
||||||
})
|
|
||||||
export class VideoSessionComponent implements OnInit {
|
|
||||||
|
|
||||||
lesson: Lesson;
|
|
||||||
|
|
||||||
OV: OpenVidu;
|
|
||||||
session: Session;
|
|
||||||
publisher: Publisher;
|
|
||||||
|
|
||||||
sessionId: string;
|
|
||||||
token: string;
|
|
||||||
|
|
||||||
cameraOptions: any;
|
|
||||||
|
|
||||||
localVideoActivated: boolean;
|
|
||||||
localAudioActivated: boolean;
|
|
||||||
videoIcon: string;
|
|
||||||
audioIcon: string;
|
|
||||||
fullscreenIcon: string;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private location: Location,
|
|
||||||
private authenticationService: AuthenticationService,
|
|
||||||
private videoSessionService: VideoSessionService) { }
|
|
||||||
|
|
||||||
|
|
||||||
OPEN_VIDU_CONNECTION() {
|
|
||||||
|
|
||||||
// 0) Obtain 'sessionId' and 'token' from server
|
|
||||||
// In this case, the method ngOnInit takes care of it
|
|
||||||
|
|
||||||
|
|
||||||
// 1) Initialize OpenVidu and your Session
|
|
||||||
this.OV = new OpenVidu("wss://" + location.hostname + ":8443/");
|
|
||||||
this.session = this.OV.initSession("apikey", this.sessionId);
|
|
||||||
|
|
||||||
|
|
||||||
// 2) Specify the actions when events take place
|
|
||||||
this.session.on('streamCreated', (event) => {
|
|
||||||
console.warn("STREAM CREATED!");
|
|
||||||
console.warn(event.stream);
|
|
||||||
this.session.subscribe(event.stream, 'subscriber', {
|
|
||||||
insertMode: 'append',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.session.on('streamDestroyed', (event) => {
|
|
||||||
console.warn("STREAM DESTROYED!");
|
|
||||||
console.warn(event.stream);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.session.on('connectionCreated', (event) => {
|
|
||||||
if (event.connection.connectionId == this.session.connection.connectionId) {
|
|
||||||
console.warn("YOUR OWN CONNECTION CREATED!");
|
|
||||||
} else {
|
|
||||||
console.warn("OTHER USER'S CONNECTION CREATED!");
|
|
||||||
}
|
|
||||||
console.warn(event.connection);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.session.on('connectionDestroyed', (event) => {
|
|
||||||
console.warn("OTHER USER'S CONNECTION DESTROYED!");
|
|
||||||
console.warn(event.connection);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 3) Connect to the session
|
|
||||||
this.session.connect(this.token, "CLIENT:" + this.authenticationService.getCurrentUser().name, (error) => {
|
|
||||||
|
|
||||||
// If the connection is successful, initialize a publisher and publish to the session
|
|
||||||
if (!error) {
|
|
||||||
|
|
||||||
if (this.authenticationService.isTeacher()) {
|
|
||||||
|
|
||||||
// 4) Get your own camera stream with the desired resolution and publish it, only if the user is supposed to do so
|
|
||||||
this.publisher = this.OV.initPublisher('publisher', this.cameraOptions);
|
|
||||||
|
|
||||||
this.publisher.on('accessAllowed', () => {
|
|
||||||
console.warn("CAMERA ACCESS ALLOWED!");
|
|
||||||
});
|
|
||||||
this.publisher.on('accessDenied', () => {
|
|
||||||
console.warn("CAMERA ACCESS DENIED!");
|
|
||||||
});
|
|
||||||
this.publisher.on('streamCreated', (event) => {
|
|
||||||
console.warn("STREAM CREATED BY PUBLISHER!");
|
|
||||||
console.warn(event.stream);
|
|
||||||
})
|
|
||||||
|
|
||||||
// 5) Publish your stream
|
|
||||||
this.session.publish(this.publisher);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
console.log('There was an error connecting to the session:', error.code, error.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
|
|
||||||
// Specific aspects of this concrete application
|
|
||||||
this.previousConnectionStuff();
|
|
||||||
|
|
||||||
|
|
||||||
if (this.authenticationService.isTeacher()) {
|
|
||||||
|
|
||||||
// If the user is the teacher: creates the session and gets a token (with PUBLISHER role)
|
|
||||||
this.videoSessionService.createSession(this.lesson.id).subscribe(
|
|
||||||
sessionId => { // {0: sessionId}
|
|
||||||
this.sessionId = sessionId[0];
|
|
||||||
this.videoSessionService.generateToken(this.lesson.id).subscribe(
|
|
||||||
sessionIdAndToken => {
|
|
||||||
this.token = sessionIdAndToken[1];
|
|
||||||
console.warn("Token: " + this.token);
|
|
||||||
console.warn("SessionId: " + this.sessionId);
|
|
||||||
this.OPEN_VIDU_CONNECTION();
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
// If the user is a student: gets a token (with SUBSCRIBER role)
|
|
||||||
this.videoSessionService.generateToken(this.lesson.id).subscribe(
|
|
||||||
sessionIdAndToken => { // {0: sessionId, 1: token}
|
|
||||||
this.sessionId = sessionIdAndToken[0];
|
|
||||||
this.token = sessionIdAndToken[1];
|
|
||||||
console.warn("Token: " + this.token);
|
|
||||||
console.warn("SessionId: " + this.sessionId);
|
|
||||||
this.OPEN_VIDU_CONNECTION();
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Specific aspects of this concrete application
|
|
||||||
this.afterConnectionStuff();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngAfterViewInit() {
|
|
||||||
this.toggleScrollPage("hidden");
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.videoSessionService.removeUser(this.lesson.id).subscribe(
|
|
||||||
response => {
|
|
||||||
console.warn("You have succesfully left the lesson");
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
this.toggleScrollPage("auto");
|
|
||||||
this.exitFullScreen();
|
|
||||||
if (this.OV) this.session.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleScrollPage(scroll: string) {
|
|
||||||
let content = <HTMLElement>document.getElementsByClassName("mat-sidenav-content")[0];
|
|
||||||
content.style.overflow = scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleLocalVideo() {
|
|
||||||
this.localVideoActivated = !this.localVideoActivated;
|
|
||||||
this.publisher.publishVideo(this.localVideoActivated);
|
|
||||||
this.videoIcon = this.localVideoActivated ? 'videocam' : 'videocam_off';
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleLocalAudio() {
|
|
||||||
this.localAudioActivated = !this.localAudioActivated;
|
|
||||||
this.publisher.publishAudio(this.localAudioActivated);
|
|
||||||
this.audioIcon = this.localAudioActivated ? 'mic' : 'mic_off';
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleFullScreen() {
|
|
||||||
let document: any = window.document;
|
|
||||||
let fs = document.getElementsByTagName('html')[0];
|
|
||||||
if (!document.fullscreenElement &&
|
|
||||||
!document.mozFullScreenElement &&
|
|
||||||
!document.webkitFullscreenElement &&
|
|
||||||
!document.msFullscreenElement) {
|
|
||||||
console.log("enter FULLSCREEN!");
|
|
||||||
this.fullscreenIcon = 'fullscreen_exit';
|
|
||||||
if (fs.requestFullscreen) {
|
|
||||||
fs.requestFullscreen();
|
|
||||||
} else if (fs.msRequestFullscreen) {
|
|
||||||
fs.msRequestFullscreen();
|
|
||||||
} else if (fs.mozRequestFullScreen) {
|
|
||||||
fs.mozRequestFullScreen();
|
|
||||||
} else if (fs.webkitRequestFullscreen) {
|
|
||||||
fs.webkitRequestFullscreen();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log("exit FULLSCREEN!");
|
|
||||||
this.fullscreenIcon = 'fullscreen';
|
|
||||||
if (document.exitFullscreen) {
|
|
||||||
document.exitFullscreen();
|
|
||||||
} else if (document.msExitFullscreen) {
|
|
||||||
document.msExitFullscreen();
|
|
||||||
} else if (document.mozCancelFullScreen) {
|
|
||||||
document.mozCancelFullScreen();
|
|
||||||
} else if (document.webkitExitFullscreen) {
|
|
||||||
document.webkitExitFullscreen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exitFullScreen() {
|
|
||||||
let document: any = window.document;
|
|
||||||
let fs = document.getElementsByTagName('html')[0];
|
|
||||||
if (document.exitFullscreen) {
|
|
||||||
document.exitFullscreen();
|
|
||||||
} else if (document.msExitFullscreen) {
|
|
||||||
document.msExitFullscreen();
|
|
||||||
} else if (document.mozCancelFullScreen) {
|
|
||||||
document.mozCancelFullScreen();
|
|
||||||
} else if (document.webkitExitFullscreen) {
|
|
||||||
document.webkitExitFullscreen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
previousConnectionStuff() {
|
|
||||||
this.lesson = this.videoSessionService.lesson;
|
|
||||||
this.cameraOptions = this.videoSessionService.cameraOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
afterConnectionStuff() {
|
|
||||||
this.localVideoActivated = this.cameraOptions.video;
|
|
||||||
this.localAudioActivated = this.cameraOptions.audio;
|
|
||||||
this.videoIcon = this.localVideoActivated ? "videocam" : "videocam_off";
|
|
||||||
this.audioIcon = this.localAudioActivated ? "mic" : "mic_off";
|
|
||||||
this.fullscreenIcon = "fullscreen";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
import { User } from './user';
|
|
||||||
|
|
||||||
export class Lesson {
|
|
||||||
|
|
||||||
public id?: number;
|
|
||||||
public title: string;
|
|
||||||
public teacher: User;
|
|
||||||
public attenders: User[];
|
|
||||||
|
|
||||||
constructor(title: string) {
|
|
||||||
this.title = title;
|
|
||||||
this.attenders = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
import { Lesson } from './lesson';
|
|
||||||
|
|
||||||
export class User {
|
|
||||||
|
|
||||||
public id?: number;
|
|
||||||
public name: string;
|
|
||||||
public nickName: string;
|
|
||||||
public roles: string[];
|
|
||||||
public lessons: Lesson[];
|
|
||||||
|
|
||||||
constructor(u: User) {
|
|
||||||
this.id = u.id;
|
|
||||||
this.name = u.name;
|
|
||||||
this.nickName = u.nickName;
|
|
||||||
this.roles = u.roles;
|
|
||||||
this.lessons = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,147 +0,0 @@
|
||||||
import { Observable } from 'rxjs/Rx';
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Http, RequestOptions, Headers, Response } from '@angular/http';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
|
|
||||||
import { environment } from '../../environments/environment';
|
|
||||||
|
|
||||||
import 'rxjs/add/operator/map';
|
|
||||||
|
|
||||||
import { User } from '../models/user';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class AuthenticationService {
|
|
||||||
|
|
||||||
private urlLogIn = environment.URL_BACK + '/api-logIn';
|
|
||||||
private urlLogOut = environment.URL_BACK + '/api-logOut';
|
|
||||||
|
|
||||||
public token: string;
|
|
||||||
private user: User;
|
|
||||||
private role: string;
|
|
||||||
|
|
||||||
constructor(private http: Http, private router: Router) {
|
|
||||||
this.reqIsLogged();
|
|
||||||
|
|
||||||
// set token if saved in local storage
|
|
||||||
// let auth_token = JSON.parse(localStorage.getItem('auth_token'));
|
|
||||||
// this.token = auth_token && auth_token.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
logIn(user: string, pass: string) {
|
|
||||||
|
|
||||||
console.log('Login service started...');
|
|
||||||
|
|
||||||
let userPass = utf8_to_b64(user + ':' + pass);
|
|
||||||
let headers = new Headers({
|
|
||||||
'Authorization': 'Basic ' + userPass,
|
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
|
||||||
});
|
|
||||||
let options = new RequestOptions({ headers });
|
|
||||||
|
|
||||||
return this.http.get(this.urlLogIn, options)
|
|
||||||
.map(response => {
|
|
||||||
this.processLogInResponse(response);
|
|
||||||
return this.user;
|
|
||||||
})
|
|
||||||
.catch(error => Observable.throw(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
logOut() {
|
|
||||||
|
|
||||||
console.log('Logging out...');
|
|
||||||
|
|
||||||
return this.http.get(this.urlLogOut).map(
|
|
||||||
response => {
|
|
||||||
|
|
||||||
console.log('Logout succesful!');
|
|
||||||
|
|
||||||
this.user = null;
|
|
||||||
this.role = null;
|
|
||||||
|
|
||||||
// clear token remove user from local storage to log user out and navigates to welcome page
|
|
||||||
this.token = null;
|
|
||||||
localStorage.removeItem('login');
|
|
||||||
localStorage.removeItem('rol');
|
|
||||||
this.router.navigate(['']);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
})
|
|
||||||
.catch(error => Observable.throw(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
directLogOut() {
|
|
||||||
this.logOut().subscribe(
|
|
||||||
response => { },
|
|
||||||
error => console.log("Error when trying to log out: " + error)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private processLogInResponse(response) {
|
|
||||||
// Correctly logged in
|
|
||||||
console.log('Login succesful processing...');
|
|
||||||
|
|
||||||
this.user = (response.json() as User);
|
|
||||||
|
|
||||||
localStorage.setItem('login', 'OPENVIDUAPP');
|
|
||||||
if (this.user.roles.indexOf('ROLE_TEACHER') !== -1) {
|
|
||||||
this.role = 'ROLE_TEACHER';
|
|
||||||
localStorage.setItem('rol', 'ROLE_TEACHER');
|
|
||||||
}
|
|
||||||
if (this.user.roles.indexOf('ROLE_STUDENT') !== -1) {
|
|
||||||
this.role = 'ROLE_STUDENT';
|
|
||||||
localStorage.setItem('rol', 'ROLE_STUDENT');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reqIsLogged() {
|
|
||||||
|
|
||||||
console.log('ReqIsLogged called');
|
|
||||||
|
|
||||||
let headers = new Headers({
|
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
|
||||||
});
|
|
||||||
let options = new RequestOptions({ headers });
|
|
||||||
|
|
||||||
this.http.get(this.urlLogIn, options).subscribe(
|
|
||||||
response => this.processLogInResponse(response),
|
|
||||||
error => {
|
|
||||||
if (error.status != 401) {
|
|
||||||
console.error('Error when asking if logged: ' + JSON.stringify(error));
|
|
||||||
this.logOut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkCredentials() {
|
|
||||||
if (!this.isLoggedIn()) {
|
|
||||||
this.logOut();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isLoggedIn() {
|
|
||||||
return ((this.user != null) && (this.user !== undefined));
|
|
||||||
}
|
|
||||||
|
|
||||||
getCurrentUser() {
|
|
||||||
return this.user;
|
|
||||||
}
|
|
||||||
|
|
||||||
isTeacher() {
|
|
||||||
return ((this.user.roles.indexOf('ROLE_TEACHER')) !== -1) && (localStorage.getItem('rol') === 'ROLE_TEACHER');
|
|
||||||
}
|
|
||||||
|
|
||||||
isStudent() {
|
|
||||||
return ((this.user.roles.indexOf('ROLE_STUDENT')) !== -1) && (localStorage.getItem('rol') === 'ROLE_STUDENT');
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUserLessons(lessons) {
|
|
||||||
this.getCurrentUser().lessons = lessons;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function utf8_to_b64(str) {
|
|
||||||
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
|
|
||||||
return String.fromCharCode(<any>'0x' + p1);
|
|
||||||
}));
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Http, Headers, RequestOptions, Response } from '@angular/http';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { environment } from '../../environments/environment';
|
|
||||||
|
|
||||||
import { Lesson } from '../models/lesson';
|
|
||||||
import { User } from '../models/user';
|
|
||||||
import { AuthenticationService } from './authentication.service';
|
|
||||||
|
|
||||||
import 'rxjs/Rx';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class LessonService {
|
|
||||||
|
|
||||||
private url = environment.URL_BACK + '/api-lessons';
|
|
||||||
|
|
||||||
constructor(private http: Http, private authenticationService: AuthenticationService) { }
|
|
||||||
|
|
||||||
getLessons(user: User) {
|
|
||||||
let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + this.authenticationService.token });
|
|
||||||
let options = new RequestOptions({ headers });
|
|
||||||
return this.http.get(this.url + '/user/' + user.id, options) // Must send userId
|
|
||||||
.map((response: Response) => response.json() as Lesson[])
|
|
||||||
.catch(error => this.handleError(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
getLesson(lessonId: number) {
|
|
||||||
let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + this.authenticationService.token });
|
|
||||||
let options = new RequestOptions({ headers });
|
|
||||||
return this.http.get(this.url + '/lesson/' + lessonId, options) // Must send userId
|
|
||||||
.map((response: Response) => response.json() as Lesson)
|
|
||||||
.catch(error => this.handleError(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST new lesson. On success returns the created lesson
|
|
||||||
newLesson(lesson: Lesson) {
|
|
||||||
let body = JSON.stringify(lesson);
|
|
||||||
let headers = new Headers({
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
|
||||||
});
|
|
||||||
let options = new RequestOptions({ headers });
|
|
||||||
return this.http.post(this.url + '/new', body, options)
|
|
||||||
.map(response => response.json() as Lesson)
|
|
||||||
.catch(error => this.handleError(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
// PUT existing lesson. On success returns the updated lesson
|
|
||||||
editLesson(lesson: Lesson) {
|
|
||||||
let body = JSON.stringify(lesson);
|
|
||||||
let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + this.authenticationService.token });
|
|
||||||
let options = new RequestOptions({ headers });
|
|
||||||
return this.http.put(this.url + '/edit', body, options)
|
|
||||||
.map(response => response.json() as Lesson)
|
|
||||||
.catch(error => this.handleError(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
// DELETE existing lesson. On success returns the deleted lesson (simplified version)
|
|
||||||
deleteLesson(lessonId: number) {
|
|
||||||
let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + this.authenticationService.token });
|
|
||||||
let options = new RequestOptions({ headers });
|
|
||||||
return this.http.delete(this.url + '/delete/' + lessonId, options)
|
|
||||||
.map(response => response.json() as Lesson)
|
|
||||||
.catch(error => this.handleError(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
// PUT existing lesson, modifying its attenders (adding them). On success returns the updated lesson.attenders array
|
|
||||||
addLessonAttenders(lessonId: number, userEmails: string[]) {
|
|
||||||
let body = JSON.stringify(userEmails);
|
|
||||||
let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + this.authenticationService.token });
|
|
||||||
let options = new RequestOptions({ headers });
|
|
||||||
return this.http.put(this.url + '/edit/add-attenders/lesson/' + lessonId, body, options)
|
|
||||||
.map(response => response.json())
|
|
||||||
.catch(error => this.handleError(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
// PUT existing lesson, modifying its attenders (deleting them). On success returns the updated lesson.attenders array
|
|
||||||
deleteLessonAttenders(lesson: Lesson) {
|
|
||||||
let body = JSON.stringify(lesson);
|
|
||||||
let headers = new Headers({ 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + this.authenticationService.token });
|
|
||||||
let options = new RequestOptions({ headers });
|
|
||||||
return this.http.put(this.url + '/edit/delete-attenders', body, options)
|
|
||||||
.map(response => response.json() as User[])
|
|
||||||
.catch(error => this.handleError(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
obtainLocalLesson(id: number) {
|
|
||||||
return this.authenticationService.getCurrentUser().lessons.find(lesson => lesson.id == id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleError(error: any) {
|
|
||||||
console.error(error);
|
|
||||||
return Observable.throw('Server error (' + error.status + '): ' + error.text())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Http, Headers, RequestOptions, Response } from '@angular/http';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
|
|
||||||
import { User } from '../models/user';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class UserService {
|
|
||||||
|
|
||||||
private url = '/api-users';
|
|
||||||
|
|
||||||
constructor(private http: Http) { }
|
|
||||||
|
|
||||||
newUser(name: string, pass: string, nickName: string, role: string) {
|
|
||||||
let body = JSON.stringify([name, pass, nickName, role]);
|
|
||||||
let headers = new Headers({
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
|
||||||
});
|
|
||||||
let options = new RequestOptions({ headers });
|
|
||||||
return this.http.post(this.url + "/new", body, options)
|
|
||||||
.map(response => response.json() as User)
|
|
||||||
.catch(error => this.handleError(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleError(error: any) {
|
|
||||||
return Observable.throw(error.status);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Http, Headers, RequestOptions } from '@angular/http';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
|
|
||||||
import { Lesson } from '../models/lesson';
|
|
||||||
|
|
||||||
import { AuthenticationService } from './authentication.service';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class VideoSessionService {
|
|
||||||
|
|
||||||
lesson: Lesson;
|
|
||||||
cameraOptions: any;
|
|
||||||
|
|
||||||
constructor(private http: Http, private authenticationService: AuthenticationService) { }
|
|
||||||
|
|
||||||
// Returns {0: sessionId}
|
|
||||||
createSession(lessonId: number) {
|
|
||||||
let body = JSON.stringify(lessonId);
|
|
||||||
return this.http.post('/api-sessions/create-session', body)
|
|
||||||
.map(response => response.json())
|
|
||||||
.catch(error => this.handleError(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns {0: sessionId, 1: token}
|
|
||||||
generateToken(lessonId: number) {
|
|
||||||
let body = JSON.stringify(lessonId);
|
|
||||||
let headers = new Headers({ 'Content-Type': 'application/json' });
|
|
||||||
let options = new RequestOptions({ headers });
|
|
||||||
return this.http.post('/api-sessions/generate-token', body, options)
|
|
||||||
.map(response => response.json())
|
|
||||||
.catch(error => this.handleError(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
removeUser(lessonId: number) {
|
|
||||||
let body = JSON.stringify(lessonId);
|
|
||||||
let headers = new Headers({ 'Content-Type': 'application/json' });
|
|
||||||
let options = new RequestOptions({ headers });
|
|
||||||
return this.http.post('/api-sessions/remove-user', body, options)
|
|
||||||
.map(response => response)
|
|
||||||
.catch(error => this.handleError(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleError(error: any) {
|
|
||||||
console.error(error);
|
|
||||||
return Observable.throw('Server error (' + error.status + '): ' + error.text())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
export const environment = {
|
|
||||||
production: true
|
|
||||||
};
|
|
|
@ -1,9 +0,0 @@
|
||||||
// The file contents for the current environment will overwrite these during build.
|
|
||||||
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
|
|
||||||
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
|
|
||||||
// The list of which env maps to which file can be found in `.angular-cli.json`.
|
|
||||||
|
|
||||||
export const environment = {
|
|
||||||
production: false,
|
|
||||||
URL_BACK: 'https://localhost:5000'
|
|
||||||
};
|
|
Binary file not shown.
Before Width: | Height: | Size: 5.3 KiB |
|
@ -1,23 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>OpenviduSampleApp</title>
|
|
||||||
<base href="/">
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
|
||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
|
||||||
<link href="https://fonts.googleapis.com/css?family=Exo+2" rel="stylesheet">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<app-root>
|
|
||||||
<div class="cssload-container">
|
|
||||||
<div class="cssload-tube-tunnel"></div>
|
|
||||||
</div>
|
|
||||||
</app-root>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { enableProdMode } from '@angular/core';
|
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|
||||||
|
|
||||||
import { AppModule } from './app/app.module';
|
|
||||||
import { environment } from './environments/environment';
|
|
||||||
|
|
||||||
if (environment.production) {
|
|
||||||
enableProdMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
|
|
@ -1,68 +0,0 @@
|
||||||
/**
|
|
||||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
|
||||||
* You can add your own extra polyfills to this file.
|
|
||||||
*
|
|
||||||
* This file is divided into 2 sections:
|
|
||||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
|
||||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
|
||||||
* file.
|
|
||||||
*
|
|
||||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
|
||||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
|
||||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
|
||||||
*
|
|
||||||
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
|
||||||
* BROWSER POLYFILLS
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
|
|
||||||
// import 'core-js/es6/symbol';
|
|
||||||
// import 'core-js/es6/object';
|
|
||||||
// import 'core-js/es6/function';
|
|
||||||
// import 'core-js/es6/parse-int';
|
|
||||||
// import 'core-js/es6/parse-float';
|
|
||||||
// import 'core-js/es6/number';
|
|
||||||
// import 'core-js/es6/math';
|
|
||||||
// import 'core-js/es6/string';
|
|
||||||
// import 'core-js/es6/date';
|
|
||||||
// import 'core-js/es6/array';
|
|
||||||
// import 'core-js/es6/regexp';
|
|
||||||
// import 'core-js/es6/map';
|
|
||||||
// import 'core-js/es6/set';
|
|
||||||
|
|
||||||
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
|
|
||||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
|
||||||
|
|
||||||
/** IE10 and IE11 requires the following to support `@angular/animation`. */
|
|
||||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
|
||||||
|
|
||||||
|
|
||||||
/** Evergreen browsers require these. **/
|
|
||||||
import 'core-js/es6/reflect';
|
|
||||||
import 'core-js/es7/reflect';
|
|
||||||
|
|
||||||
|
|
||||||
/** ALL Firefox browsers require the following to support `@angular/animation`. **/
|
|
||||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
|
||||||
* Zone JS is required by Angular itself.
|
|
||||||
*/
|
|
||||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************************************
|
|
||||||
* APPLICATION IMPORTS
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Date, currency, decimal and percent pipes.
|
|
||||||
* Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
|
|
||||||
*/
|
|
||||||
// import 'intl'; // Run `npm install --save intl`.
|
|
|
@ -1,230 +0,0 @@
|
||||||
@import '~@angular/material/core/theming/prebuilt/deeppurple-amber.css';
|
|
||||||
|
|
||||||
* {
|
|
||||||
font-family: 'Exo 2', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
md-icon {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
md-icon:hover {
|
|
||||||
color: grey;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
padding: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-sidenav-content {
|
|
||||||
min-height: 100% !important;
|
|
||||||
display: flex !important;
|
|
||||||
flex-direction: column !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hover-link:hover {
|
|
||||||
color: #ffd740 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feedback-list {
|
|
||||||
font-weight: 500;
|
|
||||||
margin-top: 5px;
|
|
||||||
margin-left: 2em;
|
|
||||||
display: list-item;
|
|
||||||
}
|
|
||||||
|
|
||||||
.div-inner-main {
|
|
||||||
min-width: 30%;
|
|
||||||
margin-left: 20px;
|
|
||||||
margin-right: 20px;
|
|
||||||
margin-top: 50px;
|
|
||||||
margin-bottom: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.block-btn {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#publisher {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#publisher video {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#subscriber {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#subscriber video {
|
|
||||||
width: 350px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Colors of Deep Purple Material Theme */
|
|
||||||
|
|
||||||
.back-primary {
|
|
||||||
background: #673ab7 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-accent {
|
|
||||||
background: #ffd740;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-warn {
|
|
||||||
background: #f44336;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-secondary {
|
|
||||||
background: #D1C4E9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-primary {
|
|
||||||
color: #673ab7 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-accent {
|
|
||||||
color: #ffd740;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-warn {
|
|
||||||
color: #f44336;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-secondary {
|
|
||||||
color: #D1C4E9;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*Loader*/
|
|
||||||
|
|
||||||
.cssload-container {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
z-index: 1000;
|
|
||||||
margin: -25px 0 0 -25px;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cssload-tube-tunnel {
|
|
||||||
width: 56px;
|
|
||||||
height: 56px;
|
|
||||||
margin: 0 auto;
|
|
||||||
border: 5px solid;
|
|
||||||
border-radius: 50%;
|
|
||||||
border-color: #673ab7;
|
|
||||||
animation: cssload-scale 1035ms infinite linear;
|
|
||||||
-o-animation: cssload-scale 1035ms infinite linear;
|
|
||||||
-ms-animation: cssload-scale 1035ms infinite linear;
|
|
||||||
-webkit-animation: cssload-scale 1035ms infinite linear;
|
|
||||||
-moz-animation: cssload-scale 1035ms infinite linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes cssload-scale {
|
|
||||||
0% {
|
|
||||||
transform: scale(0);
|
|
||||||
transform: scale(0);
|
|
||||||
}
|
|
||||||
90% {
|
|
||||||
transform: scale(0.7);
|
|
||||||
transform: scale(0.7);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: scale(1);
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-o-keyframes cssload-scale {
|
|
||||||
0% {
|
|
||||||
-o-transform: scale(0);
|
|
||||||
transform: scale(0);
|
|
||||||
}
|
|
||||||
90% {
|
|
||||||
-o-transform: scale(0.7);
|
|
||||||
transform: scale(0.7);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-o-transform: scale(1);
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-ms-keyframes cssload-scale {
|
|
||||||
0% {
|
|
||||||
-ms-transform: scale(0);
|
|
||||||
transform: scale(0);
|
|
||||||
}
|
|
||||||
90% {
|
|
||||||
-ms-transform: scale(0.7);
|
|
||||||
transform: scale(0.7);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-ms-transform: scale(1);
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes cssload-scale {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: scale(0);
|
|
||||||
transform: scale(0);
|
|
||||||
}
|
|
||||||
90% {
|
|
||||||
-webkit-transform: scale(0.7);
|
|
||||||
transform: scale(0.7);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: scale(1);
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-moz-keyframes cssload-scale {
|
|
||||||
0% {
|
|
||||||
-moz-transform: scale(0);
|
|
||||||
transform: scale(0);
|
|
||||||
}
|
|
||||||
90% {
|
|
||||||
-moz-transform: scale(0.7);
|
|
||||||
transform: scale(0.7);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-moz-transform: scale(1);
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.filtered {
|
|
||||||
filter: blur(5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*End Loader*/
|
|
|
@ -1,32 +0,0 @@
|
||||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
|
||||||
|
|
||||||
import 'zone.js/dist/long-stack-trace-zone';
|
|
||||||
import 'zone.js/dist/proxy.js';
|
|
||||||
import 'zone.js/dist/sync-test';
|
|
||||||
import 'zone.js/dist/jasmine-patch';
|
|
||||||
import 'zone.js/dist/async-test';
|
|
||||||
import 'zone.js/dist/fake-async-test';
|
|
||||||
import { getTestBed } from '@angular/core/testing';
|
|
||||||
import {
|
|
||||||
BrowserDynamicTestingModule,
|
|
||||||
platformBrowserDynamicTesting
|
|
||||||
} from '@angular/platform-browser-dynamic/testing';
|
|
||||||
|
|
||||||
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
|
|
||||||
declare var __karma__: any;
|
|
||||||
declare var require: any;
|
|
||||||
|
|
||||||
// Prevent Karma from running prematurely.
|
|
||||||
__karma__.loaded = function () {};
|
|
||||||
|
|
||||||
// First, initialize the Angular testing environment.
|
|
||||||
getTestBed().initTestEnvironment(
|
|
||||||
BrowserDynamicTestingModule,
|
|
||||||
platformBrowserDynamicTesting()
|
|
||||||
);
|
|
||||||
// Then we find all the tests.
|
|
||||||
const context = require.context('./', true, /\.spec\.ts$/);
|
|
||||||
// And load the modules.
|
|
||||||
context.keys().map(context);
|
|
||||||
// Finally, start Karma to run the tests.
|
|
||||||
__karma__.start();
|
|
|
@ -1,22 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"sourceMap": true,
|
|
||||||
"declaration": false,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"lib": [
|
|
||||||
"es2016",
|
|
||||||
"dom"
|
|
||||||
],
|
|
||||||
"outDir": "../out-tsc/app",
|
|
||||||
"target": "es5",
|
|
||||||
"module": "es2015",
|
|
||||||
"baseUrl": "",
|
|
||||||
"types": []
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"test.ts",
|
|
||||||
"**/*.spec.ts"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"sourceMap": true,
|
|
||||||
"declaration": false,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"lib": [
|
|
||||||
"es2016"
|
|
||||||
],
|
|
||||||
"outDir": "../out-tsc/spec",
|
|
||||||
"module": "commonjs",
|
|
||||||
"target": "es6",
|
|
||||||
"baseUrl": "",
|
|
||||||
"types": [
|
|
||||||
"jasmine",
|
|
||||||
"node"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"test.ts"
|
|
||||||
],
|
|
||||||
"include": [
|
|
||||||
"**/*.spec.ts"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
/* SystemJS module definition */
|
|
||||||
declare var module: {
|
|
||||||
id: string;
|
|
||||||
};
|
|
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
"compileOnSave": false,
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "./dist/out-tsc",
|
|
||||||
"sourceMap": true,
|
|
||||||
"declaration": false,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"target": "es5",
|
|
||||||
"lib": [
|
|
||||||
"es2016",
|
|
||||||
"dom"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,116 +0,0 @@
|
||||||
{
|
|
||||||
"rulesDirectory": [
|
|
||||||
"node_modules/codelyzer"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"callable-types": true,
|
|
||||||
"class-name": true,
|
|
||||||
"comment-format": [
|
|
||||||
true,
|
|
||||||
"check-space"
|
|
||||||
],
|
|
||||||
"curly": true,
|
|
||||||
"eofline": true,
|
|
||||||
"forin": true,
|
|
||||||
"import-blacklist": [true, "rxjs"],
|
|
||||||
"import-spacing": true,
|
|
||||||
"indent": [
|
|
||||||
true,
|
|
||||||
"spaces"
|
|
||||||
],
|
|
||||||
"interface-over-type-literal": true,
|
|
||||||
"label-position": true,
|
|
||||||
"max-line-length": [
|
|
||||||
true,
|
|
||||||
140
|
|
||||||
],
|
|
||||||
"member-access": false,
|
|
||||||
"member-ordering": [
|
|
||||||
true,
|
|
||||||
"static-before-instance",
|
|
||||||
"variables-before-functions"
|
|
||||||
],
|
|
||||||
"no-arg": true,
|
|
||||||
"no-bitwise": true,
|
|
||||||
"no-console": [
|
|
||||||
true,
|
|
||||||
"debug",
|
|
||||||
"info",
|
|
||||||
"time",
|
|
||||||
"timeEnd",
|
|
||||||
"trace"
|
|
||||||
],
|
|
||||||
"no-construct": true,
|
|
||||||
"no-debugger": true,
|
|
||||||
"no-duplicate-variable": true,
|
|
||||||
"no-empty": false,
|
|
||||||
"no-empty-interface": true,
|
|
||||||
"no-eval": true,
|
|
||||||
"no-inferrable-types": [true, "ignore-params"],
|
|
||||||
"no-shadowed-variable": true,
|
|
||||||
"no-string-literal": false,
|
|
||||||
"no-string-throw": true,
|
|
||||||
"no-switch-case-fall-through": true,
|
|
||||||
"no-trailing-whitespace": true,
|
|
||||||
"no-unused-expression": true,
|
|
||||||
"no-use-before-declare": true,
|
|
||||||
"no-var-keyword": true,
|
|
||||||
"object-literal-sort-keys": false,
|
|
||||||
"one-line": [
|
|
||||||
true,
|
|
||||||
"check-open-brace",
|
|
||||||
"check-catch",
|
|
||||||
"check-else",
|
|
||||||
"check-whitespace"
|
|
||||||
],
|
|
||||||
"prefer-const": true,
|
|
||||||
"quotemark": [
|
|
||||||
true,
|
|
||||||
"single"
|
|
||||||
],
|
|
||||||
"radix": true,
|
|
||||||
"semicolon": [
|
|
||||||
"always"
|
|
||||||
],
|
|
||||||
"triple-equals": [
|
|
||||||
true,
|
|
||||||
"allow-null-check"
|
|
||||||
],
|
|
||||||
"typedef-whitespace": [
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
"call-signature": "nospace",
|
|
||||||
"index-signature": "nospace",
|
|
||||||
"parameter": "nospace",
|
|
||||||
"property-declaration": "nospace",
|
|
||||||
"variable-declaration": "nospace"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"typeof-compare": true,
|
|
||||||
"unified-signatures": true,
|
|
||||||
"variable-name": false,
|
|
||||||
"whitespace": [
|
|
||||||
true,
|
|
||||||
"check-branch",
|
|
||||||
"check-decl",
|
|
||||||
"check-operator",
|
|
||||||
"check-separator",
|
|
||||||
"check-type"
|
|
||||||
],
|
|
||||||
|
|
||||||
"directive-selector": [true, "attribute", "app", "camelCase"],
|
|
||||||
"component-selector": [true, "element", "app", "kebab-case"],
|
|
||||||
"use-input-property-decorator": true,
|
|
||||||
"use-output-property-decorator": true,
|
|
||||||
"use-host-property-decorator": true,
|
|
||||||
"no-input-rename": true,
|
|
||||||
"no-output-rename": true,
|
|
||||||
"use-life-cycle-interface": true,
|
|
||||||
"use-pipe-transform-interface": true,
|
|
||||||
"component-class-suffix": true,
|
|
||||||
"directive-class-suffix": true,
|
|
||||||
"no-access-missing-member": true,
|
|
||||||
"templates-use-public": true,
|
|
||||||
"invoke-injectable": true
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
FROM java:8
|
|
||||||
VOLUME /tmp
|
|
||||||
ADD openvidu-sample-app-0.0.1-SNAPSHOT.war openvidu-sample-app.war
|
|
||||||
ADD wrapper.sh wrapper.sh
|
|
||||||
RUN bash -c 'chmod +x /wrapper.sh'
|
|
||||||
RUN bash -c 'touch /openvidu-sample-app.war'
|
|
||||||
ENTRYPOINT ["/bin/bash", "/wrapper.sh"]
|
|
|
@ -1,29 +0,0 @@
|
||||||
version: '2.0'
|
|
||||||
|
|
||||||
services:
|
|
||||||
demo-mysql:
|
|
||||||
image: mysql:5.6
|
|
||||||
ports:
|
|
||||||
- 3307:3306
|
|
||||||
environment:
|
|
||||||
- MYSQL_ROOT_PASSWORD=demo_pass
|
|
||||||
- MYSQL_DATABASE=openvidu_sample_app
|
|
||||||
- MYSQL_USER=demo_user
|
|
||||||
- MYSQL_PASSWORD=demo_pass
|
|
||||||
#restart: always
|
|
||||||
|
|
||||||
demo-app:
|
|
||||||
image: openvidu/openvidu-sample-app:latest
|
|
||||||
depends_on:
|
|
||||||
- demo-mysql
|
|
||||||
ports:
|
|
||||||
- 5000:5000
|
|
||||||
environment:
|
|
||||||
- DATABASE_HOST=demo-mysql
|
|
||||||
- DATABASE_USER=demo_user
|
|
||||||
- DATABASE_PASSWORD=demo_pass
|
|
||||||
- DATABASE_NAME=openvidu_sample_app
|
|
||||||
- DATABASE_PORT=3307
|
|
||||||
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3307/openvidu_sample_app
|
|
||||||
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
sleep 40
|
|
||||||
java -Djava.security.egd=file:/dev/./urandom -jar /openvidu-sample-app.war
|
|
|
@ -1,30 +0,0 @@
|
||||||
package io.openvidu.sample.app;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
|
||||||
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
|
||||||
@EnableWebSocket
|
|
||||||
public class App
|
|
||||||
{
|
|
||||||
public static void main( String[] args )
|
|
||||||
{
|
|
||||||
SpringApplication.run(App.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public WebMvcConfigurer corsConfigurer() {
|
|
||||||
return new WebMvcConfigurerAdapter() {
|
|
||||||
@Override
|
|
||||||
public void addCorsMappings(CorsRegistry registry) {
|
|
||||||
registry.addMapping("/**").allowedOrigins("*");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package io.openvidu.sample.app;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.CommandLineRunner;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
|
|
||||||
import io.openvidu.sample.app.lesson.Lesson;
|
|
||||||
import io.openvidu.sample.app.lesson.LessonRepository;
|
|
||||||
import io.openvidu.sample.app.user.User;
|
|
||||||
import io.openvidu.sample.app.user.UserRepository;
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
public class DatabaseInitializer implements CommandLineRunner {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserRepository userRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private LessonRepository lessonRepository;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run(String... args) throws Exception {
|
|
||||||
|
|
||||||
//Sample users
|
|
||||||
User user1 = new User("student1@gmail.com", "pass", "Student Imprudent", "ROLE_STUDENT");
|
|
||||||
User user2 = new User("student2@gmail.com", "pass", "Student Concludent", "ROLE_STUDENT");
|
|
||||||
User user3 = new User("teacher@gmail.com", "pass", "Teacher Cheater", "ROLE_TEACHER");
|
|
||||||
|
|
||||||
//Saving users
|
|
||||||
userRepository.save(user1);
|
|
||||||
userRepository.save(user2);
|
|
||||||
userRepository.save(user3);
|
|
||||||
|
|
||||||
//Sample lessons
|
|
||||||
Lesson c1 = new Lesson("Lesson number 1", user3);
|
|
||||||
Lesson c2 = new Lesson("Lesson number 2", user3);
|
|
||||||
|
|
||||||
c1.getAttenders().add(user1);
|
|
||||||
c1.getAttenders().add(user2);
|
|
||||||
c1.getAttenders().add(user3);
|
|
||||||
|
|
||||||
c2.getAttenders().add(user1);
|
|
||||||
c2.getAttenders().add(user3);
|
|
||||||
|
|
||||||
//Saving lessons
|
|
||||||
lessonRepository.save(c1);
|
|
||||||
lessonRepository.save(c2);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
package io.openvidu.sample.app.lesson;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.GeneratedValue;
|
|
||||||
import javax.persistence.GenerationType;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.ManyToMany;
|
|
||||||
import javax.persistence.ManyToOne;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonView;
|
|
||||||
|
|
||||||
import io.openvidu.sample.app.user.User;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
public class Lesson {
|
|
||||||
|
|
||||||
public interface SimpleCourseList {}
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
|
||||||
@JsonView(SimpleCourseList.class)
|
|
||||||
private long id;
|
|
||||||
|
|
||||||
@JsonView(SimpleCourseList.class)
|
|
||||||
private String title;
|
|
||||||
|
|
||||||
@ManyToOne
|
|
||||||
private User teacher;
|
|
||||||
|
|
||||||
@ManyToMany
|
|
||||||
private Set<User> attenders;
|
|
||||||
|
|
||||||
public Lesson() {}
|
|
||||||
|
|
||||||
public Lesson(String title, User teacher) {
|
|
||||||
this.title = title;
|
|
||||||
this.teacher = teacher;
|
|
||||||
this.attenders = new HashSet<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(long id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTitle() {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(String title) {
|
|
||||||
this.title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public User getTeacher() {
|
|
||||||
return teacher;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTeacher(User teacher) {
|
|
||||||
this.teacher = teacher;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<User> getAttenders() {
|
|
||||||
return attenders;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAttenders(Set<User> attenders) {
|
|
||||||
this.attenders = attenders;
|
|
||||||
}
|
|
||||||
|
|
||||||
//To make 'user.getLesson().remove(lesson)' possible
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other){
|
|
||||||
if (other == null) return false;
|
|
||||||
if (other == this) return true;
|
|
||||||
if (!(other instanceof Lesson))return false;
|
|
||||||
Lesson otherCourse = (Lesson)other;
|
|
||||||
return (otherCourse.id == this.id);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,299 +0,0 @@
|
||||||
package io.openvidu.sample.app.lesson;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.apache.commons.validator.routines.EmailValidator;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import io.openvidu.sample.app.user.User;
|
|
||||||
import io.openvidu.sample.app.user.UserComponent;
|
|
||||||
import io.openvidu.sample.app.user.UserRepository;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api-lessons")
|
|
||||||
public class LessonController {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private LessonRepository lessonRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserRepository userRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserComponent user;
|
|
||||||
|
|
||||||
private class AddAttendersResponse {
|
|
||||||
public Collection<User> attendersAdded;
|
|
||||||
public Collection<User> attendersAlreadyAdded;
|
|
||||||
public Collection<String> emailsInvalid;
|
|
||||||
public Collection<String> emailsValidNotRegistered;
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
|
|
||||||
public ResponseEntity<Collection<Lesson>> getLessons(@PathVariable(value="id") String id){
|
|
||||||
if (!this.userIsLogged()){
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
long id_i = -1;
|
|
||||||
try{
|
|
||||||
id_i = Long.parseLong(id);
|
|
||||||
}catch(NumberFormatException e){
|
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
Set<Long> s = new HashSet<>();
|
|
||||||
s.add(id_i);
|
|
||||||
Collection<User> users = userRepository.findAll(s);
|
|
||||||
Collection<Lesson> lessons = new HashSet<>();
|
|
||||||
lessons = lessonRepository.findByAttenders(users);
|
|
||||||
return new ResponseEntity<>(lessons ,HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value = "/lesson/{id}", method = RequestMethod.GET)
|
|
||||||
public ResponseEntity<Lesson> getLesson(@PathVariable(value="id") String id){
|
|
||||||
if (!this.userIsLogged()){
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
long id_i = -1;
|
|
||||||
try{
|
|
||||||
id_i = Long.parseLong(id);
|
|
||||||
}catch(NumberFormatException e){
|
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
Lesson lesson = lessonRepository.findOne(id_i);
|
|
||||||
return new ResponseEntity<>(lesson ,HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value = "/new", method = RequestMethod.POST)
|
|
||||||
public ResponseEntity<Lesson> newLesson(@RequestBody Lesson lesson) {
|
|
||||||
if (!this.userIsLogged()){
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
User userLogged = user.getLoggedUser();
|
|
||||||
|
|
||||||
if (!user.hasRoleTeacher()){
|
|
||||||
// Students are not authorized to add new lessons
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updating lesson ('teacher', 'attenders')
|
|
||||||
lesson.setTeacher(userLogged);
|
|
||||||
lesson.getAttenders().add(userLogged);
|
|
||||||
|
|
||||||
// Saving lesson
|
|
||||||
lessonRepository.save(lesson);
|
|
||||||
lessonRepository.flush();
|
|
||||||
|
|
||||||
lesson = lessonRepository.findOne(lesson.getId());
|
|
||||||
return new ResponseEntity<>(lesson, HttpStatus.CREATED);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value = "/edit", method = RequestMethod.PUT)
|
|
||||||
public ResponseEntity<Lesson> modifyLesson(@RequestBody Lesson lesson) {
|
|
||||||
if (!this.userIsLogged()){
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
Lesson c = lessonRepository.findOne(lesson.getId());
|
|
||||||
|
|
||||||
checkAuthorization(c, c.getTeacher());
|
|
||||||
|
|
||||||
//Modifying the lesson attributes
|
|
||||||
c.setTitle(lesson.getTitle());
|
|
||||||
|
|
||||||
//Saving the modified lesson
|
|
||||||
lessonRepository.save(c);
|
|
||||||
return new ResponseEntity<>(c, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value = "/delete/{lessonId}", method = RequestMethod.DELETE)
|
|
||||||
public ResponseEntity<Lesson> deleteLesson(@PathVariable(value="lessonId") String lessonId) {
|
|
||||||
if (!this.userIsLogged()){
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
long id_lesson = -1;
|
|
||||||
try{
|
|
||||||
id_lesson = Long.parseLong(lessonId);
|
|
||||||
}catch(NumberFormatException e){
|
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
Lesson c = lessonRepository.findOne(id_lesson);
|
|
||||||
|
|
||||||
checkAuthorization(c, c.getTeacher());
|
|
||||||
|
|
||||||
//Removing the deleted lesson from its attenders
|
|
||||||
Collection<Lesson> lessons = new HashSet<>();
|
|
||||||
lessons.add(c);
|
|
||||||
Collection<User> users = userRepository.findByLessons(lessons);
|
|
||||||
for(User u: users){
|
|
||||||
u.getLessons().remove(c);
|
|
||||||
}
|
|
||||||
userRepository.save(users);
|
|
||||||
c.getAttenders().clear();
|
|
||||||
|
|
||||||
lessonRepository.delete(c);
|
|
||||||
return new ResponseEntity<>(c, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value = "/edit/add-attenders/lesson/{lessonId}", method = RequestMethod.PUT)
|
|
||||||
public ResponseEntity<AddAttendersResponse> addAttenders(
|
|
||||||
@RequestBody String[] attenderEmails,
|
|
||||||
@PathVariable(value="lessonId") String lessonId)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (!this.userIsLogged()){
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
long id_lesson = -1;
|
|
||||||
try{
|
|
||||||
id_lesson = Long.parseLong(lessonId);
|
|
||||||
}catch(NumberFormatException e){
|
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
Lesson c = lessonRepository.findOne(id_lesson);
|
|
||||||
|
|
||||||
checkAuthorization(c, c.getTeacher());
|
|
||||||
|
|
||||||
//Strings with a valid email format
|
|
||||||
Set<String> attenderEmailsValid = new HashSet<>();
|
|
||||||
//Strings with an invalid email format
|
|
||||||
Set<String> attenderEmailsInvalid = new HashSet<>();
|
|
||||||
//Strings with a valid email format but no registered in the application
|
|
||||||
Set<String> attenderEmailsNotRegistered = new HashSet<>();
|
|
||||||
|
|
||||||
EmailValidator emailValidator = EmailValidator.getInstance();
|
|
||||||
|
|
||||||
for (int i = 0; i < attenderEmails.length; i++){
|
|
||||||
if (emailValidator.isValid(attenderEmails[i])) {
|
|
||||||
attenderEmailsValid.add(attenderEmails[i]);
|
|
||||||
} else {
|
|
||||||
attenderEmailsInvalid.add(attenderEmails[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<User> newPossibleAttenders = userRepository.findByNameIn(attenderEmailsValid);
|
|
||||||
Collection<User> newAddedAttenders = new HashSet<>();
|
|
||||||
Collection<User> alreadyAddedAttenders = new HashSet<>();
|
|
||||||
|
|
||||||
for (String s : attenderEmailsValid){
|
|
||||||
if (!this.userListContainsEmail(newPossibleAttenders, s)){
|
|
||||||
attenderEmailsNotRegistered.add(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (User attender : newPossibleAttenders){
|
|
||||||
boolean newAtt = true;
|
|
||||||
if (!attender.getLessons().contains(c)) attender.getLessons().add(c); else newAtt = false;
|
|
||||||
if (!c.getAttenders().contains(attender)) c.getAttenders().add(attender); else newAtt = false;
|
|
||||||
if (newAtt) newAddedAttenders.add(attender); else alreadyAddedAttenders.add(attender);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Saving the attenders (all of them, just in case a field of the bidirectional relationship is missing in a Lesson or a User)
|
|
||||||
userRepository.save(newPossibleAttenders);
|
|
||||||
//Saving the modified lesson
|
|
||||||
lessonRepository.save(c);
|
|
||||||
|
|
||||||
AddAttendersResponse customResponse = new AddAttendersResponse();
|
|
||||||
customResponse.attendersAdded = newAddedAttenders;
|
|
||||||
customResponse.attendersAlreadyAdded = alreadyAddedAttenders;
|
|
||||||
customResponse.emailsInvalid = attenderEmailsInvalid;
|
|
||||||
customResponse.emailsValidNotRegistered = attenderEmailsNotRegistered;
|
|
||||||
|
|
||||||
return new ResponseEntity<>(customResponse, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value = "/edit/delete-attenders", method = RequestMethod.PUT)
|
|
||||||
public ResponseEntity<Set<User>> deleteAttenders(@RequestBody Lesson lesson) {
|
|
||||||
if (!this.userIsLogged()){
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
Lesson c = lessonRepository.findOne(lesson.getId());
|
|
||||||
|
|
||||||
checkAuthorization(c, c.getTeacher());
|
|
||||||
|
|
||||||
Set<Lesson> setLesson = new HashSet<>();
|
|
||||||
setLesson.add(c);
|
|
||||||
Collection<User> lessonAttenders = userRepository.findByLessons(setLesson);
|
|
||||||
|
|
||||||
for (User attender : lessonAttenders){
|
|
||||||
if (!lesson.getAttenders().contains(attender)){
|
|
||||||
attender.getLessons().remove(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
userRepository.save(lessonAttenders);
|
|
||||||
|
|
||||||
//Modifying the lesson attenders
|
|
||||||
c.setAttenders(lesson.getAttenders());
|
|
||||||
//Saving the modified lesson
|
|
||||||
lessonRepository.save(c);
|
|
||||||
return new ResponseEntity<>(c.getAttenders(), HttpStatus.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//Login checking method for the backend
|
|
||||||
private boolean userIsLogged(){
|
|
||||||
if (!user.isLoggedUser()) {
|
|
||||||
System.out.println("Not user logged");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Authorization checking for editing and deleting lessons (the teacher must own the Lesson)
|
|
||||||
private ResponseEntity<Object> checkAuthorization(Object o, User u){
|
|
||||||
if(o == null){
|
|
||||||
//The object does not exist
|
|
||||||
return new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
|
|
||||||
}
|
|
||||||
if(!this.user.getLoggedUser().equals(u)){
|
|
||||||
//The teacher is not authorized to edit it if he is not its owner
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Checks if a User collection contains a user with certain email
|
|
||||||
private boolean userListContainsEmail(Collection<User> users, String email){
|
|
||||||
boolean isContained = false;
|
|
||||||
for (User u : users){
|
|
||||||
if (u.getName().equals(email)) {
|
|
||||||
isContained = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isContained;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
package io.openvidu.sample.app.lesson;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
import io.openvidu.sample.app.user.User;
|
|
||||||
|
|
||||||
public interface LessonRepository extends JpaRepository<Lesson, Long> {
|
|
||||||
|
|
||||||
public Collection<Lesson> findByAttenders(Collection<User> users);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
package io.openvidu.sample.app.security;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpSession;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import io.openvidu.sample.app.user.User;
|
|
||||||
import io.openvidu.sample.app.user.UserComponent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is used to provide REST endpoints to logIn and logOut to the
|
|
||||||
* service. These endpoints are used by Angular 2 SPA client application.
|
|
||||||
*
|
|
||||||
* NOTE: This class is not intended to be modified by app developer.
|
|
||||||
*/
|
|
||||||
@RestController
|
|
||||||
public class LoginController {
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(LoginController.class);
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserComponent userComponent;
|
|
||||||
|
|
||||||
@RequestMapping("/api-logIn")
|
|
||||||
public ResponseEntity<User> logIn() {
|
|
||||||
|
|
||||||
System.out.println("Logging in...");
|
|
||||||
|
|
||||||
if (!userComponent.isLoggedUser()) {
|
|
||||||
|
|
||||||
System.out.println("Not user logged");
|
|
||||||
|
|
||||||
log.info("Not user logged");
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
} else {
|
|
||||||
User loggedUser = userComponent.getLoggedUser();
|
|
||||||
|
|
||||||
System.out.println("Logged as " + loggedUser.getName());
|
|
||||||
|
|
||||||
log.info("Logged as " + loggedUser.getName());
|
|
||||||
return new ResponseEntity<>(loggedUser, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping("/api-logOut")
|
|
||||||
public ResponseEntity<Boolean> logOut(HttpSession session) {
|
|
||||||
|
|
||||||
System.out.println("Logging out...");
|
|
||||||
|
|
||||||
if (!userComponent.isLoggedUser()) {
|
|
||||||
log.info("No user logged");
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
} else {
|
|
||||||
session.invalidate();
|
|
||||||
log.info("Logged out");
|
|
||||||
return new ResponseEntity<>(true, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
package io.openvidu.sample.app.security;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Security configuration. In this class several aspects
|
|
||||||
* related to security can be configured:
|
|
||||||
* Security behavior: Login method, session management, CSRF, etc..
|
|
||||||
* Authentication provider: Responsible to authenticate users. In this
|
|
||||||
* example, we use an instance of UserRepositoryAuthProvider, that authenticate
|
|
||||||
* users stored in a Spring Data database.
|
|
||||||
* URL Access Authorization: Access to http URLs depending on Authenticated
|
|
||||||
* vs anonymous users and also based on user role.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* NOTE: The only part of this class intended for app developer customization is
|
|
||||||
* the method configureUrlAuthorization. App developer should
|
|
||||||
* decide what URLs are accessible by what user role.
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public UserRepositoryAuthProvider userRepoAuthProvider;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
|
||||||
|
|
||||||
configureUrlAuthorization(http);
|
|
||||||
|
|
||||||
// Disable CSRF protection (it is difficult to implement with ng2)
|
|
||||||
http.csrf().disable();
|
|
||||||
|
|
||||||
// Use Http Basic Authentication
|
|
||||||
http.httpBasic();
|
|
||||||
|
|
||||||
// Do not redirect when logout
|
|
||||||
http.logout().logoutSuccessHandler((rq, rs, a) -> {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configureUrlAuthorization(HttpSecurity http) throws Exception {
|
|
||||||
|
|
||||||
// APP: This rules have to be changed by app developer
|
|
||||||
|
|
||||||
// URLs that need authentication to access to it
|
|
||||||
//Lessons API
|
|
||||||
http.authorizeRequests().antMatchers(HttpMethod.GET, "/api-lessons/**").hasAnyRole("TEACHER", "STUDENT");
|
|
||||||
http.authorizeRequests().antMatchers(HttpMethod.POST, "/api-lessons/**").hasRole("TEACHER");
|
|
||||||
http.authorizeRequests().antMatchers(HttpMethod.PUT, "/api-lessons/**").hasRole("TEACHER");
|
|
||||||
http.authorizeRequests().antMatchers(HttpMethod.DELETE, "/api-lessons/**").hasRole("TEACHER");
|
|
||||||
|
|
||||||
http.authorizeRequests().antMatchers(HttpMethod.POST, "/api-sessions/**").authenticated();
|
|
||||||
|
|
||||||
// Other URLs can be accessed without authentication
|
|
||||||
http.authorizeRequests().anyRequest().permitAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
|
||||||
|
|
||||||
// Database authentication provider
|
|
||||||
auth.authenticationProvider(userRepoAuthProvider);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
package io.openvidu.sample.app.security;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
||||||
import org.springframework.security.core.Authentication;
|
|
||||||
import org.springframework.security.core.AuthenticationException;
|
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import io.openvidu.sample.app.user.User;
|
|
||||||
import io.openvidu.sample.app.user.UserComponent;
|
|
||||||
import io.openvidu.sample.app.user.UserRepository;
|
|
||||||
/**
|
|
||||||
* This class is used to check http credentials against database data. Also it
|
|
||||||
* is responsible to set database user info into userComponent, a session scoped
|
|
||||||
* bean that holds session user information.
|
|
||||||
*
|
|
||||||
* NOTE: This class is not intended to be modified by app developer.
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class UserRepositoryAuthProvider implements AuthenticationProvider {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserRepository userRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserComponent userComponent;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
|
||||||
|
|
||||||
String username = authentication.getName();
|
|
||||||
String password = (String) authentication.getCredentials();
|
|
||||||
|
|
||||||
User user = userRepository.findByName(username);
|
|
||||||
|
|
||||||
if (user == null) {
|
|
||||||
throw new BadCredentialsException("User not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!new BCryptPasswordEncoder().matches(password, user.getPasswordHash())) {
|
|
||||||
|
|
||||||
throw new BadCredentialsException("Wrong password");
|
|
||||||
} else {
|
|
||||||
|
|
||||||
userComponent.setLoggedUser(user);
|
|
||||||
|
|
||||||
List<GrantedAuthority> roles = new ArrayList<>();
|
|
||||||
for (String role : user.getRoles()) {
|
|
||||||
roles.add(new SimpleGrantedAuthority(role));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new UsernamePasswordAuthenticationToken(username, password, roles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean supports(Class<?> authenticationObject) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,234 +0,0 @@
|
||||||
package io.openvidu.sample.app.session_manager;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import io.openvidu.java.client.OpenVidu;
|
|
||||||
import io.openvidu.java.client.Session;
|
|
||||||
import io.openvidu.java.client.TokenOptions;
|
|
||||||
import io.openvidu.sample.app.lesson.Lesson;
|
|
||||||
import io.openvidu.sample.app.lesson.LessonRepository;
|
|
||||||
import io.openvidu.sample.app.user.User;
|
|
||||||
import io.openvidu.sample.app.user.UserComponent;
|
|
||||||
import io.openvidu.java.client.OpenViduRole;
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api-sessions")
|
|
||||||
public class SessionController {
|
|
||||||
|
|
||||||
OpenVidu openVidu;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private LessonRepository lessonRepository;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserComponent user;
|
|
||||||
|
|
||||||
private Map<Long, Session> lessonIdSession = new ConcurrentHashMap<>();
|
|
||||||
private Map<String, Map<Long, String>> sessionIdUserIdToken = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private String OPENVIDU_URL;
|
|
||||||
private String SECRET;
|
|
||||||
|
|
||||||
public SessionController(@Value("${openvidu.secret}") String secret, @Value("${openvidu.url}") String openviduUrl){
|
|
||||||
this.SECRET = secret;
|
|
||||||
this.OPENVIDU_URL = openviduUrl;
|
|
||||||
this.openVidu = new OpenVidu(OPENVIDU_URL, SECRET);
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(value = "/create-session", method = RequestMethod.POST)
|
|
||||||
public ResponseEntity<JSONObject> createSession(@RequestBody String lessonId) {
|
|
||||||
|
|
||||||
if (!this.userIsLogged()) {
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!user.hasRoleTeacher()) {
|
|
||||||
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
long id_lesson = -1;
|
|
||||||
try {
|
|
||||||
id_lesson = Long.parseLong(lessonId);
|
|
||||||
} catch(NumberFormatException e){
|
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
Lesson c = lessonRepository.findOne(id_lesson);
|
|
||||||
|
|
||||||
if (!checkAuthorization(c, c.getTeacher())){
|
|
||||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject responseJson = new JSONObject();
|
|
||||||
|
|
||||||
if(this.lessonIdSession.get(id_lesson) != null) {
|
|
||||||
// If there's already a valid sessionId for this lesson, not necessary to ask for a new one
|
|
||||||
responseJson.put(0, this.lessonIdSession.get(id_lesson).getSessionId());
|
|
||||||
return new ResponseEntity<>(responseJson, HttpStatus.OK);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
// IMPORTANT STUFF
|
|
||||||
Session session = this.openVidu.createSession();
|
|
||||||
String sessionId = session.getSessionId();
|
|
||||||
// END IMPORTANT STUFF
|
|
||||||
|
|
||||||
this.lessonIdSession.put(id_lesson, session);
|
|
||||||
this.sessionIdUserIdToken.put(sessionId, new HashMap<>());
|
|
||||||
|
|
||||||
showMap();
|
|
||||||
|
|
||||||
responseJson.put(0, sessionId);
|
|
||||||
|
|
||||||
return new ResponseEntity<>(responseJson, HttpStatus.OK);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return getErrorResponse(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(value = "/generate-token", method = RequestMethod.POST)
|
|
||||||
public ResponseEntity<JSONObject> generateToken(@RequestBody String lessonId) {
|
|
||||||
|
|
||||||
if (!this.userIsLogged()) {
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
long id_lesson = -1;
|
|
||||||
try{
|
|
||||||
id_lesson = Long.parseLong(lessonId);
|
|
||||||
}catch(NumberFormatException e){
|
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
Lesson c = lessonRepository.findOne(id_lesson);
|
|
||||||
|
|
||||||
if (!checkAuthorizationUsers(c, c.getAttenders())){
|
|
||||||
System.out.println("Not authorizedd");
|
|
||||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.lessonIdSession.get(id_lesson) == null){
|
|
||||||
System.out.println("There's no Session fot this lesson");
|
|
||||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
Session session = this.lessonIdSession.get(id_lesson);
|
|
||||||
OpenViduRole role = user.hasRoleTeacher() ? OpenViduRole.PUBLISHER : OpenViduRole.SUBSCRIBER;
|
|
||||||
|
|
||||||
JSONObject responseJson = new JSONObject();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// IMPORTANT STUFF
|
|
||||||
TokenOptions tokenOpts = new TokenOptions.Builder()
|
|
||||||
.role(role)
|
|
||||||
.data("SERVER=" + this.user.getLoggedUser().getName())
|
|
||||||
.build();
|
|
||||||
String token = (String) this.lessonIdSession.get(id_lesson).generateToken(tokenOpts);
|
|
||||||
// END IMPORTANT STUFF
|
|
||||||
|
|
||||||
this.sessionIdUserIdToken.get(session.getSessionId()).put(this.user.getLoggedUser().getId(), token);
|
|
||||||
|
|
||||||
responseJson.put(0, session.getSessionId());
|
|
||||||
responseJson.put(1, token);
|
|
||||||
|
|
||||||
showMap();
|
|
||||||
|
|
||||||
return new ResponseEntity<>(responseJson, HttpStatus.OK);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return getErrorResponse(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value = "/remove-user", method = RequestMethod.POST)
|
|
||||||
public ResponseEntity<JSONObject> removeUser(@RequestBody String lessonId) throws Exception {
|
|
||||||
|
|
||||||
if (!this.userIsLogged()) {
|
|
||||||
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
|
||||||
}
|
|
||||||
|
|
||||||
long id_lesson = -1;
|
|
||||||
try{
|
|
||||||
id_lesson = Long.parseLong(lessonId);
|
|
||||||
}catch(NumberFormatException e){
|
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
Lesson c = lessonRepository.findOne(id_lesson);
|
|
||||||
|
|
||||||
if (!checkAuthorizationUsers(c, c.getAttenders())){
|
|
||||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.lessonIdSession.get(id_lesson) == null){
|
|
||||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
String sessionId = this.lessonIdSession.get(id_lesson).getSessionId();
|
|
||||||
if (this.sessionIdUserIdToken.get(sessionId).remove(this.user.getLoggedUser().getId()) != null){
|
|
||||||
// This user has left the lesson
|
|
||||||
if(this.sessionIdUserIdToken.get(sessionId).isEmpty()){
|
|
||||||
// The last user has left the lesson
|
|
||||||
this.lessonIdSession.remove(id_lesson);
|
|
||||||
this.sessionIdUserIdToken.remove(sessionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
showMap();
|
|
||||||
|
|
||||||
return new ResponseEntity<>(HttpStatus.OK);
|
|
||||||
} else {
|
|
||||||
System.out.println("Problems in the app server: the user didn't have a valid token");
|
|
||||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void showMap(){
|
|
||||||
System.out.println("------------------------------");
|
|
||||||
System.out.println(this.lessonIdSession.toString());
|
|
||||||
System.out.println(this.sessionIdUserIdToken.toString());
|
|
||||||
System.out.println("------------------------------");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private boolean userIsLogged(){
|
|
||||||
if (!user.isLoggedUser()) {
|
|
||||||
System.out.println("Not user logged");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResponseEntity<JSONObject> getErrorResponse(Exception e){
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
json.put("cause", e.getCause());
|
|
||||||
json.put("error", e.getMessage());
|
|
||||||
json.put("exception", e.getClass());
|
|
||||||
return new ResponseEntity<>(json, HttpStatus.INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authorization checking for creating or joining a certain lesson
|
|
||||||
private boolean checkAuthorization(Object o, User u){
|
|
||||||
return !(o == null || !this.user.getLoggedUser().equals(u));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authorization checking for joining a session (the user must be an attender)
|
|
||||||
private boolean checkAuthorizationUsers(Object o, Collection<User> users){
|
|
||||||
return !(o == null || !users.contains(this.user.getLoggedUser()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,118 +0,0 @@
|
||||||
package io.openvidu.sample.app.user;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.FetchType;
|
|
||||||
import javax.persistence.GeneratedValue;
|
|
||||||
import javax.persistence.GenerationType;
|
|
||||||
import javax.persistence.ElementCollection;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.ManyToMany;
|
|
||||||
|
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
|
|
||||||
import io.openvidu.sample.app.lesson.Lesson;
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
public class User {
|
|
||||||
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
|
||||||
private long id;
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
private String nickName;
|
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
private String passwordHash;
|
|
||||||
|
|
||||||
@ElementCollection(fetch = FetchType.EAGER)
|
|
||||||
private List<String> roles;
|
|
||||||
|
|
||||||
//It is ignored in order to avoid infinite recursiveness
|
|
||||||
//This makes necessary another interaction with the database (after login to retrieve the lessons of the user)
|
|
||||||
@JsonIgnore
|
|
||||||
@ManyToMany(mappedBy="attenders")
|
|
||||||
private Set<Lesson> lessons;
|
|
||||||
|
|
||||||
public User() {}
|
|
||||||
|
|
||||||
public User(String name, String password, String nickName, String... roles){
|
|
||||||
this.name = name;
|
|
||||||
this.passwordHash = new BCryptPasswordEncoder().encode(password);
|
|
||||||
this.roles = new ArrayList<>(Arrays.asList(roles));
|
|
||||||
this.nickName = nickName;
|
|
||||||
this.lessons = new HashSet<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(Long id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPasswordHash() {
|
|
||||||
return passwordHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPasswordHash(String passwordHash) {
|
|
||||||
this.passwordHash = passwordHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getRoles() {
|
|
||||||
return roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRoles(List<String> roles) {
|
|
||||||
this.roles = roles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNickName() {
|
|
||||||
return nickName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNickName(String nickName) {
|
|
||||||
this.nickName = nickName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<Lesson> getLessons() {
|
|
||||||
return lessons;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLessons(Set<Lesson> lessons) {
|
|
||||||
this.lessons = lessons;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other){
|
|
||||||
if (other == null) return false;
|
|
||||||
if (other == this) return true;
|
|
||||||
if (!(other instanceof User))return false;
|
|
||||||
User otherUser = (User)other;
|
|
||||||
return ((otherUser.id == this.id) && (otherUser.name.equals(this.name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return name.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
package io.openvidu.sample.app.user;
|
|
||||||
|
|
||||||
import org.springframework.context.annotation.Scope;
|
|
||||||
import org.springframework.context.annotation.ScopedProxyMode;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is designed to manage the information for the user while he is
|
|
||||||
* logged in the service. This object can be used in any other @Component
|
|
||||||
* auto-wiring it as usual.
|
|
||||||
*
|
|
||||||
* Instances of this class are never sent to the user in any REST endpoint. It
|
|
||||||
* can hold sensible information that can not be known in the client.
|
|
||||||
*
|
|
||||||
* NOTE: This class is intended to be extended by developer adding new
|
|
||||||
* attributes. Current attributes can not be removed because they are used in
|
|
||||||
* authentication procedures.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
|
|
||||||
public class UserComponent {
|
|
||||||
|
|
||||||
private User user;
|
|
||||||
|
|
||||||
public User getLoggedUser() {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLoggedUser(User user) {
|
|
||||||
this.user = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isLoggedUser() {
|
|
||||||
return this.user != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasRoleTeacher() {
|
|
||||||
return this.user.getRoles().contains("ROLE_TEACHER");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package io.openvidu.sample.app.user;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import org.apache.commons.validator.routines.EmailValidator;
|
|
||||||
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api-users")
|
|
||||||
public class UserController {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserRepository userRepository;
|
|
||||||
|
|
||||||
//userData: [name, pass, nickName, role]
|
|
||||||
@RequestMapping(value = "/new", method = RequestMethod.POST)
|
|
||||||
public ResponseEntity<User> newUser(@RequestBody String[] userData) throws Exception {
|
|
||||||
|
|
||||||
System.out.println("Signing up a user...");
|
|
||||||
|
|
||||||
//If the email is not already in use
|
|
||||||
if(userRepository.findByName(userData[0]) == null) {
|
|
||||||
|
|
||||||
//If the email has a valid format
|
|
||||||
if (EmailValidator.getInstance().isValid(userData[0])){
|
|
||||||
String role = (String) userData[3];
|
|
||||||
System.out.println("Email and password are valid. Role of the new user: " + role);
|
|
||||||
if(role == null){
|
|
||||||
userData[3] = "ROLE_STUDENT";
|
|
||||||
} else if (role.equals("teacher")) {
|
|
||||||
userData[3] = "ROLE_TEACHER";
|
|
||||||
} else {
|
|
||||||
userData[3] = "ROLE_STUDENT";
|
|
||||||
}
|
|
||||||
User newUser = new User(userData[0], userData[1], userData[2], userData[3]);
|
|
||||||
userRepository.save(newUser);
|
|
||||||
return new ResponseEntity<>(newUser, HttpStatus.CREATED);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
System.out.println("Email NOT valid");
|
|
||||||
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
System.out.println("Email already in use");
|
|
||||||
return new ResponseEntity<>(HttpStatus.CONFLICT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package io.openvidu.sample.app.user;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
|
||||||
|
|
||||||
import io.openvidu.sample.app.lesson.Lesson;
|
|
||||||
|
|
||||||
public interface UserRepository extends JpaRepository<User, Long>{
|
|
||||||
|
|
||||||
public User findByName(String name);
|
|
||||||
|
|
||||||
public Collection<User> findByNameIn(Collection<String> names);
|
|
||||||
|
|
||||||
public Collection<User> findByLessons(Collection<Lesson> lessons);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
spring.profiles.active=container
|
|
||||||
|
|
||||||
server.port: 5000
|
|
||||||
server.ssl.enabled: true
|
|
||||||
server.ssl.key-store: classpath:openvidu-selfsigned.jks
|
|
||||||
server.ssl.key-store-password: openvidu
|
|
||||||
server.ssl.key-store-type: JKS
|
|
||||||
server.ssl.key-alias: openvidu-selfsigned
|
|
||||||
|
|
||||||
spring.datasource.url: jdbc:mysql://mysql:3306/openvidu_sample_app
|
|
||||||
spring.datasource.username: op-root
|
|
||||||
spring.datasource.password: pass
|
|
||||||
spring.datasource.driverClassName: com.mysql.jdbc.Driver
|
|
||||||
spring.datasource.platform: mysql
|
|
||||||
spring.jpa.database: MYSQL
|
|
||||||
spring.jpa.hibernate.ddl-auto: create-drop
|
|
||||||
|
|
||||||
openvidu.url: https://service-openvidu-server-kms:8443/
|
|
||||||
openvidu.secret: MY_SECRET
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
server.port: 5000
|
|
||||||
server.ssl.enabled: true
|
|
||||||
server.ssl.key-store: classpath:openvidu-selfsigned.jks
|
|
||||||
server.ssl.key-store-password: openvidu
|
|
||||||
server.ssl.key-store-type: JKS
|
|
||||||
server.ssl.key-alias: openvidu-selfsigned
|
|
||||||
|
|
||||||
spring.datasource.url: jdbc:mysql://localhost/openvidu_sample_app
|
|
||||||
spring.datasource.username: root
|
|
||||||
spring.datasource.password: pass
|
|
||||||
spring.datasource.driverClassName: com.mysql.jdbc.Driver
|
|
||||||
spring.jpa.database: MYSQL
|
|
||||||
spring.jpa.hibernate.ddl-auto: create-drop
|
|
||||||
|
|
||||||
openvidu.url: https://localhost:8443/
|
|
||||||
openvidu.secret: MY_SECRET
|
|
Binary file not shown.
|
@ -1,38 +0,0 @@
|
||||||
package io.openvidu.sample.app.test;
|
|
||||||
|
|
||||||
import junit.framework.Test;
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
import junit.framework.TestSuite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unit test for simple App.
|
|
||||||
*/
|
|
||||||
public class AppTest
|
|
||||||
extends TestCase
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Create the test case
|
|
||||||
*
|
|
||||||
* @param testName name of the test case
|
|
||||||
*/
|
|
||||||
public AppTest( String testName )
|
|
||||||
{
|
|
||||||
super( testName );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the suite of tests being tested
|
|
||||||
*/
|
|
||||||
public static Test suite()
|
|
||||||
{
|
|
||||||
return new TestSuite( AppTest.class );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rigourous Test :-)
|
|
||||||
*/
|
|
||||||
public void testApp()
|
|
||||||
{
|
|
||||||
assertTrue( true );
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue