Compare commits
No commits in common. "master" and "v1" have entirely different histories.
|
@ -2,11 +2,10 @@
|
||||||
.git
|
.git
|
||||||
.gitignore
|
.gitignore
|
||||||
README.md
|
README.md
|
||||||
SECURITY.md
|
|
||||||
.github
|
|
||||||
|
|
||||||
# Rails
|
# Rails
|
||||||
.env
|
.env
|
||||||
|
.env.prod
|
||||||
*.rbc
|
*.rbc
|
||||||
capybara-*.html
|
capybara-*.html
|
||||||
.rspec
|
.rspec
|
||||||
|
@ -14,16 +13,14 @@ log
|
||||||
tmp
|
tmp
|
||||||
/db/**/*.sqlite3
|
/db/**/*.sqlite3
|
||||||
/db/**/*.sqlite3-journal
|
/db/**/*.sqlite3-journal
|
||||||
/db/production
|
public/system
|
||||||
/db/production-postgres
|
|
||||||
public/assets
|
|
||||||
public/b
|
|
||||||
coverage/
|
coverage/
|
||||||
|
spec/tmp
|
||||||
.rvmrc
|
.rvmrc
|
||||||
vendor/bundle
|
.byebug_history
|
||||||
.bundle
|
|
||||||
Dockerfile
|
# docker
|
||||||
.gitlab-ci.yml
|
docker-compose.yml
|
||||||
.rubocop.yml
|
production-compose.yml
|
||||||
spec
|
|
||||||
test
|
.atom
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
<!---
|
|
||||||
IMPORTANT
|
|
||||||
This template is mandatory for all Pull Requests.
|
|
||||||
Please follow the template to ensure your Pull Request is reviewed.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--- Provide a general summary of your changes in the Title above -->
|
|
||||||
|
|
||||||
## Description
|
|
||||||
<!--- Describe your changes in detail -->
|
|
||||||
|
|
||||||
## Testing Steps
|
|
||||||
<!--- Please describe in detail how to test your changes. -->
|
|
||||||
|
|
||||||
## Screenshots (if appropriate):
|
|
||||||
<!--- Please include screenshots that may help to visualize your changes. -->
|
|
|
@ -1,95 +0,0 @@
|
||||||
env:
|
|
||||||
RUBY_VERSION: 2.7
|
|
||||||
|
|
||||||
name: CI Build Pre-Release
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [prereleased]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
main:
|
|
||||||
name: Build Docker Image
|
|
||||||
env:
|
|
||||||
DOCKER_REPOSITORY: ${{ secrets.DOCKER_REPOSITORY }}
|
|
||||||
DOCKER_BUILD_ENABLED: ${{ secrets.DOCKER_BUILD_ENABLED }}
|
|
||||||
DOCKER_BUILD_ALTERNATE_ENABLED: ${{ secrets.DOCKER_BUILD_ALTERNATE_ENABLED }}
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
|
|
||||||
- name: Cache Docker layers
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /tmp/.buildx-cache
|
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-buildx-
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Extract Docker Repository
|
|
||||||
id: ci_docker_repository
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
shell: bash
|
|
||||||
run: echo "##[set-output name=repository;]$(echo ${DOCKER_REPOSITORY:-$GITHUB_REPOSITORY})"
|
|
||||||
|
|
||||||
- name: Extract Tag Release
|
|
||||||
id: ci_tag_release_version
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
shell: bash
|
|
||||||
run: echo "##[set-output name=tag;]$(echo ${GITHUB_REF#refs/tags/} | cut -c 9-)"
|
|
||||||
|
|
||||||
- name: Build and Push
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
"${{ steps.ci_docker_repository.outputs.repository }}:v${{ steps.ci_tag_release_version.outputs.tag }}"
|
|
||||||
build-args: "version_code=release-${{ steps.ci_tag_release_version.outputs.tag }}"
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new
|
|
||||||
|
|
||||||
# Alternate Images with alpine
|
|
||||||
- name: Alternate Alpine Build and Push
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true') && contains(env.DOCKER_BUILD_ALTERNATE_ENABLED, 'true')
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
file: dockerfiles/v2/alpine
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
"${{ steps.ci_docker_repository.outputs.repository }}:v${{ steps.ci_tag_release_version.outputs.tag }}-alpine"
|
|
||||||
build-args: "version_code=release-${{ steps.ci_tag_release_version.outputs.tag }}"
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new
|
|
||||||
|
|
||||||
# Alternate Images with amazonlinux
|
|
||||||
- name: Alternate Amazon Linux Build and Push
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true') && contains(env.DOCKER_BUILD_ALTERNATE_ENABLED, 'true')
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
file: dockerfiles/v2/amazonlinux
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
"${{ steps.ci_docker_repository.outputs.repository }}:v${{ steps.ci_tag_release_version.outputs.tag }}-amazonlinux"
|
|
||||||
build-args: "version_code=release-${{ steps.ci_tag_release_version.outputs.tag }}"
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new
|
|
||||||
|
|
||||||
- name: Move cache
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
run: |
|
|
||||||
rm -rf /tmp/.buildx-cache
|
|
||||||
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
|
|
|
@ -1,101 +0,0 @@
|
||||||
env:
|
|
||||||
RUBY_VERSION: 2.7
|
|
||||||
|
|
||||||
name: CI Build Push
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: "*"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
main:
|
|
||||||
name: Build Docker Image
|
|
||||||
env:
|
|
||||||
DOCKER_REPOSITORY: ${{ secrets.DOCKER_REPOSITORY }}
|
|
||||||
DOCKER_BUILD_ENABLED: ${{ secrets.DOCKER_BUILD_ENABLED }}
|
|
||||||
DOCKER_BUILD_ALTERNATE_ENABLED: ${{ secrets.DOCKER_BUILD_ALTERNATE_ENABLED }}
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
|
|
||||||
- name: Cache Docker layers
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /tmp/.buildx-cache
|
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-buildx-
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Extract Docker Repository
|
|
||||||
id: ci_docker_repository
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
shell: bash
|
|
||||||
run: echo "##[set-output name=repository;]$(echo ${DOCKER_REPOSITORY:-$GITHUB_REPOSITORY})"
|
|
||||||
|
|
||||||
- name: Extract Branch Name
|
|
||||||
id: ci_branch_name
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
shell: bash
|
|
||||||
run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
|
|
||||||
|
|
||||||
- name: Extract Commit Short SHA
|
|
||||||
id: ci_commit_short_sha
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
shell: bash
|
|
||||||
run: echo "##[set-output name=short_sha;]$(echo ${GITHUB_SHA} | cut -c1-7)"
|
|
||||||
|
|
||||||
- name: Build and Push
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
"${{ steps.ci_docker_repository.outputs.repository }}:${{ steps.ci_branch_name.outputs.branch }}"
|
|
||||||
build-args: "version_code=${{ steps.ci_branch_name.outputs.branch }}-${{ steps.ci_commit_short_sha.outputs.short_sha }}"
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new
|
|
||||||
|
|
||||||
# Alternate Image with alpine
|
|
||||||
- name: Alternate Alpine Build and Push
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true') && contains(env.DOCKER_BUILD_ALTERNATE_ENABLED, 'true')
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
file: dockerfiles/v2/alpine
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
"${{ steps.ci_docker_repository.outputs.repository }}:${{ steps.ci_branch_name.outputs.branch }}-alpine"
|
|
||||||
build-args: "version_code=${{ steps.ci_branch_name.outputs.branch }}-${{ steps.ci_commit_short_sha.outputs.short_sha }}"
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new
|
|
||||||
|
|
||||||
# Alternate Images for bbb-bionic-230 with amazonlinux
|
|
||||||
- name: Alternate Amazon Linux Build and Push
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true') && contains(env.DOCKER_BUILD_ALTERNATE_ENABLED, 'true')
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
file: dockerfiles/v2/amazonlinux
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
"${{ steps.ci_docker_repository.outputs.repository }}:${{ steps.ci_branch_name.outputs.branch }}-amazonlinux"
|
|
||||||
build-args: "version_code=${{ steps.ci_branch_name.outputs.branch }}-${{ steps.ci_commit_short_sha.outputs.short_sha }}"
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new
|
|
||||||
|
|
||||||
- name: Move cache
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
run: |
|
|
||||||
rm -rf /tmp/.buildx-cache
|
|
||||||
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
|
|
|
@ -1,110 +0,0 @@
|
||||||
env:
|
|
||||||
RUBY_VERSION: 2.7
|
|
||||||
|
|
||||||
name: CI Build Release
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [released]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
main:
|
|
||||||
name: Build Docker Image
|
|
||||||
env:
|
|
||||||
DOCKER_REPOSITORY: ${{ secrets.DOCKER_REPOSITORY }}
|
|
||||||
DOCKER_BUILD_ENABLED: ${{ secrets.DOCKER_BUILD_ENABLED }}
|
|
||||||
DOCKER_BUILD_ALTERNATE_ENABLED: ${{ secrets.DOCKER_BUILD_ALTERNATE_ENABLED }}
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
|
|
||||||
- name: Cache Docker layers
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /tmp/.buildx-cache
|
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-buildx-
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
|
|
||||||
- name: Extract Docker Repository
|
|
||||||
id: ci_docker_repository
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
shell: bash
|
|
||||||
run: echo "##[set-output name=repository;]$(echo ${DOCKER_REPOSITORY:-$GITHUB_REPOSITORY})"
|
|
||||||
|
|
||||||
- name: Extract Tag Release
|
|
||||||
id: ci_tag_release
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
shell: bash
|
|
||||||
run: echo "##[set-output name=tag;]$(echo ${GITHUB_REF#refs/tags/})"
|
|
||||||
|
|
||||||
- name: Extract Tag Release Major
|
|
||||||
id: ci_tag_release_major
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
shell: bash
|
|
||||||
run: echo "##[set-output name=tag;]$(echo ${{steps.ci_tag_release.outputs.tag}} | cut -c 9- | cut -f1-1 -d'.')"
|
|
||||||
|
|
||||||
- name: Extract Tag Release Minor
|
|
||||||
id: ci_tag_release_minor
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
shell: bash
|
|
||||||
run: echo "##[set-output name=tag;]$(echo ${{steps.ci_tag_release.outputs.tag}} | cut -c 9- | cut -f1-2 -d'.')"
|
|
||||||
|
|
||||||
- name: Build and Push
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
"${{ steps.ci_docker_repository.outputs.repository }}:${{ steps.ci_tag_release.outputs.tag }}"
|
|
||||||
"${{ steps.ci_docker_repository.outputs.repository }}:v${{ steps.ci_tag_release_major.outputs.tag }}"
|
|
||||||
"${{ steps.ci_docker_repository.outputs.repository }}:v${{ steps.ci_tag_release_minor.outputs.tag }}"
|
|
||||||
"${{ steps.ci_docker_repository.outputs.repository }}:latest"
|
|
||||||
build-args: "version_code=${{ steps.ci_tag_release_revision.outputs.tag }}"
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new
|
|
||||||
|
|
||||||
# Alternate Image with alpine
|
|
||||||
- name: Alternate Alpine Build and Push
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true') && contains(env.DOCKER_BUILD_ALTERNATE_ENABLED, 'true')
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
file: dockerfiles/v2/alpine
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
"${{ steps.ci_docker_repository.outputs.repository }}:${{ steps.ci_tag_release.outputs.tag }}-alpine"
|
|
||||||
build-args: "version_code=${{ steps.ci_tag_release_revision.outputs.tag }}"
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new
|
|
||||||
|
|
||||||
# Alternate Image with amazonlinux
|
|
||||||
- name: Alternate Amazon Linux Build and Push
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true') && contains(env.DOCKER_BUILD_ALTERNATE_ENABLED, 'true')
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
file: dockerfiles/v2/amazonlinux
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
"${{ steps.ci_docker_repository.outputs.repository }}:${{ steps.ci_tag_release.outputs.tag }}-amazonlinux"
|
|
||||||
build-args: "version_code=${{ steps.ci_tag_release_revision.outputs.tag }}"
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache-new
|
|
||||||
|
|
||||||
- name: Move cache
|
|
||||||
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
|
|
||||||
run: |
|
|
||||||
rm -rf /tmp/.buildx-cache
|
|
||||||
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
|
|
|
@ -1,68 +0,0 @@
|
||||||
env:
|
|
||||||
RUBY_VERSION: 2.7
|
|
||||||
DB_ADAPTER: postgresql
|
|
||||||
DB_HOST: localhost
|
|
||||||
DB_NAME: postgres
|
|
||||||
DB_USERNAME: postgres
|
|
||||||
DB_PASSWORD: postgres
|
|
||||||
DB_PORT: 5432
|
|
||||||
|
|
||||||
name: CI
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches-ignore: "master"
|
|
||||||
pull_request:
|
|
||||||
branches: "*"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: Rubocop + RSpec
|
|
||||||
runs-on: ubuntu-18.04
|
|
||||||
|
|
||||||
services:
|
|
||||||
postgres:
|
|
||||||
image: postgres:13.2-alpine
|
|
||||||
env:
|
|
||||||
POSTGRES_DB: postgres
|
|
||||||
POSTGRES_PASSWORD: postgres
|
|
||||||
POSTGRES_USER: postgres
|
|
||||||
ports:
|
|
||||||
- 5432:5432
|
|
||||||
# Health checks to wait until postgres is ready
|
|
||||||
options: >-
|
|
||||||
--health-cmd pg_isready
|
|
||||||
--health-interval 10s
|
|
||||||
--health-timeout 5s
|
|
||||||
--health-retries 5
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install Ruby ${{ env.RUBY_VERSION }}
|
|
||||||
uses: actions/setup-ruby@v1
|
|
||||||
with:
|
|
||||||
ruby-version: ${{ env.RUBY_VERSION }}
|
|
||||||
|
|
||||||
- name: Bundle cache
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: vendor/bundle
|
|
||||||
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-gems-
|
|
||||||
|
|
||||||
- name: Bundle install
|
|
||||||
run: |
|
|
||||||
bundle config path vendor/bundle
|
|
||||||
bundle install --jobs 4 --retry 3
|
|
||||||
|
|
||||||
- name: Setup database
|
|
||||||
run: |
|
|
||||||
bundler exec rails db:create RAILS_ENV=test
|
|
||||||
bundler exec rails db:migrate RAILS_ENV=test
|
|
||||||
|
|
||||||
- name: Run Rubocop
|
|
||||||
run: bundle exec rubocop --parallel --fail-level F
|
|
||||||
|
|
||||||
- name: Run RSpec
|
|
||||||
run: bundle exec rspec
|
|
|
@ -6,45 +6,21 @@
|
||||||
|
|
||||||
# Ignore bundler config.
|
# Ignore bundler config.
|
||||||
/.bundle
|
/.bundle
|
||||||
|
vendor/.bundle
|
||||||
|
|
||||||
# Ignore the default SQLite database.
|
# Ignore the default SQLite database.
|
||||||
/db/*.sqlite3
|
/db/**/*.sqlite3
|
||||||
/db/*.sqlite3-journal
|
/db/**/*.sqlite3-journal
|
||||||
|
|
||||||
# Ignore static assets.
|
|
||||||
/public/system/**
|
/public/system/**
|
||||||
/public/assets/**
|
/public/assets/**
|
||||||
/public/b/**
|
|
||||||
|
|
||||||
# Ignore uploaded files
|
|
||||||
/storage
|
|
||||||
|
|
||||||
# Ignore production paths.
|
|
||||||
/db/production
|
|
||||||
/db/production-postgres
|
|
||||||
|
|
||||||
# Ignore all logfiles and tempfiles.
|
# Ignore all logfiles and tempfiles.
|
||||||
/log/*
|
/log/*
|
||||||
/tmp
|
/tmp/*
|
||||||
!/log/.keep
|
!/log/.keep
|
||||||
|
!/tmp/.keep
|
||||||
|
.env
|
||||||
|
coverage
|
||||||
|
|
||||||
# Ignore Byebug command history file.
|
# Ignore Byebug command history file.
|
||||||
.byebug_history
|
.byebug_history
|
||||||
|
|
||||||
# Ignore environment configuration.
|
|
||||||
.env
|
|
||||||
env
|
|
||||||
|
|
||||||
# Ignore yarn configs
|
|
||||||
/node_modules
|
|
||||||
|
|
||||||
# IDEs
|
|
||||||
.idea
|
|
||||||
.idea/**
|
|
||||||
.vscode
|
|
||||||
.vscode/**
|
|
||||||
|
|
||||||
config/terms.md
|
|
||||||
coverage*
|
|
||||||
|
|
||||||
/atom-sftp-sync.json
|
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
stages:
|
|
||||||
- test
|
|
||||||
- build
|
|
||||||
- deploy
|
|
||||||
|
|
||||||
cache:
|
|
||||||
paths:
|
|
||||||
- /cache
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
|
|
||||||
test:
|
|
||||||
stage: test
|
|
||||||
image: ruby:2.5
|
|
||||||
script:
|
|
||||||
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
|
|
||||||
- bundle install --path /cache
|
|
||||||
- bundle exec rake db:create RAILS_ENV=test
|
|
||||||
- bundle exec rake test & bundle exec rspec & bundle exec rubocop
|
|
||||||
except:
|
|
||||||
variables:
|
|
||||||
- $CD_TEST_IGNORE
|
|
||||||
|
|
||||||
build:
|
|
||||||
stage: build
|
|
||||||
image: docker:stable
|
|
||||||
services:
|
|
||||||
- docker:dind
|
|
||||||
script:
|
|
||||||
# Install bash, curl, git for deployment script
|
|
||||||
- apk update && apk add --no-cache bash curl git
|
|
||||||
# Install CA certs, openssl to https downloads, python for gcloud sdk
|
|
||||||
- apk add --update make ca-certificates openssl python
|
|
||||||
- update-ca-certificates
|
|
||||||
# Build.
|
|
||||||
- ./scripts/image_build.sh $CI_PROJECT_PATH $CI_COMMIT_REF_NAME $CI_COMMIT_SHA
|
|
||||||
only:
|
|
||||||
refs:
|
|
||||||
- branches
|
|
||||||
- tags
|
|
||||||
variables:
|
|
||||||
- $CD_DOCKER_USERNAME
|
|
||||||
- $CD_DOCKER_PASSWORD
|
|
||||||
except:
|
|
||||||
variables:
|
|
||||||
- $CD_BUILD_IGNORE
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
stage: deploy
|
|
||||||
image: docker:stable
|
|
||||||
services:
|
|
||||||
- docker:dind
|
|
||||||
script:
|
|
||||||
# Install bash, curl, git for deployment script
|
|
||||||
- apk update && apk add --no-cache bash curl git
|
|
||||||
# Install CA certs, openssl to https downloads, python for gcloud sdk
|
|
||||||
- apk add --update make ca-certificates openssl python
|
|
||||||
- update-ca-certificates
|
|
||||||
# Deploy.
|
|
||||||
- ./scripts/image_deploy.sh $CI_PROJECT_PATH $CI_COMMIT_REF_NAME $CI_COMMIT_SHA $CI_COMMIT_BEFORE_SHA
|
|
||||||
only:
|
|
||||||
refs:
|
|
||||||
- branches
|
|
||||||
- tags
|
|
||||||
variables:
|
|
||||||
- $CD_DOCKER_USERNAME
|
|
||||||
- $CD_DOCKER_PASSWORD
|
|
||||||
- $CD_DEPLOY_SCRIPT
|
|
||||||
except:
|
|
||||||
variables:
|
|
||||||
- $CD_DEPLOY_IGNORE
|
|
42
.rake_tasks~
|
@ -1,42 +0,0 @@
|
||||||
about
|
|
||||||
app:template
|
|
||||||
app:update
|
|
||||||
assets:clean[keep]
|
|
||||||
assets:clobber
|
|
||||||
assets:environment
|
|
||||||
assets:precompile
|
|
||||||
autoprefixer:info
|
|
||||||
cache_digests:dependencies
|
|
||||||
cache_digests:nested_dependencies
|
|
||||||
conf:check
|
|
||||||
db:create
|
|
||||||
db:drop
|
|
||||||
db:environment:set
|
|
||||||
db:fixtures:load
|
|
||||||
db:migrate
|
|
||||||
db:migrate:status
|
|
||||||
db:rollback
|
|
||||||
db:schema:cache:clear
|
|
||||||
db:schema:cache:dump
|
|
||||||
db:schema:dump
|
|
||||||
db:schema:load
|
|
||||||
db:seed
|
|
||||||
db:setup
|
|
||||||
db:structure:dump
|
|
||||||
db:structure:load
|
|
||||||
db:version
|
|
||||||
dev:cache
|
|
||||||
initializers
|
|
||||||
log:clear
|
|
||||||
middleware
|
|
||||||
notes
|
|
||||||
notes:custom
|
|
||||||
restart
|
|
||||||
routes
|
|
||||||
secret
|
|
||||||
stats
|
|
||||||
test
|
|
||||||
test:db
|
|
||||||
time:zones[country_or_offset]
|
|
||||||
tmp:clear
|
|
||||||
tmp:create
|
|
191
.rubocop.yml
|
@ -1,191 +0,0 @@
|
||||||
AllCops:
|
|
||||||
Exclude:
|
|
||||||
- 'db/schema.rb'
|
|
||||||
- 'vendor/**/*'
|
|
||||||
DisabledByDefault: false
|
|
||||||
TargetRubyVersion: 2.7
|
|
||||||
|
|
||||||
NewCops: enable
|
|
||||||
|
|
||||||
Style/BlockDelimiters:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Checks if uses of quotes match the configured preference.
|
|
||||||
Style/StringLiterals:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Document classes and non-namespace modules.
|
|
||||||
Style/Documentation:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Check for conditionals that can be replaced with guard clauses
|
|
||||||
Style/GuardClause:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Checks the formatting of empty method definitions.
|
|
||||||
Style/EmptyMethod:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Checks for trailing comma in hash literals.
|
|
||||||
Style/TrailingCommaInHashLiteral:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Checks for trailing comma in argument lists.
|
|
||||||
Style/TrailingCommaInArguments:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Checks that `include`, `extend` and `prepend` exists at the top level.
|
|
||||||
Style/MixinUsage:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Use %i or %I for arrays of symbols.
|
|
||||||
Style/SymbolArray:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Don't use begin blocks when they are not needed.
|
|
||||||
Style/RedundantBegin:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Use `%`-literal delimiters consistently
|
|
||||||
Style/PercentLiteralDelimiters:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Only use if/unless modifiers on single line statements.
|
|
||||||
Style/MultilineIfModifier:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Checks for trailing comma in array literals.
|
|
||||||
Style/TrailingCommaInArrayLiteral:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Use `expand_path(__dir__)` instead of `expand_path('..', __FILE__)`.
|
|
||||||
Style/ExpandPathArguments:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Do not assign mutable objects to constants.
|
|
||||||
Style/MutableConstant:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Avoid rescuing without specifying an error class.
|
|
||||||
Style/RescueStandardError:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Align the elements of a hash literal if they span more than one line.
|
|
||||||
Layout/HashAlignment:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Align the parameters of a method definition if they span more than one line.
|
|
||||||
Layout/ParameterAlignment:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Align ends corresponding to defs correctly.
|
|
||||||
Layout/EndAlignment:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Align elses and elsifs correctly.
|
|
||||||
Layout/ElseAlignment:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Add empty line after guard clause.
|
|
||||||
Layout/EmptyLineAfterGuardClause:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Align the arguments of a method call if they span more than one line.
|
|
||||||
Layout/ArgumentAlignment:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
#
|
|
||||||
Layout/IndentationWidth:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Layout/CaseIndentation:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Checks for ambiguous block association with method when param passed without parentheses.
|
|
||||||
Lint/AmbiguousBlockAssociation:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Avoid long blocks with many lines.
|
|
||||||
Metrics/BlockLength:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# A complexity metric geared towards measuring complexity for a human reader.
|
|
||||||
Metrics/PerceivedComplexity:
|
|
||||||
Max: 17
|
|
||||||
|
|
||||||
# Avoid classes longer than 100 lines of code.
|
|
||||||
Metrics/ClassLength:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Limit lines to 80 characters.
|
|
||||||
Layout/LineLength:
|
|
||||||
Max: 130
|
|
||||||
|
|
||||||
# Avoid methods longer than 10 lines of code.
|
|
||||||
Metrics/MethodLength:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Metrics/ModuleLength:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# A calculated magnitude based on number of assignments,
|
|
||||||
# branches, and conditions.
|
|
||||||
Metrics/AbcSize:
|
|
||||||
Max: 60
|
|
||||||
|
|
||||||
# A complexity metric that is strongly correlated to the number
|
|
||||||
# of test cases needed to validate a method.
|
|
||||||
Metrics/CyclomaticComplexity:
|
|
||||||
Max: 20
|
|
||||||
|
|
||||||
# Checks for method parameter names that contain capital letters, end in numbers, or do not meet a minimal length.
|
|
||||||
Naming/MethodParameterName:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Lint/LiteralInInterpolation:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Layout/EmptyLinesAroundAttributeAccessor:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Layout/SpaceAroundMethodCallOperator:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Lint/DeprecatedOpenSSLConstant:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Lint/RaiseException:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Lint/StructNewOverride:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/ExponentialNotation:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/HashEachMethods:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/HashTransformKeys:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/HashTransformValues:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/SlicingWithRange:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/OptionalBooleanParameter:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Lint/DuplicateBranch:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Lint/ConstantDefinitionInBlock:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Lint/EmptyBlock:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/HashLikeCase:
|
|
||||||
Enabled: false
|
|
|
@ -1 +0,0 @@
|
||||||
2.7.2
|
|
80
Dockerfile
|
@ -1,74 +1,20 @@
|
||||||
FROM ruby:2.7.2-alpine AS base
|
FROM ruby:2.4.0
|
||||||
|
|
||||||
# Set a variable for the install location.
|
# app dependencies
|
||||||
ARG RAILS_ROOT=/usr/src/app
|
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
|
||||||
# Set Rails environment.
|
|
||||||
ENV RAILS_ENV production
|
|
||||||
ENV BUNDLE_APP_CONFIG="$RAILS_ROOT/.bundle"
|
|
||||||
|
|
||||||
# Make the directory and set as working.
|
ENV RAILS_ENV=production \
|
||||||
RUN mkdir -p $RAILS_ROOT
|
APP_HOME=/usr/src/app
|
||||||
WORKDIR $RAILS_ROOT
|
|
||||||
|
|
||||||
ARG BUILD_PACKAGES="build-base curl-dev git"
|
|
||||||
ARG DEV_PACKAGES="postgresql-dev sqlite-libs sqlite-dev yaml-dev zlib-dev nodejs yarn"
|
|
||||||
ARG RUBY_PACKAGES="tzdata"
|
|
||||||
|
|
||||||
# Install app dependencies.
|
|
||||||
RUN apk update \
|
|
||||||
&& apk upgrade \
|
|
||||||
&& apk add --update --no-cache $BUILD_PACKAGES $DEV_PACKAGES $RUBY_PACKAGES
|
|
||||||
|
|
||||||
COPY Gemfile* ./
|
|
||||||
COPY Gemfile Gemfile.lock $RAILS_ROOT/
|
|
||||||
|
|
||||||
RUN bundle config --global frozen 1 \
|
|
||||||
&& bundle config set deployment 'true' \
|
|
||||||
&& bundle config set without 'development:test:assets' \
|
|
||||||
&& bundle install -j4 --path=vendor/bundle \
|
|
||||||
&& rm -rf vendor/bundle/ruby/2.7.0/cache/*.gem \
|
|
||||||
&& find vendor/bundle/ruby/2.7.0/gems/ -name "*.c" -delete \
|
|
||||||
&& find vendor/bundle/ruby/2.7.0/gems/ -name "*.o" -delete
|
|
||||||
|
|
||||||
# Adding project files.
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Remove folders not needed in resulting image
|
|
||||||
RUN rm -rf tmp/cache spec
|
|
||||||
|
|
||||||
############### Build step done ###############
|
|
||||||
|
|
||||||
FROM ruby:2.7.2-alpine
|
|
||||||
|
|
||||||
# Set a variable for the install location.
|
|
||||||
ARG RAILS_ROOT=/usr/src/app
|
|
||||||
ARG PACKAGES="tzdata curl postgresql-client sqlite-libs yarn nodejs bash"
|
|
||||||
|
|
||||||
ENV RAILS_ENV=production
|
|
||||||
ENV BUNDLE_APP_CONFIG="$RAILS_ROOT/.bundle"
|
|
||||||
|
|
||||||
WORKDIR $RAILS_ROOT
|
|
||||||
|
|
||||||
RUN apk update \
|
|
||||||
&& apk upgrade \
|
|
||||||
&& apk add --update --no-cache $PACKAGES
|
|
||||||
|
|
||||||
|
|
||||||
COPY --from=base $RAILS_ROOT $RAILS_ROOT
|
RUN mkdir $APP_HOME
|
||||||
|
WORKDIR $APP_HOME
|
||||||
|
|
||||||
# Expose port 80.
|
# Add the greenlight app
|
||||||
EXPOSE 80
|
ADD . $APP_HOME
|
||||||
|
|
||||||
# Sets the footer of greenlight application with current build version
|
# Install app dependencies
|
||||||
ARG version_code
|
RUN bundle install --without development test doc --deployment --clean
|
||||||
ENV VERSION_CODE=$version_code
|
RUN bundle exec rake assets:precompile --trace
|
||||||
|
|
||||||
# Set executable permission to start file
|
CMD ["scripts/default_start.sh"]
|
||||||
RUN chmod +x bin/start
|
|
||||||
|
|
||||||
# FIXME / to remove / https://github.com/nahi/httpclient/issues/445
|
|
||||||
RUN cat /etc/ssl/certs/ca-certificates.crt \
|
|
||||||
>/usr/src/app/vendor/bundle/ruby/2.7.0/gems/httpclient-2.8.3/lib/httpclient/cacert.pem
|
|
||||||
|
|
||||||
# Start the application.
|
|
||||||
CMD ["bin/start"]
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
FROM ruby:2.4.0
|
||||||
|
|
||||||
|
# app dependencies
|
||||||
|
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
|
||||||
|
|
||||||
|
ENV RAILS_ENV=development \
|
||||||
|
APP_HOME=/usr/src/app
|
||||||
|
|
||||||
|
RUN mkdir $APP_HOME
|
||||||
|
WORKDIR $APP_HOME
|
155
Gemfile
|
@ -1,79 +1,98 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
git_source(:github) do |repo_name|
|
ruby '2.4.0'
|
||||||
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
|
|
||||||
"https://github.com/#{repo_name}.git"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
||||||
gem 'aws-sdk-s3', '~> 1.88.1'
|
gem 'rails', '~> 5.0.0', '>= 5.0.0.1'
|
||||||
gem 'bcrypt', '~> 3.1.7'
|
# Use sqlite3 as the database for Active Record
|
||||||
gem 'bigbluebutton-api-ruby', git: 'https://github.com/mconf/bigbluebutton-api-ruby.git', branch: 'master'
|
gem 'sqlite3'
|
||||||
gem 'bn-ldap-authentication', '~> 0.1.4'
|
# Use postgres as the database for Active Record
|
||||||
gem 'bootsnap', '~> 1.7.2', require: false
|
gem 'pg'
|
||||||
gem 'bootstrap', '~> 4.3.1'
|
# Use Puma as the app server
|
||||||
gem 'cancancan', '~> 2.3.0'
|
gem 'puma', '~> 3.0'
|
||||||
gem 'coveralls', '~> 0.8.23', require: false
|
# Use SCSS for stylesheets
|
||||||
gem 'font-awesome-sass', '~> 5.9.0'
|
gem 'sass-rails', '~> 5.0'
|
||||||
gem 'google-cloud-storage', '~> 1.30.0'
|
# Use Uglifier as compressor for JavaScript assets
|
||||||
gem 'http_accept_language', '~> 2.1.1'
|
gem 'uglifier', '>= 1.3.0'
|
||||||
gem 'i18n-language-mapping', '~> 0.1.3.1'
|
# Use CoffeeScript for .coffee assets and views
|
||||||
gem 'jbuilder', '~> 2.11.2'
|
gem 'coffee-rails', '~> 4.2'
|
||||||
gem 'jquery-rails', '~> 4.4.0'
|
# See https://github.com/rails/execjs#readme for more supported runtimes
|
||||||
gem 'jquery-ui-rails', '~> 6.0.1'
|
gem 'therubyracer', platforms: :ruby
|
||||||
gem 'local_time', '~> 2.1.0'
|
|
||||||
gem 'net-ldap', '~> 0.17.0'
|
|
||||||
gem 'omniauth', '~> 1.9.1'
|
|
||||||
gem 'omniauth-bn-launcher', '~> 0.1.3'
|
|
||||||
gem 'omniauth-bn-office365', '~> 0.1.1'
|
|
||||||
gem 'omniauth-google-oauth2', '~> 0.7.0'
|
|
||||||
gem 'omniauth_openid_connect', '~> 0.3.5'
|
|
||||||
gem 'omniauth-twitter', '~> 1.4.0'
|
|
||||||
gem 'pagy', '~> 3.11.0'
|
|
||||||
gem 'pluck_to_hash', '~> 1.0.2'
|
|
||||||
gem 'puma', '~> 4.3.8'
|
|
||||||
gem 'rails', '~> 5.2.6'
|
|
||||||
gem 'random_password', '~> 0.1.1'
|
|
||||||
gem "recaptcha", '~> 5.7.0'
|
|
||||||
gem 'redcarpet', '~> 3.5.1'
|
|
||||||
gem 'remote_syslog_logger', '~> 1.0.4'
|
|
||||||
gem 'rubocop', '~> 1.10.0'
|
|
||||||
gem 'sassc-rails', '~> 2.1.2'
|
|
||||||
gem 'sprockets', '~> 3.7.2'
|
|
||||||
gem 'sqlite3', '~> 1.3.6'
|
|
||||||
gem 'tabler-rubygem', git: 'https://github.com/blindsidenetworks/tabler-rubygem.git', tag: '0.1.4.1'
|
|
||||||
gem 'turbolinks', '~> 5.2.1'
|
|
||||||
gem 'tzinfo-data', '~> 1.2021.1'
|
|
||||||
gem 'uglifier', '~> 4.2.0'
|
|
||||||
|
|
||||||
group :production do
|
# Use jquery as the JavaScript library
|
||||||
gem 'hiredis', '~> 0.6.3'
|
gem 'jquery-rails'
|
||||||
gem "lograge", '~> 0.11.2'
|
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
|
||||||
gem 'pg', '~> 0.18'
|
gem 'turbolinks', '~> 5'
|
||||||
gem 'redis', '~> 4.2.5'
|
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
||||||
gem 'sequel', '~> 5.41.0'
|
gem 'jbuilder', '~> 2.5'
|
||||||
end
|
# Use Redis adapter to run Action Cable in production
|
||||||
|
# gem 'redis', '~> 3.0'
|
||||||
|
# Use ActiveModel has_secure_password
|
||||||
|
# gem 'bcrypt', '~> 3.1.7'
|
||||||
|
|
||||||
|
# Use Capistrano for deployment
|
||||||
|
# gem 'capistrano-rails', group: :development
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'byebug', '~> 11.1', platform: :mri
|
# For environment configuration
|
||||||
gem 'dotenv-rails', '~> 2.7'
|
gem 'dotenv-rails'
|
||||||
end
|
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
|
||||||
|
gem 'byebug', platform: :mri
|
||||||
group :test do
|
|
||||||
gem 'action-cable-testing', '~> 0.6'
|
|
||||||
gem "factory_bot_rails", '~> 6.1'
|
|
||||||
gem 'faker', '~> 2.16'
|
|
||||||
gem 'rails-controller-testing', '~> 1.0'
|
|
||||||
gem 'rspec-rails', '~> 3.7'
|
|
||||||
gem 'shoulda-matchers', '~> 3.1'
|
|
||||||
gem 'webmock', '~> 3.11'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem 'listen', '~> 3.0'
|
# Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
|
||||||
gem 'spring', '~> 2.1'
|
gem 'web-console'
|
||||||
gem 'spring-watcher-listen', '~> 2.0'
|
gem 'listen', '~> 3.0.5'
|
||||||
gem 'web-console', '~> 3.7'
|
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
||||||
|
gem 'spring'
|
||||||
|
gem 'spring-watcher-listen', '~> 2.0.0'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
group :test do
|
||||||
|
gem 'mocha'
|
||||||
|
gem 'simplecov', :require => false
|
||||||
|
end
|
||||||
|
|
||||||
|
group :production do
|
||||||
|
# For more condensed logging
|
||||||
|
gem "lograge"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
||||||
|
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
||||||
|
|
||||||
|
gem 'omniauth', '1.6.1'
|
||||||
|
gem 'omniauth-twitter', '1.2.1'
|
||||||
|
gem 'omniauth-google-oauth2', '0.4.1'
|
||||||
|
gem 'omniauth-ldap'
|
||||||
|
|
||||||
|
gem 'bigbluebutton-api-ruby'
|
||||||
|
|
||||||
|
gem 'bootstrap-sass', '3.3.0.0'
|
||||||
|
gem 'bootstrap-social-rails', '~> 4.12'
|
||||||
|
gem 'font-awesome-sass', '4.7.0'
|
||||||
|
gem 'jquery-ui-rails'
|
||||||
|
gem 'jquery-datatables-rails', '~> 3.4.0'
|
||||||
|
gem 'rails-timeago', '~> 2.0'
|
||||||
|
|
||||||
|
gem 'http_accept_language'
|
||||||
|
|
||||||
|
# For sending slack notifications.
|
||||||
|
gem 'slack-notifier'
|
||||||
|
|
||||||
|
# For landing background image uploading.
|
||||||
|
gem 'paperclip', '~> 5.1'
|
||||||
|
|
||||||
|
# For uploading recordings to Youtube.
|
||||||
|
gem 'yt', '~> 0.28.0'
|
||||||
|
|
||||||
|
# Simple HTTP client.
|
||||||
|
gem 'faraday'
|
||||||
|
|
||||||
|
|
||||||
|
# For SAML authentication
|
||||||
|
gem 'omniauth-saml'
|
||||||
|
|
||||||
|
# For device detection to determine BigBlueButton client.
|
||||||
|
gem 'browser'
|
||||||
|
|
704
Gemfile.lock
|
@ -1,521 +1,339 @@
|
||||||
GIT
|
|
||||||
remote: https://github.com/blindsidenetworks/tabler-rubygem.git
|
|
||||||
revision: 4c16e6dc930f6b92dec093abaf9652d768b46d97
|
|
||||||
tag: 0.1.4.1
|
|
||||||
specs:
|
|
||||||
tabler-rubygem (0.1.4.1)
|
|
||||||
autoprefixer-rails (>= 6.0.3)
|
|
||||||
|
|
||||||
GIT
|
|
||||||
remote: https://github.com/mconf/bigbluebutton-api-ruby.git
|
|
||||||
revision: 91dc495324a6b7e162773227ec3650f8a5b39c50
|
|
||||||
branch: master
|
|
||||||
specs:
|
|
||||||
bigbluebutton-api-ruby (1.7.0)
|
|
||||||
childprocess (>= 1.0.1)
|
|
||||||
ffi (>= 1.9.24)
|
|
||||||
json (>= 1.8.6)
|
|
||||||
nokogiri (>= 1.10.4)
|
|
||||||
rack (>= 1.6.11)
|
|
||||||
rubyzip (>= 1.3.0)
|
|
||||||
xml-simple (~> 1.1)
|
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
action-cable-testing (0.6.1)
|
actioncable (5.0.7)
|
||||||
actioncable (>= 5.0)
|
actionpack (= 5.0.7)
|
||||||
actioncable (5.2.6)
|
nio4r (>= 1.2, < 3.0)
|
||||||
actionpack (= 5.2.6)
|
websocket-driver (~> 0.6.1)
|
||||||
nio4r (~> 2.0)
|
actionmailer (5.0.7)
|
||||||
websocket-driver (>= 0.6.1)
|
actionpack (= 5.0.7)
|
||||||
actionmailer (5.2.6)
|
actionview (= 5.0.7)
|
||||||
actionpack (= 5.2.6)
|
activejob (= 5.0.7)
|
||||||
actionview (= 5.2.6)
|
|
||||||
activejob (= 5.2.6)
|
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (5.2.6)
|
actionpack (5.0.7)
|
||||||
actionview (= 5.2.6)
|
actionview (= 5.0.7)
|
||||||
activesupport (= 5.2.6)
|
activesupport (= 5.0.7)
|
||||||
rack (~> 2.0, >= 2.0.8)
|
rack (~> 2.0)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (~> 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
actionview (5.2.6)
|
actionview (5.0.7)
|
||||||
activesupport (= 5.2.6)
|
activesupport (= 5.0.7)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubis (~> 2.7.0)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||||
activejob (5.2.6)
|
activejob (5.0.7)
|
||||||
activesupport (= 5.2.6)
|
activesupport (= 5.0.7)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (5.2.6)
|
activemodel (5.0.7)
|
||||||
activesupport (= 5.2.6)
|
activesupport (= 5.0.7)
|
||||||
activerecord (5.2.6)
|
activerecord (5.0.7)
|
||||||
activemodel (= 5.2.6)
|
activemodel (= 5.0.7)
|
||||||
activesupport (= 5.2.6)
|
activesupport (= 5.0.7)
|
||||||
arel (>= 9.0)
|
arel (~> 7.0)
|
||||||
activestorage (5.2.6)
|
activesupport (5.0.7)
|
||||||
actionpack (= 5.2.6)
|
|
||||||
activerecord (= 5.2.6)
|
|
||||||
marcel (~> 1.0.0)
|
|
||||||
activesupport (5.2.6)
|
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
addressable (2.8.0)
|
arel (7.1.4)
|
||||||
public_suffix (>= 2.0.2, < 5.0)
|
bigbluebutton-api-ruby (1.6.0)
|
||||||
aes_key_wrap (1.1.0)
|
xml-simple (~> 1.1)
|
||||||
arel (9.0.0)
|
bindex (0.5.0)
|
||||||
ast (2.4.2)
|
bootstrap-sass (3.3.0.0)
|
||||||
attr_required (1.0.1)
|
sass (~> 3.2)
|
||||||
autoprefixer-rails (10.2.4.0)
|
bootstrap-social-rails (4.12.0)
|
||||||
|
railties (>= 3.1)
|
||||||
|
browser (2.5.2)
|
||||||
|
builder (3.2.3)
|
||||||
|
byebug (9.1.0)
|
||||||
|
climate_control (0.2.0)
|
||||||
|
cocaine (0.5.8)
|
||||||
|
climate_control (>= 0.0.3, < 1.0)
|
||||||
|
coffee-rails (4.2.2)
|
||||||
|
coffee-script (>= 2.2.0)
|
||||||
|
railties (>= 4.0.0)
|
||||||
|
coffee-script (2.4.1)
|
||||||
|
coffee-script-source
|
||||||
execjs
|
execjs
|
||||||
aws-eventstream (1.1.1)
|
coffee-script-source (1.12.2)
|
||||||
aws-partitions (1.435.0)
|
concurrent-ruby (1.0.5)
|
||||||
aws-sdk-core (3.113.0)
|
concurrent-ruby (1.0.5-java)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
crass (1.0.4)
|
||||||
aws-partitions (~> 1, >= 1.239.0)
|
docile (1.1.5)
|
||||||
aws-sigv4 (~> 1.1)
|
dotenv (2.2.1)
|
||||||
jmespath (~> 1.0)
|
dotenv-rails (2.2.1)
|
||||||
aws-sdk-kms (1.43.0)
|
dotenv (= 2.2.1)
|
||||||
aws-sdk-core (~> 3, >= 3.112.0)
|
railties (>= 3.2, < 5.2)
|
||||||
aws-sigv4 (~> 1.1)
|
erubis (2.7.0)
|
||||||
aws-sdk-s3 (1.88.2)
|
|
||||||
aws-sdk-core (~> 3, >= 3.112.0)
|
|
||||||
aws-sdk-kms (~> 1)
|
|
||||||
aws-sigv4 (~> 1.1)
|
|
||||||
aws-sigv4 (1.2.3)
|
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
|
||||||
bcrypt (3.1.16)
|
|
||||||
bindata (2.4.10)
|
|
||||||
bindex (0.8.1)
|
|
||||||
bn-ldap-authentication (0.1.4)
|
|
||||||
net-ldap (~> 0)
|
|
||||||
bootsnap (1.7.3)
|
|
||||||
msgpack (~> 1.0)
|
|
||||||
bootstrap (4.3.1)
|
|
||||||
autoprefixer-rails (>= 9.1.0)
|
|
||||||
popper_js (>= 1.14.3, < 2)
|
|
||||||
sassc-rails (>= 2.0.0)
|
|
||||||
builder (3.2.4)
|
|
||||||
byebug (11.1.3)
|
|
||||||
cancancan (2.3.0)
|
|
||||||
childprocess (4.0.0)
|
|
||||||
concurrent-ruby (1.1.8)
|
|
||||||
coveralls (0.8.23)
|
|
||||||
json (>= 1.8, < 3)
|
|
||||||
simplecov (~> 0.16.1)
|
|
||||||
term-ansicolor (~> 1.3)
|
|
||||||
thor (>= 0.19.4, < 2.0)
|
|
||||||
tins (~> 1.6)
|
|
||||||
crack (0.4.5)
|
|
||||||
rexml
|
|
||||||
crass (1.0.6)
|
|
||||||
declarative (0.0.20)
|
|
||||||
declarative-option (0.1.0)
|
|
||||||
diff-lcs (1.4.4)
|
|
||||||
digest-crc (0.6.3)
|
|
||||||
rake (>= 12.0.0, < 14.0.0)
|
|
||||||
docile (1.3.5)
|
|
||||||
dotenv (2.7.6)
|
|
||||||
dotenv-rails (2.7.6)
|
|
||||||
dotenv (= 2.7.6)
|
|
||||||
railties (>= 3.2)
|
|
||||||
erubi (1.10.0)
|
|
||||||
execjs (2.7.0)
|
execjs (2.7.0)
|
||||||
factory_bot (6.1.0)
|
faraday (0.12.2)
|
||||||
activesupport (>= 5.0.0)
|
|
||||||
factory_bot_rails (6.1.0)
|
|
||||||
factory_bot (~> 6.1.0)
|
|
||||||
railties (>= 5.0.0)
|
|
||||||
faker (2.17.0)
|
|
||||||
i18n (>= 1.6, < 2)
|
|
||||||
faraday (1.3.0)
|
|
||||||
faraday-net_http (~> 1.0)
|
|
||||||
multipart-post (>= 1.2, < 3)
|
multipart-post (>= 1.2, < 3)
|
||||||
ruby2_keywords
|
ffi (1.9.18)
|
||||||
faraday-net_http (1.0.1)
|
ffi (1.9.18-java)
|
||||||
ffi (1.15.0)
|
ffi (1.9.18-x64-mingw32)
|
||||||
font-awesome-sass (5.9.0)
|
ffi (1.9.18-x86-mingw32)
|
||||||
sassc (>= 1.11)
|
font-awesome-sass (4.7.0)
|
||||||
globalid (0.4.2)
|
sass (>= 3.2)
|
||||||
|
globalid (0.4.1)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
google-apis-core (0.3.0)
|
hashie (3.5.7)
|
||||||
addressable (~> 2.5, >= 2.5.1)
|
|
||||||
googleauth (~> 0.14)
|
|
||||||
httpclient (>= 2.8.1, < 3.0)
|
|
||||||
mini_mime (~> 1.0)
|
|
||||||
representable (~> 3.0)
|
|
||||||
retriable (>= 2.0, < 4.0)
|
|
||||||
rexml
|
|
||||||
signet (~> 0.14)
|
|
||||||
webrick
|
|
||||||
google-apis-iamcredentials_v1 (0.2.0)
|
|
||||||
google-apis-core (~> 0.1)
|
|
||||||
google-apis-storage_v1 (0.3.0)
|
|
||||||
google-apis-core (~> 0.1)
|
|
||||||
google-cloud-core (1.6.0)
|
|
||||||
google-cloud-env (~> 1.0)
|
|
||||||
google-cloud-errors (~> 1.0)
|
|
||||||
google-cloud-env (1.5.0)
|
|
||||||
faraday (>= 0.17.3, < 2.0)
|
|
||||||
google-cloud-errors (1.1.0)
|
|
||||||
google-cloud-storage (1.30.0)
|
|
||||||
addressable (~> 2.5)
|
|
||||||
digest-crc (~> 0.4)
|
|
||||||
google-apis-iamcredentials_v1 (~> 0.1)
|
|
||||||
google-apis-storage_v1 (~> 0.1)
|
|
||||||
google-cloud-core (~> 1.2)
|
|
||||||
googleauth (~> 0.9)
|
|
||||||
mini_mime (~> 1.0)
|
|
||||||
googleauth (0.16.0)
|
|
||||||
faraday (>= 0.17.3, < 2.0)
|
|
||||||
jwt (>= 1.4, < 3.0)
|
|
||||||
memoist (~> 0.16)
|
|
||||||
multi_json (~> 1.11)
|
|
||||||
os (>= 0.9, < 2.0)
|
|
||||||
signet (~> 0.14)
|
|
||||||
hashdiff (1.0.1)
|
|
||||||
hashie (4.1.0)
|
|
||||||
hiredis (0.6.3)
|
|
||||||
http_accept_language (2.1.1)
|
http_accept_language (2.1.1)
|
||||||
httpclient (2.8.3)
|
i18n (1.0.1)
|
||||||
i18n (1.8.10)
|
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
i18n-language-mapping (0.1.3.1)
|
jbuilder (2.7.0)
|
||||||
jbuilder (2.11.2)
|
activesupport (>= 4.2.0)
|
||||||
activesupport (>= 5.0.0)
|
multi_json (>= 1.2)
|
||||||
jmespath (1.4.0)
|
jquery-datatables-rails (3.4.0)
|
||||||
jquery-rails (4.4.0)
|
actionpack (>= 3.1)
|
||||||
|
jquery-rails
|
||||||
|
railties (>= 3.1)
|
||||||
|
sass-rails
|
||||||
|
jquery-rails (4.3.1)
|
||||||
rails-dom-testing (>= 1, < 3)
|
rails-dom-testing (>= 1, < 3)
|
||||||
railties (>= 4.2.0)
|
railties (>= 4.2.0)
|
||||||
thor (>= 0.14, < 2.0)
|
thor (>= 0.14, < 2.0)
|
||||||
jquery-ui-rails (6.0.1)
|
jquery-ui-rails (6.0.1)
|
||||||
railties (>= 3.2.16)
|
railties (>= 3.2.16)
|
||||||
json (2.5.1)
|
json (1.8.6)
|
||||||
json-jwt (1.13.0)
|
json (1.8.6-java)
|
||||||
activesupport (>= 4.2)
|
jwt (1.5.6)
|
||||||
aes_key_wrap
|
libv8 (3.16.14.19)
|
||||||
bindata
|
listen (3.0.8)
|
||||||
jwt (2.2.2)
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||||
listen (3.5.0)
|
rb-inotify (~> 0.9, >= 0.9.7)
|
||||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
lograge (0.9.0)
|
||||||
rb-inotify (~> 0.9, >= 0.9.10)
|
|
||||||
local_time (2.1.0)
|
|
||||||
lograge (0.11.2)
|
|
||||||
actionpack (>= 4)
|
actionpack (>= 4)
|
||||||
activesupport (>= 4)
|
activesupport (>= 4)
|
||||||
railties (>= 4)
|
railties (>= 4)
|
||||||
request_store (~> 1.0)
|
request_store (~> 1.0)
|
||||||
loofah (2.9.1)
|
loofah (2.2.2)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
mail (2.7.1)
|
mail (2.7.0)
|
||||||
mini_mime (>= 0.1.1)
|
mini_mime (>= 0.1.1)
|
||||||
marcel (1.0.1)
|
metaclass (0.0.4)
|
||||||
memoist (0.16.2)
|
method_source (0.9.0)
|
||||||
method_source (1.0.0)
|
mime-types (3.1)
|
||||||
mini_mime (1.1.0)
|
mime-types-data (~> 3.2015)
|
||||||
mini_portile2 (2.6.1)
|
mime-types-data (3.2016.0521)
|
||||||
minitest (5.14.4)
|
mimemagic (0.3.2)
|
||||||
msgpack (1.4.2)
|
mini_mime (1.0.0)
|
||||||
multi_json (1.15.0)
|
mini_portile2 (2.3.0)
|
||||||
|
minitest (5.11.3)
|
||||||
|
mocha (1.3.0)
|
||||||
|
metaclass (~> 0.0.1)
|
||||||
|
multi_json (1.13.1)
|
||||||
multi_xml (0.6.0)
|
multi_xml (0.6.0)
|
||||||
multipart-post (2.1.1)
|
multipart-post (2.0.0)
|
||||||
net-ldap (0.17.0)
|
net-ldap (0.16.1)
|
||||||
nio4r (2.5.7)
|
nio4r (2.3.1)
|
||||||
nokogiri (1.12.5)
|
nio4r (2.3.1-java)
|
||||||
mini_portile2 (~> 2.6.1)
|
nokogiri (1.8.2)
|
||||||
racc (~> 1.4)
|
mini_portile2 (~> 2.3.0)
|
||||||
oauth (0.5.5)
|
nokogiri (1.8.2-java)
|
||||||
oauth2 (1.4.7)
|
nokogiri (1.8.2-x64-mingw32)
|
||||||
faraday (>= 0.8, < 2.0)
|
mini_portile2 (~> 2.3.0)
|
||||||
jwt (>= 1.0, < 3.0)
|
nokogiri (1.8.2-x86-mingw32)
|
||||||
|
mini_portile2 (~> 2.3.0)
|
||||||
|
oauth (0.5.4)
|
||||||
|
oauth2 (1.4.0)
|
||||||
|
faraday (>= 0.8, < 0.13)
|
||||||
|
jwt (~> 1.0)
|
||||||
multi_json (~> 1.3)
|
multi_json (~> 1.3)
|
||||||
multi_xml (~> 0.5)
|
multi_xml (~> 0.5)
|
||||||
rack (>= 1.2, < 3)
|
rack (>= 1.2, < 3)
|
||||||
omniauth (1.9.1)
|
omniauth (1.6.1)
|
||||||
hashie (>= 3.4.6)
|
hashie (>= 3.4.6, < 3.6.0)
|
||||||
rack (>= 1.6.2, < 3)
|
rack (>= 1.6.2, < 3)
|
||||||
omniauth-bn-launcher (0.1.3)
|
omniauth-google-oauth2 (0.4.1)
|
||||||
omniauth (~> 1.3, >= 1.3.2)
|
jwt (~> 1.5.2)
|
||||||
omniauth-oauth2 (= 1.5.0)
|
multi_json (~> 1.3)
|
||||||
omniauth-bn-office365 (0.1.1)
|
|
||||||
omniauth (>= 1.3.2)
|
|
||||||
omniauth-oauth2 (>= 1.5.0)
|
|
||||||
omniauth-google-oauth2 (0.7.0)
|
|
||||||
jwt (>= 2.0)
|
|
||||||
omniauth (>= 1.1.1)
|
omniauth (>= 1.1.1)
|
||||||
omniauth-oauth2 (>= 1.5)
|
omniauth-oauth2 (>= 1.3.1)
|
||||||
omniauth-oauth (1.2.0)
|
omniauth-ldap (1.0.5)
|
||||||
|
net-ldap (~> 0.12)
|
||||||
|
omniauth (~> 1.0)
|
||||||
|
pyu-ruby-sasl (~> 0.0.3.2)
|
||||||
|
rubyntlm (~> 0.3.4)
|
||||||
|
omniauth-oauth (1.1.0)
|
||||||
oauth
|
oauth
|
||||||
omniauth (>= 1.0, < 3)
|
omniauth (~> 1.0)
|
||||||
omniauth-oauth2 (1.5.0)
|
omniauth-oauth2 (1.5.0)
|
||||||
oauth2 (~> 1.1)
|
oauth2 (~> 1.1)
|
||||||
omniauth (~> 1.2)
|
omniauth (~> 1.2)
|
||||||
omniauth-twitter (1.4.0)
|
omniauth-saml (1.8.1)
|
||||||
|
omniauth (~> 1.3)
|
||||||
|
ruby-saml (~> 1.4, >= 1.4.3)
|
||||||
|
omniauth-twitter (1.2.1)
|
||||||
|
json (~> 1.3)
|
||||||
omniauth-oauth (~> 1.1)
|
omniauth-oauth (~> 1.1)
|
||||||
rack
|
paperclip (5.2.1)
|
||||||
omniauth_openid_connect (0.3.5)
|
activemodel (>= 4.2.0)
|
||||||
addressable (~> 2.5)
|
activesupport (>= 4.2.0)
|
||||||
omniauth (~> 1.9)
|
cocaine (~> 0.5.5)
|
||||||
openid_connect (~> 1.1)
|
mime-types
|
||||||
openid_connect (1.2.0)
|
mimemagic (~> 0.3.0)
|
||||||
activemodel
|
pg (1.0.0)
|
||||||
attr_required (>= 1.0.0)
|
pg (1.0.0-x64-mingw32)
|
||||||
json-jwt (>= 1.5.0)
|
pg (1.0.0-x86-mingw32)
|
||||||
rack-oauth2 (>= 1.6.1)
|
puma (3.11.2)
|
||||||
swd (>= 1.0.0)
|
puma (3.11.2-java)
|
||||||
tzinfo
|
pyu-ruby-sasl (0.0.3.3)
|
||||||
validate_email
|
rack (2.0.5)
|
||||||
validate_url
|
rack-test (0.6.3)
|
||||||
webfinger (>= 1.0.1)
|
rack (>= 1.0)
|
||||||
os (1.1.1)
|
rails (5.0.7)
|
||||||
pagy (3.11.0)
|
actioncable (= 5.0.7)
|
||||||
parallel (1.20.1)
|
actionmailer (= 5.0.7)
|
||||||
parser (3.0.0.0)
|
actionpack (= 5.0.7)
|
||||||
ast (~> 2.4.1)
|
actionview (= 5.0.7)
|
||||||
pg (0.21.0)
|
activejob (= 5.0.7)
|
||||||
pluck_to_hash (1.0.2)
|
activemodel (= 5.0.7)
|
||||||
activerecord (>= 4.0.2)
|
activerecord (= 5.0.7)
|
||||||
activesupport (>= 4.0.2)
|
activesupport (= 5.0.7)
|
||||||
popper_js (1.16.0)
|
|
||||||
public_suffix (4.0.6)
|
|
||||||
puma (4.3.8)
|
|
||||||
nio4r (~> 2.0)
|
|
||||||
racc (1.5.2)
|
|
||||||
rack (2.2.3)
|
|
||||||
rack-oauth2 (1.16.0)
|
|
||||||
activesupport
|
|
||||||
attr_required
|
|
||||||
httpclient
|
|
||||||
json-jwt (>= 1.11.0)
|
|
||||||
rack (>= 2.1.0)
|
|
||||||
rack-test (1.1.0)
|
|
||||||
rack (>= 1.0, < 3)
|
|
||||||
rails (5.2.6)
|
|
||||||
actioncable (= 5.2.6)
|
|
||||||
actionmailer (= 5.2.6)
|
|
||||||
actionpack (= 5.2.6)
|
|
||||||
actionview (= 5.2.6)
|
|
||||||
activejob (= 5.2.6)
|
|
||||||
activemodel (= 5.2.6)
|
|
||||||
activerecord (= 5.2.6)
|
|
||||||
activestorage (= 5.2.6)
|
|
||||||
activesupport (= 5.2.6)
|
|
||||||
bundler (>= 1.3.0)
|
bundler (>= 1.3.0)
|
||||||
railties (= 5.2.6)
|
railties (= 5.0.7)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-controller-testing (1.0.5)
|
|
||||||
actionpack (>= 5.0.1.rc1)
|
|
||||||
actionview (>= 5.0.1.rc1)
|
|
||||||
activesupport (>= 5.0.1.rc1)
|
|
||||||
rails-dom-testing (2.0.3)
|
rails-dom-testing (2.0.3)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.3.0)
|
rails-html-sanitizer (1.0.4)
|
||||||
loofah (~> 2.3)
|
loofah (~> 2.2, >= 2.2.2)
|
||||||
railties (5.2.6)
|
rails-timeago (2.16.0)
|
||||||
actionpack (= 5.2.6)
|
actionpack (>= 3.1)
|
||||||
activesupport (= 5.2.6)
|
activesupport (>= 3.1)
|
||||||
|
railties (5.0.7)
|
||||||
|
actionpack (= 5.0.7)
|
||||||
|
activesupport (= 5.0.7)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.19.0, < 2.0)
|
thor (>= 0.18.1, < 2.0)
|
||||||
rainbow (3.0.0)
|
rake (12.3.1)
|
||||||
rake (13.0.3)
|
rb-fsevent (0.10.2)
|
||||||
random_password (0.1.1)
|
rb-inotify (0.9.10)
|
||||||
rb-fsevent (0.10.4)
|
ffi (>= 0.5.0, < 2)
|
||||||
rb-inotify (0.10.1)
|
ref (2.0.0)
|
||||||
ffi (~> 1.0)
|
request_store (1.4.0)
|
||||||
recaptcha (5.7.0)
|
|
||||||
json
|
|
||||||
redcarpet (3.5.1)
|
|
||||||
redis (4.2.5)
|
|
||||||
regexp_parser (2.1.1)
|
|
||||||
remote_syslog_logger (1.0.4)
|
|
||||||
syslog_protocol
|
|
||||||
representable (3.0.4)
|
|
||||||
declarative (< 0.1.0)
|
|
||||||
declarative-option (< 0.2.0)
|
|
||||||
uber (< 0.2.0)
|
|
||||||
request_store (1.5.0)
|
|
||||||
rack (>= 1.4)
|
rack (>= 1.4)
|
||||||
retriable (3.1.2)
|
ruby-saml (1.4.3)
|
||||||
rexml (3.2.5)
|
nokogiri (>= 1.5.10)
|
||||||
rspec-core (3.9.3)
|
rubyntlm (0.3.4)
|
||||||
rspec-support (~> 3.9.3)
|
sass (3.5.5)
|
||||||
rspec-expectations (3.9.4)
|
sass-listen (~> 4.0.0)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
sass-listen (4.0.0)
|
||||||
rspec-support (~> 3.9.0)
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||||
rspec-mocks (3.9.1)
|
rb-inotify (~> 0.9, >= 0.9.7)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
sass-rails (5.0.7)
|
||||||
rspec-support (~> 3.9.0)
|
railties (>= 4.0.0, < 6)
|
||||||
rspec-rails (3.9.1)
|
sass (~> 3.1)
|
||||||
actionpack (>= 3.0)
|
sprockets (>= 2.8, < 4.0)
|
||||||
activesupport (>= 3.0)
|
sprockets-rails (>= 2.0, < 4.0)
|
||||||
railties (>= 3.0)
|
tilt (>= 1.1, < 3)
|
||||||
rspec-core (~> 3.9.0)
|
simplecov (0.15.1)
|
||||||
rspec-expectations (~> 3.9.0)
|
docile (~> 1.1.0)
|
||||||
rspec-mocks (~> 3.9.0)
|
|
||||||
rspec-support (~> 3.9.0)
|
|
||||||
rspec-support (3.9.4)
|
|
||||||
rubocop (1.10.0)
|
|
||||||
parallel (~> 1.10)
|
|
||||||
parser (>= 3.0.0.0)
|
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
|
||||||
regexp_parser (>= 1.8, < 3.0)
|
|
||||||
rexml
|
|
||||||
rubocop-ast (>= 1.2.0, < 2.0)
|
|
||||||
ruby-progressbar (~> 1.7)
|
|
||||||
unicode-display_width (>= 1.4.0, < 3.0)
|
|
||||||
rubocop-ast (1.4.1)
|
|
||||||
parser (>= 2.7.1.5)
|
|
||||||
ruby-progressbar (1.11.0)
|
|
||||||
ruby2_keywords (0.0.4)
|
|
||||||
rubyzip (2.3.0)
|
|
||||||
sassc (2.4.0)
|
|
||||||
ffi (~> 1.9)
|
|
||||||
sassc-rails (2.1.2)
|
|
||||||
railties (>= 4.0.0)
|
|
||||||
sassc (>= 2.0)
|
|
||||||
sprockets (> 3.0)
|
|
||||||
sprockets-rails
|
|
||||||
tilt
|
|
||||||
sequel (5.41.0)
|
|
||||||
shoulda-matchers (3.1.3)
|
|
||||||
activesupport (>= 4.0.0)
|
|
||||||
signet (0.15.0)
|
|
||||||
addressable (~> 2.3)
|
|
||||||
faraday (>= 0.17.3, < 2.0)
|
|
||||||
jwt (>= 1.5, < 3.0)
|
|
||||||
multi_json (~> 1.10)
|
|
||||||
simplecov (0.16.1)
|
|
||||||
docile (~> 1.1)
|
|
||||||
json (>= 1.8, < 3)
|
json (>= 1.8, < 3)
|
||||||
simplecov-html (~> 0.10.0)
|
simplecov-html (~> 0.10.0)
|
||||||
simplecov-html (0.10.2)
|
simplecov-html (0.10.2)
|
||||||
spring (2.1.1)
|
slack-notifier (2.3.2)
|
||||||
|
spring (2.0.2)
|
||||||
|
activesupport (>= 4.2)
|
||||||
spring-watcher-listen (2.0.1)
|
spring-watcher-listen (2.0.1)
|
||||||
listen (>= 2.7, < 4.0)
|
listen (>= 2.7, < 4.0)
|
||||||
spring (>= 1.2, < 3.0)
|
spring (>= 1.2, < 3.0)
|
||||||
sprockets (3.7.2)
|
sprockets (3.7.1)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
rack (> 1, < 3)
|
rack (> 1, < 3)
|
||||||
sprockets-rails (3.2.2)
|
sprockets-rails (3.2.1)
|
||||||
actionpack (>= 4.0)
|
actionpack (>= 4.0)
|
||||||
activesupport (>= 4.0)
|
activesupport (>= 4.0)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
sqlite3 (1.3.13)
|
sqlite3 (1.3.13)
|
||||||
swd (1.2.0)
|
sqlite3 (1.3.13-x64-mingw32)
|
||||||
activesupport (>= 3)
|
sqlite3 (1.3.13-x86-mingw32)
|
||||||
attr_required (>= 0.0.5)
|
sqlite3 (1.3.13-x86-mswin32-60)
|
||||||
httpclient (>= 2.4)
|
therubyracer (0.12.3)
|
||||||
sync (0.5.0)
|
libv8 (~> 3.16.14.15)
|
||||||
syslog_protocol (0.9.2)
|
ref
|
||||||
term-ansicolor (1.7.1)
|
thor (0.20.0)
|
||||||
tins (~> 1.0)
|
|
||||||
thor (1.1.0)
|
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
tilt (2.0.10)
|
thread_safe (0.3.6-java)
|
||||||
tins (1.28.0)
|
tilt (2.0.8)
|
||||||
sync
|
turbolinks (5.1.0)
|
||||||
turbolinks (5.2.1)
|
turbolinks-source (~> 5.1)
|
||||||
turbolinks-source (~> 5.2)
|
turbolinks-source (5.1.0)
|
||||||
turbolinks-source (5.2.0)
|
tzinfo (1.2.5)
|
||||||
tzinfo (1.2.9)
|
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
tzinfo-data (1.2021.1)
|
tzinfo-data (1.2017.3)
|
||||||
tzinfo (>= 1.0.0)
|
tzinfo (>= 1.0.0)
|
||||||
uber (0.1.0)
|
uglifier (4.1.3)
|
||||||
uglifier (4.2.0)
|
|
||||||
execjs (>= 0.3.0, < 3)
|
execjs (>= 0.3.0, < 3)
|
||||||
unicode-display_width (2.0.0)
|
web-console (3.5.1)
|
||||||
validate_email (0.1.6)
|
|
||||||
activemodel (>= 3.0)
|
|
||||||
mail (>= 2.2.5)
|
|
||||||
validate_url (1.0.13)
|
|
||||||
activemodel (>= 3.0.0)
|
|
||||||
public_suffix
|
|
||||||
web-console (3.7.0)
|
|
||||||
actionview (>= 5.0)
|
actionview (>= 5.0)
|
||||||
activemodel (>= 5.0)
|
activemodel (>= 5.0)
|
||||||
bindex (>= 0.4.0)
|
bindex (>= 0.4.0)
|
||||||
railties (>= 5.0)
|
railties (>= 5.0)
|
||||||
webfinger (1.1.0)
|
websocket-driver (0.6.5)
|
||||||
activesupport
|
|
||||||
httpclient (>= 2.4)
|
|
||||||
webmock (3.12.1)
|
|
||||||
addressable (>= 2.3.6)
|
|
||||||
crack (>= 0.3.2)
|
|
||||||
hashdiff (>= 0.4.0, < 2.0.0)
|
|
||||||
webrick (1.7.0)
|
|
||||||
websocket-driver (0.7.3)
|
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.5)
|
websocket-driver (0.6.5-java)
|
||||||
xml-simple (1.1.8)
|
websocket-extensions (>= 0.1.0)
|
||||||
|
websocket-extensions (0.1.3)
|
||||||
|
xml-simple (1.1.5)
|
||||||
|
yt (0.28.5)
|
||||||
|
activesupport
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
|
java
|
||||||
ruby
|
ruby
|
||||||
|
x64-mingw32
|
||||||
|
x86-mingw32
|
||||||
|
x86-mswin32
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
action-cable-testing (~> 0.6)
|
bigbluebutton-api-ruby
|
||||||
aws-sdk-s3 (~> 1.88.1)
|
bootstrap-sass (= 3.3.0.0)
|
||||||
bcrypt (~> 3.1.7)
|
bootstrap-social-rails (~> 4.12)
|
||||||
bigbluebutton-api-ruby!
|
browser
|
||||||
bn-ldap-authentication (~> 0.1.4)
|
byebug
|
||||||
bootsnap (~> 1.7.2)
|
coffee-rails (~> 4.2)
|
||||||
bootstrap (~> 4.3.1)
|
dotenv-rails
|
||||||
byebug (~> 11.1)
|
faraday
|
||||||
cancancan (~> 2.3.0)
|
font-awesome-sass (= 4.7.0)
|
||||||
coveralls (~> 0.8.23)
|
http_accept_language
|
||||||
dotenv-rails (~> 2.7)
|
jbuilder (~> 2.5)
|
||||||
factory_bot_rails (~> 6.1)
|
jquery-datatables-rails (~> 3.4.0)
|
||||||
faker (~> 2.16)
|
jquery-rails
|
||||||
font-awesome-sass (~> 5.9.0)
|
jquery-ui-rails
|
||||||
google-cloud-storage (~> 1.30.0)
|
listen (~> 3.0.5)
|
||||||
hiredis (~> 0.6.3)
|
lograge
|
||||||
http_accept_language (~> 2.1.1)
|
mocha
|
||||||
i18n-language-mapping (~> 0.1.3.1)
|
omniauth (= 1.6.1)
|
||||||
jbuilder (~> 2.11.2)
|
omniauth-google-oauth2 (= 0.4.1)
|
||||||
jquery-rails (~> 4.4.0)
|
omniauth-ldap
|
||||||
jquery-ui-rails (~> 6.0.1)
|
omniauth-saml
|
||||||
listen (~> 3.0)
|
omniauth-twitter (= 1.2.1)
|
||||||
local_time (~> 2.1.0)
|
paperclip (~> 5.1)
|
||||||
lograge (~> 0.11.2)
|
pg
|
||||||
net-ldap (~> 0.17.0)
|
puma (~> 3.0)
|
||||||
omniauth (~> 1.9.1)
|
rails (~> 5.0.0, >= 5.0.0.1)
|
||||||
omniauth-bn-launcher (~> 0.1.3)
|
rails-timeago (~> 2.0)
|
||||||
omniauth-bn-office365 (~> 0.1.1)
|
sass-rails (~> 5.0)
|
||||||
omniauth-google-oauth2 (~> 0.7.0)
|
simplecov
|
||||||
omniauth-twitter (~> 1.4.0)
|
slack-notifier
|
||||||
omniauth_openid_connect (~> 0.3.5)
|
spring
|
||||||
pagy (~> 3.11.0)
|
spring-watcher-listen (~> 2.0.0)
|
||||||
pg (~> 0.18)
|
sqlite3
|
||||||
pluck_to_hash (~> 1.0.2)
|
therubyracer
|
||||||
puma (~> 4.3.8)
|
turbolinks (~> 5)
|
||||||
rails (~> 5.2.6)
|
tzinfo-data
|
||||||
rails-controller-testing (~> 1.0)
|
uglifier (>= 1.3.0)
|
||||||
random_password (~> 0.1.1)
|
web-console
|
||||||
recaptcha (~> 5.7.0)
|
yt (~> 0.28.0)
|
||||||
redcarpet (~> 3.5.1)
|
|
||||||
redis (~> 4.2.5)
|
RUBY VERSION
|
||||||
remote_syslog_logger (~> 1.0.4)
|
ruby 2.4.0p0
|
||||||
rspec-rails (~> 3.7)
|
|
||||||
rubocop (~> 1.10.0)
|
BUNDLED WITH
|
||||||
sassc-rails (~> 2.1.2)
|
1.16.1
|
||||||
sequel (~> 5.41.0)
|
|
||||||
shoulda-matchers (~> 3.1)
|
|
||||||
spring (~> 2.1)
|
|
||||||
spring-watcher-listen (~> 2.0)
|
|
||||||
sprockets (~> 3.7.2)
|
|
||||||
sqlite3 (~> 1.3.6)
|
|
||||||
tabler-rubygem!
|
|
||||||
turbolinks (~> 5.2.1)
|
|
||||||
tzinfo-data (~> 1.2021.1)
|
|
||||||
uglifier (~> 4.2.0)
|
|
||||||
web-console (~> 3.7)
|
|
||||||
webmock (~> 3.11)
|
|
||||||
|
|
165
LICENSE
|
@ -1,165 +0,0 @@
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
Version 3, 29 June 2007
|
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
|
|
||||||
This version of the GNU Lesser General Public License incorporates
|
|
||||||
the terms and conditions of version 3 of the GNU General Public
|
|
||||||
License, supplemented by the additional permissions listed below.
|
|
||||||
|
|
||||||
0. Additional Definitions.
|
|
||||||
|
|
||||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
|
||||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
|
||||||
General Public License.
|
|
||||||
|
|
||||||
"The Library" refers to a covered work governed by this License,
|
|
||||||
other than an Application or a Combined Work as defined below.
|
|
||||||
|
|
||||||
An "Application" is any work that makes use of an interface provided
|
|
||||||
by the Library, but which is not otherwise based on the Library.
|
|
||||||
Defining a subclass of a class defined by the Library is deemed a mode
|
|
||||||
of using an interface provided by the Library.
|
|
||||||
|
|
||||||
A "Combined Work" is a work produced by combining or linking an
|
|
||||||
Application with the Library. The particular version of the Library
|
|
||||||
with which the Combined Work was made is also called the "Linked
|
|
||||||
Version".
|
|
||||||
|
|
||||||
The "Minimal Corresponding Source" for a Combined Work means the
|
|
||||||
Corresponding Source for the Combined Work, excluding any source code
|
|
||||||
for portions of the Combined Work that, considered in isolation, are
|
|
||||||
based on the Application, and not on the Linked Version.
|
|
||||||
|
|
||||||
The "Corresponding Application Code" for a Combined Work means the
|
|
||||||
object code and/or source code for the Application, including any data
|
|
||||||
and utility programs needed for reproducing the Combined Work from the
|
|
||||||
Application, but excluding the System Libraries of the Combined Work.
|
|
||||||
|
|
||||||
1. Exception to Section 3 of the GNU GPL.
|
|
||||||
|
|
||||||
You may convey a covered work under sections 3 and 4 of this License
|
|
||||||
without being bound by section 3 of the GNU GPL.
|
|
||||||
|
|
||||||
2. Conveying Modified Versions.
|
|
||||||
|
|
||||||
If you modify a copy of the Library, and, in your modifications, a
|
|
||||||
facility refers to a function or data to be supplied by an Application
|
|
||||||
that uses the facility (other than as an argument passed when the
|
|
||||||
facility is invoked), then you may convey a copy of the modified
|
|
||||||
version:
|
|
||||||
|
|
||||||
a) under this License, provided that you make a good faith effort to
|
|
||||||
ensure that, in the event an Application does not supply the
|
|
||||||
function or data, the facility still operates, and performs
|
|
||||||
whatever part of its purpose remains meaningful, or
|
|
||||||
|
|
||||||
b) under the GNU GPL, with none of the additional permissions of
|
|
||||||
this License applicable to that copy.
|
|
||||||
|
|
||||||
3. Object Code Incorporating Material from Library Header Files.
|
|
||||||
|
|
||||||
The object code form of an Application may incorporate material from
|
|
||||||
a header file that is part of the Library. You may convey such object
|
|
||||||
code under terms of your choice, provided that, if the incorporated
|
|
||||||
material is not limited to numerical parameters, data structure
|
|
||||||
layouts and accessors, or small macros, inline functions and templates
|
|
||||||
(ten or fewer lines in length), you do both of the following:
|
|
||||||
|
|
||||||
a) Give prominent notice with each copy of the object code that the
|
|
||||||
Library is used in it and that the Library and its use are
|
|
||||||
covered by this License.
|
|
||||||
|
|
||||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
|
||||||
document.
|
|
||||||
|
|
||||||
4. Combined Works.
|
|
||||||
|
|
||||||
You may convey a Combined Work under terms of your choice that,
|
|
||||||
taken together, effectively do not restrict modification of the
|
|
||||||
portions of the Library contained in the Combined Work and reverse
|
|
||||||
engineering for debugging such modifications, if you also do each of
|
|
||||||
the following:
|
|
||||||
|
|
||||||
a) Give prominent notice with each copy of the Combined Work that
|
|
||||||
the Library is used in it and that the Library and its use are
|
|
||||||
covered by this License.
|
|
||||||
|
|
||||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
|
||||||
document.
|
|
||||||
|
|
||||||
c) For a Combined Work that displays copyright notices during
|
|
||||||
execution, include the copyright notice for the Library among
|
|
||||||
these notices, as well as a reference directing the user to the
|
|
||||||
copies of the GNU GPL and this license document.
|
|
||||||
|
|
||||||
d) Do one of the following:
|
|
||||||
|
|
||||||
0) Convey the Minimal Corresponding Source under the terms of this
|
|
||||||
License, and the Corresponding Application Code in a form
|
|
||||||
suitable for, and under terms that permit, the user to
|
|
||||||
recombine or relink the Application with a modified version of
|
|
||||||
the Linked Version to produce a modified Combined Work, in the
|
|
||||||
manner specified by section 6 of the GNU GPL for conveying
|
|
||||||
Corresponding Source.
|
|
||||||
|
|
||||||
1) Use a suitable shared library mechanism for linking with the
|
|
||||||
Library. A suitable mechanism is one that (a) uses at run time
|
|
||||||
a copy of the Library already present on the user's computer
|
|
||||||
system, and (b) will operate properly with a modified version
|
|
||||||
of the Library that is interface-compatible with the Linked
|
|
||||||
Version.
|
|
||||||
|
|
||||||
e) Provide Installation Information, but only if you would otherwise
|
|
||||||
be required to provide such information under section 6 of the
|
|
||||||
GNU GPL, and only to the extent that such information is
|
|
||||||
necessary to install and execute a modified version of the
|
|
||||||
Combined Work produced by recombining or relinking the
|
|
||||||
Application with a modified version of the Linked Version. (If
|
|
||||||
you use option 4d0, the Installation Information must accompany
|
|
||||||
the Minimal Corresponding Source and Corresponding Application
|
|
||||||
Code. If you use option 4d1, you must provide the Installation
|
|
||||||
Information in the manner specified by section 6 of the GNU GPL
|
|
||||||
for conveying Corresponding Source.)
|
|
||||||
|
|
||||||
5. Combined Libraries.
|
|
||||||
|
|
||||||
You may place library facilities that are a work based on the
|
|
||||||
Library side by side in a single library together with other library
|
|
||||||
facilities that are not Applications and are not covered by this
|
|
||||||
License, and convey such a combined library under terms of your
|
|
||||||
choice, if you do both of the following:
|
|
||||||
|
|
||||||
a) Accompany the combined library with a copy of the same work based
|
|
||||||
on the Library, uncombined with any other library facilities,
|
|
||||||
conveyed under the terms of this License.
|
|
||||||
|
|
||||||
b) Give prominent notice with the combined library that part of it
|
|
||||||
is a work based on the Library, and explaining where to find the
|
|
||||||
accompanying uncombined form of the same work.
|
|
||||||
|
|
||||||
6. Revised Versions of the GNU Lesser General Public License.
|
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions
|
|
||||||
of the GNU Lesser General Public License from time to time. Such new
|
|
||||||
versions will be similar in spirit to the present version, but may
|
|
||||||
differ in detail to address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
|
||||||
Library as you received it specifies that a certain numbered version
|
|
||||||
of the GNU Lesser General Public License "or any later version"
|
|
||||||
applies to it, you have the option of following the terms and
|
|
||||||
conditions either of that published version or of any later version
|
|
||||||
published by the Free Software Foundation. If the Library as you
|
|
||||||
received it does not specify a version number of the GNU Lesser
|
|
||||||
General Public License, you may choose any version of the GNU Lesser
|
|
||||||
General Public License ever published by the Free Software Foundation.
|
|
||||||
|
|
||||||
If the Library as you received it specifies that a proxy can decide
|
|
||||||
whether future versions of the GNU Lesser General Public License shall
|
|
||||||
apply, that proxy's public statement of acceptance of any version is
|
|
||||||
permanent authorization for you to choose that version for the
|
|
||||||
Library.
|
|
38
README.md
|
@ -1,37 +1,31 @@
|
||||||
# Greenlight
|
# Greenlight
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
Greenlight is a simple front-end interface for your BigBlueButton server. At its heart, Greenlight provides a minimalistic web-based application that allows users to:
|
* Create a meeting
|
||||||
|
* Invite others to the meeting
|
||||||
|
* Join a meeting
|
||||||
|
|
||||||
* Signup/Login with Google, Office365, OpenID Connect, or through the application itself.
|
Furthermore, if you configure GreenLight to use either Google, Twitter (both via OAuth2) or LDAP for authentication, users can login to record meetings and manage recordings.
|
||||||
* Manage your account settings and user preferences.
|
|
||||||
* Create and manage your own personal rooms ([BigBlueButton](https://github.com/bigbluebutton/bigbluebutton) sessions).
|
|
||||||
* Invite others to your room using a simple URL.
|
|
||||||
* View recordings and share them with others.
|
|
||||||
|
|
||||||
Interested? Try Greenlight out on our [demo server](https://demo.bigbluebutton.org/gl)!
|
## Overview video
|
||||||
|
|
||||||
Greenlight is also completely configurable. This means you can turn on/off features to make Greenlight fit your specific use case. For more information on Greenlight and its features, see our [documentation](http://docs.bigbluebutton.org/greenlight/gl-install.html).
|
For a overview of how GreenLight works, see the following video
|
||||||
|
|
||||||
For a overview of how Greenlight works, checkout our Introduction to Greenlight Video:
|
[](https://youtu.be/yGX3JCv7OVM)
|
||||||
|
|
||||||
[](https://youtu.be/Hso8yLzkqj8)
|
|
||||||
|
|
||||||
## Installation on a BigBlueButton Server
|
## Installation on the BigBlueButton server
|
||||||
|
We designed GreenLight to install on a [BigBlueButton 1.1-beta](http://docs.bigbluebutton.org/greenlight-v1.html) (or later) server. This means you don't need a separate server to run GreenLight.
|
||||||
|
|
||||||
Greenlight is designed to work on a [BigBlueButton 2.0](https://github.com/bigbluebutton/bigbluebutton) (or later) server.
|
For more informaiton see [Installing GreenLight](http://docs.bigbluebutton.org/greenlight-v1.html#installing-greenlight).
|
||||||
|
|
||||||
For information on installing Greenlight, checkout our [Installing Greenlight on a BigBlueButton Server](http://docs.bigbluebutton.org/greenlight/gl-install.html#installing-on-a-bigbluebutton-server) documentation.
|
# Source Code
|
||||||
|
|
||||||
## Source Code & Contributing
|
GreenLight is a rails 5 application.
|
||||||
|
|
||||||
Greenlight is built using Ruby on Rails. Many developers already know Rails well, and we wanted to create both a full front-end to BigBlueButton but also a reference implementation of how to fully leverage the [BigBlueButton API](http://docs.bigbluebutton.org/dev/api.html).
|
Many developers already know Rails well, and we wanted to create both a full front-end to BigBlueButton but also a reference implementation of how to fully leverage the [BigBlueButton API](http://docs.bigbluebutton.org/dev/api.html).
|
||||||
|
|
||||||
We invite you to build upon Greenlight and help make it better. See [Contributing to BigBlueButton](http://docs.bigbluebutton.org/support/faq.html#contributing-to-bigbluebutton).
|
We invite you to build upon GreenLight and help make it better. See [Contributing to BigBlueButton](http://docs.bigbluebutton.org/support/faq.html#contributing-to-bigbluebutton).
|
||||||
|
|
||||||
We invite your feedback, questions, and suggests about Greenlight too. Please post them to the [Greenlight mailing list](https://groups.google.com/forum/#!forum/bigbluebutton-greenlight).
|
We invite your feedback, questions, and suggests about GreenLight too. Please post them to the [developer mailing list](https://groups.google.com/forum/#!forum/bigbluebutton-dev).
|
||||||
|
|
||||||
To help with organization and consistency, we have implemented a Pull Request template that must be used for all Pull Requests. This template helps ensure that the project maintainers can review all PRs in a timely manner. When creating a Pull Request, please provide as much information as possible.
|
|
||||||
|
|
9
Rakefile
|
@ -1,15 +1,6 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||||
|
|
||||||
require_relative 'config/application'
|
require_relative 'config/application'
|
||||||
require 'rake/testtask'
|
|
||||||
|
|
||||||
Rake::TestTask.new do |t|
|
|
||||||
t.libs << "test"
|
|
||||||
t.test_files = FileList['test/test*.rb']
|
|
||||||
t.verbose = true
|
|
||||||
end
|
|
||||||
|
|
||||||
Rails.application.load_tasks
|
Rails.application.load_tasks
|
||||||
|
|
11
SECURITY.md
|
@ -1,11 +0,0 @@
|
||||||
# Security Policy
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
We support only the latest version of this project.
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
If you think you’ve found a security issue with BigBlueButton or Greenlight, we ask that you do a [responsible disclosure](https://en.wikipedia.org/wiki/Responsible_disclosure) and e-mail us directly at security@bigbluebutton.org.
|
|
||||||
|
|
||||||
We will respond to you quickly, work with you to examine the scope of the issue, and give priority to fixing it as soon as possible.
|
|
|
@ -1,6 +1,6 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
Copyright 2010, 2012, 2014 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’.
|
|
||||||
|
|
||||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
||||||
This license is copied below, and is also available with a FAQ at:
|
|
||||||
http://scripts.sil.org/OFL
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------------------------------
|
|
||||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
PREAMBLE
|
|
||||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
||||||
development of collaborative font projects, to support the font creation
|
|
||||||
efforts of academic and linguistic communities, and to provide a free and
|
|
||||||
open framework in which fonts may be shared and improved in partnership
|
|
||||||
with others.
|
|
||||||
|
|
||||||
The OFL allows the licensed fonts to be used, studied, modified and
|
|
||||||
redistributed freely as long as they are not sold by themselves. The
|
|
||||||
fonts, including any derivative works, can be bundled, embedded,
|
|
||||||
redistributed and/or sold with any software provided that any reserved
|
|
||||||
names are not used by derivative works. The fonts and derivatives,
|
|
||||||
however, cannot be released under any other type of license. The
|
|
||||||
requirement for fonts to remain under this license does not apply
|
|
||||||
to any document created using the fonts or their derivatives.
|
|
||||||
|
|
||||||
DEFINITIONS
|
|
||||||
"Font Software" refers to the set of files released by the Copyright
|
|
||||||
Holder(s) under this license and clearly marked as such. This may
|
|
||||||
include source files, build scripts and documentation.
|
|
||||||
|
|
||||||
"Reserved Font Name" refers to any names specified as such after the
|
|
||||||
copyright statement(s).
|
|
||||||
|
|
||||||
"Original Version" refers to the collection of Font Software components as
|
|
||||||
distributed by the Copyright Holder(s).
|
|
||||||
|
|
||||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
||||||
or substituting -- in part or in whole -- any of the components of the
|
|
||||||
Original Version, by changing formats or by porting the Font Software to a
|
|
||||||
new environment.
|
|
||||||
|
|
||||||
"Author" refers to any designer, engineer, programmer, technical
|
|
||||||
writer or other person who contributed to the Font Software.
|
|
||||||
|
|
||||||
PERMISSION & CONDITIONS
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
||||||
redistribute, and sell modified and unmodified copies of the Font
|
|
||||||
Software, subject to the following conditions:
|
|
||||||
|
|
||||||
1) Neither the Font Software nor any of its individual components,
|
|
||||||
in Original or Modified Versions, may be sold by itself.
|
|
||||||
|
|
||||||
2) Original or Modified Versions of the Font Software may be bundled,
|
|
||||||
redistributed and/or sold with any software, provided that each copy
|
|
||||||
contains the above copyright notice and this license. These can be
|
|
||||||
included either as stand-alone text files, human-readable headers or
|
|
||||||
in the appropriate machine-readable metadata fields within text or
|
|
||||||
binary files as long as those fields can be easily viewed by the user.
|
|
||||||
|
|
||||||
3) No Modified Version of the Font Software may use the Reserved Font
|
|
||||||
Name(s) unless explicit written permission is granted by the corresponding
|
|
||||||
Copyright Holder. This restriction only applies to the primary font name as
|
|
||||||
presented to the users.
|
|
||||||
|
|
||||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
||||||
Software shall not be used to promote, endorse or advertise any
|
|
||||||
Modified Version, except to acknowledge the contribution(s) of the
|
|
||||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
||||||
permission.
|
|
||||||
|
|
||||||
5) The Font Software, modified or unmodified, in part or in whole,
|
|
||||||
must be distributed entirely under this license, and must not be
|
|
||||||
distributed under any other license. The requirement for fonts to
|
|
||||||
remain under this license does not apply to any document created
|
|
||||||
using the Font Software.
|
|
||||||
|
|
||||||
TERMINATION
|
|
||||||
This license becomes null and void if any of the above conditions are
|
|
||||||
not met.
|
|
||||||
|
|
||||||
DISCLAIMER
|
|
||||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
||||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
||||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
||||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
||||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 771 B |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 8.0 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 673 B |
Before Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,236 @@
|
||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Handles live updating and initial population of the previous meetings and active meetings lists on
|
||||||
|
// the landing page using custom Actioncable events.
|
||||||
|
|
||||||
|
var MEETINGS = {}
|
||||||
|
var WAITING = {}
|
||||||
|
var LOADING_DELAY = 1750 // milliseconds.
|
||||||
|
|
||||||
|
// Updates the previous meetings section.
|
||||||
|
var updatePreviousMeetings = function(){
|
||||||
|
$("ul.previously-joined li").each(function(idx, li) {
|
||||||
|
var previous_meeting = $(li);
|
||||||
|
if(Object.keys(MEETINGS).indexOf(previous_meeting.text()) > -1){
|
||||||
|
previous_meeting.remove()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore excess on either side of user_id.
|
||||||
|
var trimUserId = function(user_id){
|
||||||
|
components = user_id.split('_')
|
||||||
|
return components.sort(function (a, b) {return b.length - a.length;})[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds a user by their user_id.
|
||||||
|
var findByUserId = function(users, user_id){
|
||||||
|
for(i = 0; i < users.length; i++){
|
||||||
|
if(trimUserId(users[i]['user_id']) == trimUserId(user_id)){
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a user to a meeting.
|
||||||
|
var addUser = function(data){
|
||||||
|
if(data['role'] == 'MODERATOR'){
|
||||||
|
MEETINGS[data['meeting']]['moderators'].push({'name': data['user'], 'user_id': data['user_id']})
|
||||||
|
} else {
|
||||||
|
MEETINGS[data['meeting']]['participants'].push({'name':data['user'], 'user_id': data['user_id']})
|
||||||
|
}
|
||||||
|
updateMeetingText(MEETINGS[data['meeting']])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes a user from a meeting.
|
||||||
|
var removeUser = function(data){
|
||||||
|
user = findByUserId(MEETINGS[data['meeting']]['moderators'], data['user_id'])
|
||||||
|
if(user == undefined){
|
||||||
|
user = findByUserId(MEETINGS[data['meeting']]['participants'], data['user_id']);
|
||||||
|
if(user == undefined){ return; }
|
||||||
|
MEETINGS[data['meeting']]['participants'].splice(user, 1);
|
||||||
|
} else {
|
||||||
|
MEETINGS[data['meeting']]['moderators'].splice(user, 1);
|
||||||
|
}
|
||||||
|
updateMeetingText(MEETINGS[data['meeting']])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the display text for an active meeting.
|
||||||
|
var updateMeetingText = function(m){
|
||||||
|
// If a meeting has a moderators property, it is running.
|
||||||
|
var body;
|
||||||
|
if(m.hasOwnProperty('moderators')){
|
||||||
|
var list;
|
||||||
|
if(m['moderators'].length + m['participants'].length == 0){
|
||||||
|
list = '(empty)'
|
||||||
|
} else {
|
||||||
|
list = m['moderators'].map(function(x){ return x['name']; }).join('(mod), ') +
|
||||||
|
(m['moderators'].length > 0 ? '(mod)' : '') +
|
||||||
|
(m['participants'].length > 0 && m['moderators'].length != 0 ? ', ' : '') +
|
||||||
|
(m['participants'].map(function(x){ return x['name']; }).join(', '))
|
||||||
|
}
|
||||||
|
body = '<a>' + m['name'] + '</a><i>: ' + list + '</i>'
|
||||||
|
// Otherwise it hasn't started (users waiting the join).
|
||||||
|
} else {
|
||||||
|
body = '<a>' + m['name'] + '</a><i> (not yet started): ' + m['users'].join(', ') + '</i>'
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the item doesn't exist, add it and set up join meeting event.
|
||||||
|
if($("li[id='" + m['name'].replace(' ', '_') + "']").length == 0){
|
||||||
|
var meeting_item = $("<li>" + body + "</li>")
|
||||||
|
meeting_item.attr('id', m['name'].replace(' ', '_'))
|
||||||
|
$('.actives').append(meeting_item);
|
||||||
|
|
||||||
|
// Set up join on click.
|
||||||
|
meeting_item.click(function(){
|
||||||
|
joinMeeting(m['name']);
|
||||||
|
});
|
||||||
|
// Otherwise, just change the body.
|
||||||
|
} else {
|
||||||
|
$("li[id='" + m['name'].replace(' ', '_') + "']").html(body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initially populates the active meetings when the page loads using the API.
|
||||||
|
var initialPopulate = function(){
|
||||||
|
// Only populate on room resources.
|
||||||
|
var chopped = window.location.href.split('/')
|
||||||
|
if (!window.location.href.includes('rooms') || chopped[chopped.length - 2] == $('body').data('current-user')) { return; }
|
||||||
|
|
||||||
|
$.post((window.location.href + '/statuses').replace('#', ''), {previously_joined: getPreviouslyJoined()})
|
||||||
|
.done(function(data) {
|
||||||
|
|
||||||
|
// Populate waiting meetings.
|
||||||
|
Object.keys(data['waiting']).forEach(function(key) {
|
||||||
|
WAITING[key] = {'name': key, 'users': data['waiting'][key]}
|
||||||
|
updateMeetingText(WAITING[key])
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add the meetings to the active meetings list.
|
||||||
|
for(var i = 0; i < data['active'].length; i++){
|
||||||
|
var meeting = data['active'][i]
|
||||||
|
|
||||||
|
var name = meeting['name']
|
||||||
|
var participants = meeting['participants']
|
||||||
|
var moderators = meeting['moderators']
|
||||||
|
|
||||||
|
// Create meeting.
|
||||||
|
MEETINGS[name] = {'name': name, 'participants': participants, 'moderators': moderators}
|
||||||
|
updateMeetingText(MEETINGS[name])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from previous meetings if they are active.
|
||||||
|
updatePreviousMeetings();
|
||||||
|
$('.hidden-list').show();
|
||||||
|
$('.active-spinner').hide();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets a list of known previously joined meetings.
|
||||||
|
var getPreviouslyJoined = function(){
|
||||||
|
var joinedMeetings = localStorage.getItem('joinedRooms-' + $('body').data('current-user'));
|
||||||
|
if (joinedMeetings == '' || joinedMeetings == null){ return []; }
|
||||||
|
return joinedMeetings.split(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes an active meeting.
|
||||||
|
var removeActiveMeeting = function(meeting){
|
||||||
|
if(meeting){ $("li[id='" + meeting['name'].replace(' ', '_') + "']").remove() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directly join a meeting from active meetings.
|
||||||
|
var joinMeeting = function(meeting_name){
|
||||||
|
if (meeting_name == undefined || meeting_name == null) { return; }
|
||||||
|
Meeting.getInstance().setUserName(localStorage.getItem('lastJoinedName'));
|
||||||
|
Meeting.getInstance().setMeetingId(meeting_name);
|
||||||
|
|
||||||
|
var jqxhr = Meeting.getInstance().getJoinMeetingResponse();
|
||||||
|
if (jqxhr) {
|
||||||
|
jqxhr.done(function(data) {
|
||||||
|
if (data.messageKey === 'wait_for_moderator') {
|
||||||
|
waitForModerator(Meeting.getInstance().getURL());
|
||||||
|
} else {
|
||||||
|
$(location).attr("href", data.response.join_url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
jqxhr.fail(function() {
|
||||||
|
console.info("meeting join failed");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$('.meeting-user-name').parent().addClass('has-error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only need to register for logged in users.
|
||||||
|
$(document).on('turbolinks:load', function(){
|
||||||
|
if($('body').data('current-user')){
|
||||||
|
MEETINGS = {}
|
||||||
|
// Ensure actives is empty.
|
||||||
|
$('.actives').empty();
|
||||||
|
|
||||||
|
if(!App.messages){
|
||||||
|
// Setup actioncable.
|
||||||
|
App.messages = App.cable.subscriptions.create('RefreshMeetingsChannel', {
|
||||||
|
received: function(data) {
|
||||||
|
console.log('Recieved ' + data['method'] + ' action for ' + data['meeting'] + ' with room id ' + data['room'] + '.')
|
||||||
|
if(data['room'] == $('body').data('current-user')){
|
||||||
|
// Handle webhook event.
|
||||||
|
if(data['method'] == 'create'){
|
||||||
|
// Create an empty meeting.
|
||||||
|
MEETINGS[data['meeting']] = {'name': data['meeting'],
|
||||||
|
'participants': [],
|
||||||
|
'moderators': []}
|
||||||
|
updateMeetingText(MEETINGS[data['meeting']])
|
||||||
|
updatePreviousMeetings();
|
||||||
|
if (WAITING.hasOwnProperty(data['meeting'])){ delete WAITING[data['meeting']]; }
|
||||||
|
} else if(data['method'] == 'destroy'){
|
||||||
|
removeActiveMeeting(MEETINGS[data['meeting']])
|
||||||
|
PreviousMeetings.uniqueAdd([data['meeting']])
|
||||||
|
delete MEETINGS[data['meeting']]
|
||||||
|
} else if(data['method'] == 'join'){
|
||||||
|
addUser(data)
|
||||||
|
} else if(data['method'] == 'leave'){
|
||||||
|
removeUser(data)
|
||||||
|
} else if(data['method'] == 'waiting'){
|
||||||
|
// Handle waiting meeting.
|
||||||
|
if(WAITING.hasOwnProperty(data['meeting'])){
|
||||||
|
WAITING[data['meeting']]['users'].push(data['user'])
|
||||||
|
updateMeetingText(WAITING[data['meeting']])
|
||||||
|
} else {
|
||||||
|
WAITING[data['meeting']] = {'name': data['meeting'],
|
||||||
|
'users': [data['user']]}
|
||||||
|
updateMeetingText(WAITING[data['meeting']])
|
||||||
|
}
|
||||||
|
} else if((data['method'] == 'no_longer_waiting') && (WAITING.hasOwnProperty(data['meeting']))){
|
||||||
|
WAITING[data['meeting']]['users'].splice(WAITING[data['meeting']]['users'].indexOf(data['user']), 1)
|
||||||
|
updateMeetingText(WAITING[data['meeting']])
|
||||||
|
if(WAITING[data['meeting']]['users'].length == 0){
|
||||||
|
removeActiveMeeting(WAITING[data['meeting']])
|
||||||
|
delete WAITING[data['meeting']]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Populating active meetings.');
|
||||||
|
// Short delay to hide the previous meetings population.
|
||||||
|
setTimeout(initialPopulate, LOADING_DELAY);
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,340 +0,0 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
$(document).on('turbolinks:load', function(){
|
|
||||||
var controller = $("body").data('controller');
|
|
||||||
var action = $("body").data('action');
|
|
||||||
|
|
||||||
// Only run on the admins page.
|
|
||||||
if (controller == "admins") {
|
|
||||||
if(action == "index") {
|
|
||||||
//clear the role filter if user clicks on the x
|
|
||||||
$(".clear-role").click(function() {
|
|
||||||
var search = new URL(location.href).searchParams.get('search')
|
|
||||||
|
|
||||||
var url = window.location.pathname + "?page=1"
|
|
||||||
|
|
||||||
if (search) {
|
|
||||||
url += "&search=" + search
|
|
||||||
}
|
|
||||||
|
|
||||||
window.location.replace(url);
|
|
||||||
})
|
|
||||||
|
|
||||||
// Handle selected user tags
|
|
||||||
$(".manage-users-tab").click(function() {
|
|
||||||
$(".manage-users-tab").removeClass("selected")
|
|
||||||
$(this).addClass("selected")
|
|
||||||
|
|
||||||
updateTabParams(this.id)
|
|
||||||
})
|
|
||||||
|
|
||||||
$('.selectpicker').selectpicker({
|
|
||||||
liveSearchPlaceholder: getLocalizedString('javascript.search.start')
|
|
||||||
});
|
|
||||||
// Fixes turbolinks issue with bootstrap select
|
|
||||||
$(window).trigger('load.bs.select.data-api');
|
|
||||||
|
|
||||||
// Display merge accounts modal with correct info
|
|
||||||
$(".merge-user").click(function() {
|
|
||||||
// Update the path of save button
|
|
||||||
$("#merge-save-access").attr("data-path", $(this).data("path"))
|
|
||||||
let userInfo = $(this).data("info")
|
|
||||||
$("#merge-to").html("") // Clear current inputs
|
|
||||||
|
|
||||||
let spanName = document.createElement("span"),
|
|
||||||
spanEmail = document.createElement("span"),
|
|
||||||
spanUid = document.createElement("span");
|
|
||||||
spanName.innerText = userInfo.name
|
|
||||||
spanEmail.setAttribute('class', 'text-muted d-block')
|
|
||||||
spanEmail.innerText = userInfo.email
|
|
||||||
spanUid.setAttribute('class', 'text-muted d-block')
|
|
||||||
spanUid.innerText = userInfo.uid
|
|
||||||
|
|
||||||
$("#merge-to").append(spanName, spanEmail, spanUid)
|
|
||||||
})
|
|
||||||
|
|
||||||
$("#mergeUserModal").on("show.bs.modal", function() {
|
|
||||||
$(".selectpicker").selectpicker('val','')
|
|
||||||
})
|
|
||||||
|
|
||||||
$(".bootstrap-select").on("click", function() {
|
|
||||||
$(".bs-searchbox").siblings().hide()
|
|
||||||
})
|
|
||||||
|
|
||||||
$("#merge-user-select ~ button").on("click", function() {
|
|
||||||
$(".bs-searchbox").siblings().hide()
|
|
||||||
})
|
|
||||||
|
|
||||||
$(".bs-searchbox input").on("input", function() {
|
|
||||||
if ($(".bs-searchbox input").val() == '' || $(".bs-searchbox input").val().length < 3) {
|
|
||||||
$(".select-options").remove()
|
|
||||||
$(".bs-searchbox").siblings().hide()
|
|
||||||
} else {
|
|
||||||
// Manually populate the dropdown
|
|
||||||
$.get($("#merge-user-select").data("path"), { search: $(".bs-searchbox input").val() }, function(users) {
|
|
||||||
$(".select-options").remove()
|
|
||||||
if (users.length > 0) {
|
|
||||||
users.forEach(function(user) {
|
|
||||||
let opt = document.createElement("option")
|
|
||||||
$(opt).val(JSON.stringify({uid: user.uid, email: user.email, name: user.name}))
|
|
||||||
$(opt).text(user.name)
|
|
||||||
$(opt).addClass("select-options")
|
|
||||||
$(opt).attr("data-subtext", user.email)
|
|
||||||
$("#merge-user-select").append(opt)
|
|
||||||
})
|
|
||||||
// Only refresh the select dropdown if there are results to show
|
|
||||||
$('#merge-user-select').selectpicker('refresh');
|
|
||||||
}
|
|
||||||
$(".bs-searchbox").siblings().show()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// User selects an option from the Room Access dropdown
|
|
||||||
$(".bootstrap-select").on("changed.bs.select", function(){
|
|
||||||
// Get the uid of the selected user
|
|
||||||
let user = $(".selectpicker").selectpicker('val')
|
|
||||||
if (user != "") {
|
|
||||||
let userInfo = JSON.parse(user)
|
|
||||||
$("#merge-from").html("") // Clear current input
|
|
||||||
|
|
||||||
let spanName = document.createElement("span"),
|
|
||||||
spanEmail = document.createElement("span"),
|
|
||||||
spanUid = document.createElement("span");
|
|
||||||
spanName.innerText = userInfo.name
|
|
||||||
spanEmail.setAttribute('class', 'text-muted d-block')
|
|
||||||
spanEmail.innerText = userInfo.email
|
|
||||||
spanUid.setAttribute('class', 'text-muted d-block')
|
|
||||||
spanUid.id = 'from-uid'
|
|
||||||
spanUid.innerText = userInfo.uid
|
|
||||||
|
|
||||||
$("#merge-from").append(spanName, spanEmail, spanUid)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else if(action == "site_settings"){
|
|
||||||
var urlParams = new URLSearchParams(window.location.search);
|
|
||||||
// Only load the colour selectors if on the appearance tab
|
|
||||||
if (urlParams.get("tab") == null || urlParams.get("tab") == "appearance") {
|
|
||||||
loadColourSelectors()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (action == "roles"){
|
|
||||||
// Refreshes the new role modal
|
|
||||||
$("#newRoleButton").click(function(){
|
|
||||||
$("#createRoleName").val("")
|
|
||||||
})
|
|
||||||
|
|
||||||
// Updates the colour picker to the correct colour
|
|
||||||
let role_colour = $("#role-colorinput-regular").data("colour")
|
|
||||||
$("#role-colorinput-regular").css("background-color", role_colour);
|
|
||||||
$("#role-colorinput-regular").css("border-color", role_colour);
|
|
||||||
|
|
||||||
loadRoleColourSelector(role_colour, $("#role-colorinput-regular").data("disabled"));
|
|
||||||
|
|
||||||
// Loads the jquery sortable so users can manually sort roles
|
|
||||||
$("#rolesSelect").sortable({
|
|
||||||
items: "a:not(.sort-disabled)",
|
|
||||||
update: function() {
|
|
||||||
$.ajax({
|
|
||||||
url: $(this).data("url"),
|
|
||||||
type: 'PATCH',
|
|
||||||
data: $(this).sortable('serialize')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Change the branding image to the image provided
|
|
||||||
function changeBrandingImage(path) {
|
|
||||||
var url = $("#branding-url").val()
|
|
||||||
$.post(path, {value: url, tab: "appearance"})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change the Legal URL to the one provided
|
|
||||||
function changeLegalURL(path) {
|
|
||||||
var url = $("#legal-url").val()
|
|
||||||
$.post(path, {value: url, tab: "administration"})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change the Privacy Policy URL to the one provided
|
|
||||||
function changePrivacyPolicyURL(path) {
|
|
||||||
var url = $("#privpolicy-url").val()
|
|
||||||
$.post(path, {value: url, tab: "administration"})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display the maintenance Banner
|
|
||||||
function displayMaintenanceBanner(path) {
|
|
||||||
var message = $("#maintenance-banner").val()
|
|
||||||
$.post(path, {value: message, tab: "administration"})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the maintenance Banner
|
|
||||||
function clearMaintenanceBanner(path) {
|
|
||||||
$.post(path, {value: "", tab: "administration"})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change the email mapping to the string provided
|
|
||||||
function changeEmailMapping(path) {
|
|
||||||
var url = $("#email-mapping").val()
|
|
||||||
$.post(path, {value: url, tab: "registration"})
|
|
||||||
}
|
|
||||||
|
|
||||||
function mergeUsers() {
|
|
||||||
let userToMerge = $("#from-uid").text()
|
|
||||||
$.post($("#merge-save-access").data("path"), {merge: userToMerge})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filters by role
|
|
||||||
function filterRole(role) {
|
|
||||||
var search = new URL(location.href).searchParams.get('search')
|
|
||||||
|
|
||||||
var url = window.location.pathname + "?page=1" + "&role=" + role
|
|
||||||
|
|
||||||
if (search) {
|
|
||||||
url += "&search=" + search
|
|
||||||
}
|
|
||||||
|
|
||||||
window.location.replace(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateTabParams(tab) {
|
|
||||||
var search_params = new URLSearchParams(window.location.search)
|
|
||||||
|
|
||||||
if (window.location.href.includes("tab=")) {
|
|
||||||
search_params.set("tab", tab)
|
|
||||||
} else {
|
|
||||||
search_params.append("tab", tab)
|
|
||||||
}
|
|
||||||
|
|
||||||
search_params.delete("page")
|
|
||||||
|
|
||||||
window.location.search = search_params.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadColourSelectors() {
|
|
||||||
const pickrRegular = new Pickr({
|
|
||||||
el: '#colorinput-regular',
|
|
||||||
theme: 'monolith',
|
|
||||||
useAsButton: true,
|
|
||||||
lockOpacity: true,
|
|
||||||
defaultRepresentation: 'HEX',
|
|
||||||
closeWithKey: 'Enter',
|
|
||||||
default: $("#colorinput-regular").css("background-color"),
|
|
||||||
|
|
||||||
components: {
|
|
||||||
palette: true,
|
|
||||||
preview: true,
|
|
||||||
hue: true,
|
|
||||||
interaction: {
|
|
||||||
input: true,
|
|
||||||
save: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const pickrLighten = new Pickr({
|
|
||||||
el: '#colorinput-lighten',
|
|
||||||
theme: 'monolith',
|
|
||||||
useAsButton: true,
|
|
||||||
lockOpacity: true,
|
|
||||||
defaultRepresentation: 'HEX',
|
|
||||||
closeWithKey: 'Enter',
|
|
||||||
default: $("#colorinput-lighten").css("background-color"),
|
|
||||||
|
|
||||||
components: {
|
|
||||||
palette: true,
|
|
||||||
preview: true,
|
|
||||||
hue: true,
|
|
||||||
interaction: {
|
|
||||||
input: true,
|
|
||||||
save: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const pickrDarken = new Pickr({
|
|
||||||
el: '#colorinput-darken',
|
|
||||||
theme: 'monolith',
|
|
||||||
useAsButton: true,
|
|
||||||
lockOpacity: true,
|
|
||||||
defaultRepresentation: 'HEX',
|
|
||||||
closeWithKey: 'Enter',
|
|
||||||
default: $("#colorinput-darken").css("background-color"),
|
|
||||||
|
|
||||||
components: {
|
|
||||||
palette: true,
|
|
||||||
preview: true,
|
|
||||||
hue: true,
|
|
||||||
interaction: {
|
|
||||||
input: true,
|
|
||||||
save: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
pickrRegular.on("save", (color, instance) => {
|
|
||||||
$.post($("#coloring-path-regular").val(), {value: color.toHEXA().toString()}).done(function() {
|
|
||||||
location.reload()
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
pickrLighten.on("save", (color, instance) => {
|
|
||||||
$.post($("#coloring-path-lighten").val(), {value: color.toHEXA().toString(), tab: "appearance"}).done(function() {
|
|
||||||
location.reload()
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
pickrDarken.on("save", (color, instance) => {
|
|
||||||
$.post($("#coloring-path-darken").val(), {value: color.toHEXA().toString(), tab: "appearance"}).done(function() {
|
|
||||||
location.reload()
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadRoleColourSelector(role_colour, disabled) {
|
|
||||||
if (!disabled) {
|
|
||||||
const pickrRoleRegular = new Pickr({
|
|
||||||
el: '#role-colorinput-regular',
|
|
||||||
theme: 'monolith',
|
|
||||||
useAsButton: true,
|
|
||||||
lockOpacity: true,
|
|
||||||
defaultRepresentation: 'HEX',
|
|
||||||
closeWithKey: 'Enter',
|
|
||||||
default: role_colour,
|
|
||||||
|
|
||||||
components: {
|
|
||||||
palette: true,
|
|
||||||
preview: true,
|
|
||||||
hue: true,
|
|
||||||
interaction: {
|
|
||||||
input: true,
|
|
||||||
save: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// On save update the colour input's background colour and update the role colour input
|
|
||||||
pickrRoleRegular.on("save", (color, instance) => {
|
|
||||||
$("#role-colorinput-regular").css("background-color", color.toHEXA().toString());
|
|
||||||
$("#role-colorinput-regular").css("border-color", color.toHEXA().toString());
|
|
||||||
$("#role-colour").val(color.toHEXA().toString());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
@ -14,26 +14,13 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
//= require jquery2
|
||||||
// listed below.
|
//= require jquery-ui
|
||||||
//
|
//= require dataTables/jquery.dataTables
|
||||||
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
|
||||||
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
//= require rails-timeago-all
|
||||||
//
|
//= require bootstrap-sprockets
|
||||||
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
//= require qrcode/qrcode.js
|
||||||
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
|
||||||
//
|
|
||||||
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
|
||||||
// about supported directives.
|
|
||||||
//
|
|
||||||
//= require turbolinks
|
//= require turbolinks
|
||||||
//= require jquery3
|
//= require_self
|
||||||
//= require tabler
|
|
||||||
//= require tabler.plugins
|
|
||||||
//= require jquery_ujs
|
|
||||||
//= require jquery-ui/widget
|
|
||||||
//= require jquery-ui/widgets/sortable
|
|
||||||
//= require pickr.min.js
|
|
||||||
//= require bootstrap-select.min.js
|
|
||||||
//= require local-time
|
|
||||||
//= require_tree .
|
//= require_tree .
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<%
|
|
||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
@ -14,14 +13,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser General Public License along
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
%>
|
|
||||||
|
|
||||||
<%= t('mailer.user.invite.subject') %>
|
# Place all the behaviors and hooks related to the matching controller here.
|
||||||
|
# All this logic will automatically be available in application.js.
|
||||||
<%= t('mailer.user.invite.info', name: @name) %>
|
# You can use CoffeeScript in this file: http://coffeescript.org/
|
||||||
|
|
||||||
<%= t('mailer.user.invite.username', email: @email) %>
|
|
||||||
|
|
||||||
<%= t('mailer.user.invite.signup_info') %>
|
|
||||||
|
|
||||||
<%= @url %>
|
|
|
@ -1,6 +1,6 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
@ -22,11 +22,13 @@
|
||||||
//= require_tree ./channels
|
//= require_tree ./channels
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var protocol = (window.location.protocol === "https:" ? "wss://" : "ws://");
|
var protocol = window.location.protocol === "https:" ? "wss://" : "ws://";
|
||||||
var host = window.GreenLight.WEBSOCKET_HOST || window.location.host + window.GreenLight.RELATIVE_ROOT;
|
var host = window.GreenLight.WEBSOCKET_HOST || window.location.host + window.GreenLight.RELATIVE_ROOT;
|
||||||
var url = protocol + host + '/cable';
|
var path = window.GreenLight.WEBSOCKET_PATH || '/cable';
|
||||||
|
var url = protocol + host + path;
|
||||||
|
|
||||||
this.App || (this.App = {});
|
this.App || (this.App = {});
|
||||||
|
|
||||||
App.cable = ActionCable.createConsumer(url);
|
App.cable = ActionCable.createConsumer(url);
|
||||||
|
|
||||||
}).call(this);
|
}).call(this);
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
var sessionStatusRefresh = function(url) {
|
||||||
|
$.get(url + "/session_status_refresh", function(html) {
|
||||||
|
$(".center-panel-wrapper").html(html);
|
||||||
|
displayRoomURL();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var enableMeetingUpdates = function() {
|
||||||
|
var meetingId = ''
|
||||||
|
if (!$(".page-wrapper.rooms").data('main-room')) {
|
||||||
|
meetingId = $(".page-wrapper.rooms").data('id');
|
||||||
|
}
|
||||||
|
App.meeting_update = App.cable.subscriptions.create({
|
||||||
|
channel: 'MeetingUpdatesChannel',
|
||||||
|
admin_id: $(".page-wrapper.rooms").data('admin-id'),
|
||||||
|
meeting_id: meetingId
|
||||||
|
},
|
||||||
|
{
|
||||||
|
received: function(data) {
|
||||||
|
if (data.action === 'moderator_joined') {
|
||||||
|
if (!Meeting.getInstance().getModJoined()) {
|
||||||
|
Meeting.getInstance().setModJoined(true);
|
||||||
|
if (Meeting.getInstance().getWaitingForMod()) {
|
||||||
|
loopJoin();
|
||||||
|
} else {
|
||||||
|
sessionStatusRefresh($('.meeting-url').val());
|
||||||
|
showAlert(I18n.meeting_started, 4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (data.action === 'meeting_ended') {
|
||||||
|
sessionStatusRefresh($('.meeting-url').val());
|
||||||
|
showAlert(I18n.meeting_ended, 4000);
|
||||||
|
} else if (data.action === 'user_waiting') {
|
||||||
|
// show a browser notification only to the owner
|
||||||
|
if (isRoomOwner()) {
|
||||||
|
showNotification(I18n.user_waiting_title, {
|
||||||
|
body: I18n.user_waiting_body.replace(/%{user}/, data.user).replace(/%{meeting}/, '"'+data.meeting_name+'"')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if(data.action === 'unable_to_join') {
|
||||||
|
showAlert(I18n.unable_to_join_mobile, 6000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var disableMeetingUpdates = function() {
|
||||||
|
App.meeting_update.unsubscribe();
|
||||||
|
delete App.meeting_update
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).on("turbolinks:load", function() {
|
||||||
|
// disable meeting updates if enabled from a previous page
|
||||||
|
if (App.meeting_update) {
|
||||||
|
disableMeetingUpdates();
|
||||||
|
}
|
||||||
|
if ($("body[data-controller=landing]").get(0)) {
|
||||||
|
if ($("body[data-action=rooms]").get(0)) {
|
||||||
|
enableMeetingUpdates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).call(this);
|
|
@ -0,0 +1,75 @@
|
||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
var enableRecordingUpdates = function() {
|
||||||
|
App.recording_update = App.cable.subscriptions.create({
|
||||||
|
channel: 'RecordingUpdatesChannel',
|
||||||
|
admin_id: $(".page-wrapper.rooms").data('admin-id'),
|
||||||
|
meeting_id: $(".page-wrapper.rooms").data('id')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
received: function(data) {
|
||||||
|
|
||||||
|
var recordings = Recordings.getInstance();
|
||||||
|
var table = recordings.table.api();
|
||||||
|
var row = table.row("#"+data.id);
|
||||||
|
|
||||||
|
if (data.action === 'update') {
|
||||||
|
var rowData = row.data();
|
||||||
|
|
||||||
|
rowData.published = data.published;
|
||||||
|
rowData.listed = data.listed;
|
||||||
|
table.row("#"+data.id).data(rowData);
|
||||||
|
recordings.draw();
|
||||||
|
|
||||||
|
var status = data.published ? (data.listed ? 'published' : 'unlisted') : 'unpublished';
|
||||||
|
showAlert(I18n['recording_'+status], 4000);
|
||||||
|
|
||||||
|
} else if (data.action === 'delete') {
|
||||||
|
row.remove();
|
||||||
|
recordings.draw();
|
||||||
|
showAlert(I18n.recording_deleted, 4000);
|
||||||
|
|
||||||
|
} else if (data.action === 'create') {
|
||||||
|
if (isRoomOwner() && row.length == 0) {
|
||||||
|
recordings.refresh();
|
||||||
|
showAlert(I18n.recording_created, 4000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var disableRecordingUpdates = function() {
|
||||||
|
App.recording_update.unsubscribe();
|
||||||
|
delete App.recording_update
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).on("turbolinks:load", function() {
|
||||||
|
// disable recording updates if enabled from a previous page
|
||||||
|
if (App.recording_update) {
|
||||||
|
disableRecordingUpdates();
|
||||||
|
}
|
||||||
|
if ($("body[data-controller=landing]").get(0)) {
|
||||||
|
if ($("body[data-action=rooms]").get(0)) {
|
||||||
|
enableRecordingUpdates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).call(this);
|
|
@ -1,37 +0,0 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
$(document).on('turbolinks:load', function(){
|
|
||||||
$("#cookies-agree-button").click(function() {
|
|
||||||
//create a cookie that lasts 1 year
|
|
||||||
var cookieDate = new Date();
|
|
||||||
cookieDate.setFullYear(cookieDate.getFullYear() + 1); //1 year from now
|
|
||||||
document.cookie = "cookie_consented=true; path=/; expires=" + cookieDate.toUTCString() + ";"
|
|
||||||
|
|
||||||
//hide the banner at the bottom
|
|
||||||
$(".cookies-banner").attr("style","display:none !important")
|
|
||||||
})
|
|
||||||
|
|
||||||
$("#maintenance-close").click(function(event) {
|
|
||||||
//create a cookie that lasts 1 day
|
|
||||||
|
|
||||||
var cookieDate = new Date()
|
|
||||||
cookieDate.setDate(cookieDate.getDate() + 1) //1 day from now
|
|
||||||
console.log("maintenance_window=" + $(event.target).data("date") + "; path=/; expires=" + cookieDate.toUTCString() + ";")
|
|
||||||
|
|
||||||
document.cookie = "maintenance_window=" + $(event.target).data("date") + "; path=/; expires=" + cookieDate.toUTCString() + ";"
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,57 +0,0 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
$(document).on('turbolinks:load', function(){
|
|
||||||
var controller = $("body").data('controller');
|
|
||||||
var action = $("body").data('action');
|
|
||||||
|
|
||||||
// Only run on the admins page.
|
|
||||||
if (controller == "admins" && action == "index") {
|
|
||||||
// show the modal with the correct form action url
|
|
||||||
$(".delete-user").click(function(){
|
|
||||||
$("#delete-confirm").parent().attr("action", $(this).data("path"))
|
|
||||||
|
|
||||||
if ($(this).data("delete") == "temp-delete") {
|
|
||||||
$("#perm-delete").hide()
|
|
||||||
$("#delete-warning").show()
|
|
||||||
} else {
|
|
||||||
$("#perm-delete").show()
|
|
||||||
$("#delete-warning").hide()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
$(".delete-user").click(function(data){
|
|
||||||
document.getElementById("delete-checkbox").checked = false
|
|
||||||
$("#delete-confirm").prop("disabled", "disabled")
|
|
||||||
|
|
||||||
if ($(data.target).data("delete") == "temp-delete") {
|
|
||||||
$("#perm-delete").hide()
|
|
||||||
$("#delete-warning").show()
|
|
||||||
} else {
|
|
||||||
$("#perm-delete").show()
|
|
||||||
$("#delete-warning").hide()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$("#delete-checkbox").click(function(data){
|
|
||||||
if (document.getElementById("delete-checkbox").checked) {
|
|
||||||
$("#delete-confirm").removeAttr("disabled")
|
|
||||||
} else {
|
|
||||||
$("#delete-confirm").prop("disabled", "disabled")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -0,0 +1,297 @@
|
||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/** global: Meeting */
|
||||||
|
/** global: PreviousMeetings */
|
||||||
|
/** global: QRCode */
|
||||||
|
/** global: Recordings */
|
||||||
|
/** global: Turbolinks */
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
var qrcode;
|
||||||
|
|
||||||
|
var waitForModerator = function(url) {
|
||||||
|
window.localStorage.setItem("waitingName", $('.meeting-user-name').val());
|
||||||
|
$.post(url + "/wait", {name: $('.meeting-user-name').val()}, function(html) {
|
||||||
|
$(".center-panel-wrapper").html(html);
|
||||||
|
});
|
||||||
|
if (!Meeting.getInstance().getWaitingForMod()) {
|
||||||
|
Meeting.getInstance().setWaitingForMod(true);
|
||||||
|
if (Meeting.getInstance().getModJoined()) {
|
||||||
|
loopJoin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var init = function() {
|
||||||
|
Meeting.clear();
|
||||||
|
var nameInput = $('.meeting-user-name');
|
||||||
|
if (!nameInput.val()) {
|
||||||
|
var lastName = window.localStorage.getItem('lastJoinedName');
|
||||||
|
if (lastName !== 'undefined') {
|
||||||
|
nameInput.val(lastName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup event handlers
|
||||||
|
$('.center-panel-wrapper').on ('click', '.meeting-join', function () {
|
||||||
|
var name = $('.meeting-user-name').val();
|
||||||
|
Meeting.getInstance().setUserName(name);
|
||||||
|
Meeting.getInstance().setMeetingId($(".page-wrapper").data('id'));
|
||||||
|
|
||||||
|
// a user name is set, join the user into the session
|
||||||
|
if (name !== undefined && name !== null) {
|
||||||
|
var jqxhr = Meeting.getInstance().getJoinMeetingResponse();
|
||||||
|
if (jqxhr) {
|
||||||
|
jqxhr.done(function(data) {
|
||||||
|
if (data.messageKey === 'wait_for_moderator') {
|
||||||
|
waitForModerator(Meeting.getInstance().getURL());
|
||||||
|
} else if (data.messageKey === 'ok') {
|
||||||
|
$(location).attr("href", data.response.join_url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
jqxhr.fail(function() {
|
||||||
|
console.info("meeting join failed");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$('.meeting-user-name').parent().addClass('has-error');
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not user name was set it means we must ask for a name
|
||||||
|
} else {
|
||||||
|
$(location).attr("href", Meeting.getInstance().getURL());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on ('click', '.meeting-start', function () {
|
||||||
|
Turbolinks.visit($('.meeting-url').val());
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on ('input', '.meeting-user-name', function () {
|
||||||
|
if ($(this).val() === '') {
|
||||||
|
$(this).parent().addClass('has-error')
|
||||||
|
} else {
|
||||||
|
$(this).parent().removeClass('has-error')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on ('keypress', '.meeting-user-name', function (event) {
|
||||||
|
if (event.keyCode === 13) {
|
||||||
|
event.preventDefault();
|
||||||
|
$('.meeting-join').click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on ('keypress', '.meeting-name', function (event) {
|
||||||
|
if (event.keyCode === 13) {
|
||||||
|
event.preventDefault();
|
||||||
|
$('.meeting-start').click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on ('click', '.meeting-end', function () {
|
||||||
|
var jqxhr = Meeting.getInstance().endMeeting();
|
||||||
|
var btn = $(this);
|
||||||
|
btn.prop("disabled", true);
|
||||||
|
jqxhr.fail(function() {
|
||||||
|
console.info("meeting end failed");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on ('click', '.meeting-url-copy', function () {
|
||||||
|
var meetingURLInput = $('.meeting-url');
|
||||||
|
|
||||||
|
// copy URL
|
||||||
|
meetingURLInput.select();
|
||||||
|
try {
|
||||||
|
var success = document.execCommand("copy");
|
||||||
|
if (success) {
|
||||||
|
meetingURLInput.blur();
|
||||||
|
$(this).trigger('hint', [$(this).data('copied-hint')]);
|
||||||
|
} else {
|
||||||
|
$(this).trigger('hint', [$(this).data('copy-error')]);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
$(this).trigger('hint', [$(this).data('copy-error')]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on('hint', '.meeting-url-copy', function (event, msg) {
|
||||||
|
$(this).focus();
|
||||||
|
$(this).attr('title', msg)
|
||||||
|
.tooltip('fixTitle')
|
||||||
|
.tooltip('show')
|
||||||
|
.attr('title', $(this).data('copy-hint'))
|
||||||
|
.tooltip('fixTitle');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on('mouseleave', '.meeting-url-copy', function () {
|
||||||
|
$(this).blur();
|
||||||
|
});
|
||||||
|
|
||||||
|
// button used to send invitations to the meeting (i.e. "mailto:" link)
|
||||||
|
$('.center-panel-wrapper').on('click', '.meeting-invite', function () {
|
||||||
|
var meetingURL = Meeting.getInstance().getURL();
|
||||||
|
var subject = $(this).data("invite-subject");
|
||||||
|
var body = $(this).data("invite-body").replace("&&URL&&", meetingURL);
|
||||||
|
var mailto = "mailto:?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body);
|
||||||
|
window.open(mailto);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on ('click', '.meeting-url-qrcode', function () {
|
||||||
|
var meetingURL;
|
||||||
|
|
||||||
|
try {
|
||||||
|
meetingURL = $('.meeting-url').val();
|
||||||
|
if ($('.meeting-url-qrcode-group').is(':empty')) {
|
||||||
|
// generate code
|
||||||
|
qrcode = new QRCode($(".meeting-url-qrcode-group")[0], {
|
||||||
|
text: meetingURL,
|
||||||
|
width: 128,
|
||||||
|
height: 128,
|
||||||
|
colorDark : "#000000",
|
||||||
|
colorLight : "#ffffff",
|
||||||
|
correctLevel : QRCode.CorrectLevel.H
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// clear the code.
|
||||||
|
qrcode.clear();
|
||||||
|
// make another code.
|
||||||
|
qrcode.makeCode(meetingURL);
|
||||||
|
}
|
||||||
|
$(this).trigger('hint', [$(this).data('qrcode-generated-hint')]);
|
||||||
|
} catch (err) {
|
||||||
|
$(this).trigger('hint', [$(this).data('qrcode-generate-error')]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on('hint', '.meeting-url-qrcode', function (event, msg) {
|
||||||
|
$(this).focus();
|
||||||
|
$(this).attr('title', msg)
|
||||||
|
.tooltip('fixTitle')
|
||||||
|
.tooltip('show')
|
||||||
|
.attr('title', $(this).data('qrcode-generate-hint'))
|
||||||
|
.tooltip('fixTitle');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on('mouseleave', '.meeting-url-qrcode', function () {
|
||||||
|
$(this).blur();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on('focus', '.meeting-url', function () {
|
||||||
|
$(this).select();
|
||||||
|
});
|
||||||
|
|
||||||
|
// only allow ctrl commands
|
||||||
|
$('.center-panel-wrapper').on('keydown', '.meeting-url', function (event) {
|
||||||
|
if(!event.ctrlKey) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// enable tooltips
|
||||||
|
var options = {
|
||||||
|
selector: '.has-tooltip',
|
||||||
|
container: 'body'
|
||||||
|
};
|
||||||
|
$(document).tooltip(options);
|
||||||
|
options = {
|
||||||
|
selector: '.bottom-tooltip',
|
||||||
|
container: 'body',
|
||||||
|
placement: 'bottom'
|
||||||
|
};
|
||||||
|
$(document).tooltip(options);
|
||||||
|
|
||||||
|
// focus name input or join button
|
||||||
|
if ($('.meeting-name').is(':visible')) {
|
||||||
|
$('.meeting-name').focus();
|
||||||
|
} else if ($('.meeting-user-name').is(':visible')) {
|
||||||
|
$('.meeting-user-name').focus();
|
||||||
|
} else {
|
||||||
|
$('.meeting-join').focus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var initIndex = function() {
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on('input', '.meeting-name', function () {
|
||||||
|
var newId = $(this).val();
|
||||||
|
Meeting.getInstance().setMeetingId(newId);
|
||||||
|
$(".page-wrapper.meetings").data('id', newId);
|
||||||
|
$('.meeting-url').val(Meeting.getInstance().getURL());
|
||||||
|
$('.join-meeting-title').text('"'+newId+'"');
|
||||||
|
if (newId === '') {
|
||||||
|
$('.invite-join-wrapper').addClass('hidden');
|
||||||
|
} else {
|
||||||
|
$('.invite-join-wrapper').removeClass('hidden');
|
||||||
|
}
|
||||||
|
if (!$('.meeting-url-qrcode-group').is(':empty')) {
|
||||||
|
$('.meeting-url-qrcode-group').empty();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
PreviousMeetings.init('joinedMeetings');
|
||||||
|
};
|
||||||
|
|
||||||
|
var initMeetings = function() {
|
||||||
|
$('.meeting-url').val(Meeting.getInstance().getURL());
|
||||||
|
};
|
||||||
|
|
||||||
|
var initRooms = function() {
|
||||||
|
displayRoomURL();
|
||||||
|
var roomAdmin = $('.page-wrapper.rooms').data('admin-id');
|
||||||
|
|
||||||
|
$('.center-panel-wrapper').on('input', '.meeting-name', function () {
|
||||||
|
var newId = $(this).val();
|
||||||
|
Meeting.getInstance().setMeetingId(newId);
|
||||||
|
$('.meeting-url').val(Meeting.getInstance().getURL());
|
||||||
|
$('.join-meeting-title').text('"'+newId+'"');
|
||||||
|
if (newId === '') {
|
||||||
|
$('.invite-join-wrapper').addClass('hidden');
|
||||||
|
} else {
|
||||||
|
$('.invite-join-wrapper').removeClass('hidden');
|
||||||
|
}
|
||||||
|
if (!$('.meeting-url-qrcode-group').is(':empty')) {
|
||||||
|
$('.meeting-url-qrcode-group').empty();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($(".page-wrapper.rooms").data('main-room')) {
|
||||||
|
PreviousMeetings.init('joinedRooms-'+roomAdmin);
|
||||||
|
|
||||||
|
if ($('input.meeting-name').val() !== '') {
|
||||||
|
$('input.meeting-name').trigger('input');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Recordings.getInstance().refresh();
|
||||||
|
Recordings.getInstance().setupActionHandlers();
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).on("turbolinks:load", function() {
|
||||||
|
if ($("body[data-controller=landing]").get(0)) {
|
||||||
|
init();
|
||||||
|
if ($("body[data-action=index]").get(0)) {
|
||||||
|
initIndex();
|
||||||
|
} else if ($("body[data-action=meetings]").get(0)) {
|
||||||
|
initMeetings();
|
||||||
|
} else if ($("body[data-action=rooms]").get(0)) {
|
||||||
|
initRooms();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).call(this);
|
|
@ -1,51 +0,0 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
$(document).on('turbolinks:load', function(){
|
|
||||||
$.rails.refreshCSRFTokens();
|
|
||||||
})
|
|
||||||
|
|
||||||
document.addEventListener("turbolinks:before-cache", function() {
|
|
||||||
$(".alert").remove()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Gets the localized string
|
|
||||||
function getLocalizedString(key) {
|
|
||||||
var keyArr = key.split(".")
|
|
||||||
var translated = I18n
|
|
||||||
|
|
||||||
// Search current language for the key
|
|
||||||
try {
|
|
||||||
keyArr.forEach(function(k) {
|
|
||||||
translated = translated[k]
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
// Key is missing in selected language so default to english
|
|
||||||
translated = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// If key is not found, search the fallback language for the key
|
|
||||||
if (translated === null || translated === undefined) {
|
|
||||||
translated = I18nFallback
|
|
||||||
|
|
||||||
keyArr.forEach(function(k) {
|
|
||||||
translated = translated[k]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return translated
|
|
||||||
}
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Meeting class
|
||||||
|
|
||||||
|
_meetingInstance = null
|
||||||
|
|
||||||
|
class @Meeting
|
||||||
|
constructor: (@meetingId, @type, @userName, @adminId) ->
|
||||||
|
|
||||||
|
# Gets the current instance or creates a new one
|
||||||
|
@getInstance: ->
|
||||||
|
if _meetingInstance
|
||||||
|
return _meetingInstance
|
||||||
|
meetingId = $(".page-wrapper").data('id')
|
||||||
|
type = $("body").data('resource')
|
||||||
|
name = $('.meeting-user-name').val()
|
||||||
|
adminId = $(".page-wrapper").data('admin-id')
|
||||||
|
_meetingInstance = new Meeting(meetingId, type, name, adminId)
|
||||||
|
return _meetingInstance
|
||||||
|
|
||||||
|
@clear: ->
|
||||||
|
_meetingInstance = null
|
||||||
|
|
||||||
|
@buildMeetingURL: (meetingId, type, adminId) ->
|
||||||
|
fullId = ''
|
||||||
|
if adminId
|
||||||
|
fullId = encodeURIComponent(adminId) + '/' + encodeURIComponent(meetingId)
|
||||||
|
else
|
||||||
|
fullId = encodeURIComponent(meetingId)
|
||||||
|
return @buildRootURL() + '/' + type + '/' + fullId
|
||||||
|
|
||||||
|
@buildRootURL: ->
|
||||||
|
url = location.protocol + '//' + location.hostname
|
||||||
|
if location.port
|
||||||
|
url += ':' + location.port
|
||||||
|
if GreenLight.RELATIVE_ROOT
|
||||||
|
url += GreenLight.RELATIVE_ROOT
|
||||||
|
return url
|
||||||
|
|
||||||
|
# Sends the end meeting request
|
||||||
|
# Returns a response object
|
||||||
|
endMeeting: ->
|
||||||
|
return $.ajax({
|
||||||
|
url: @getURL() + "/end",
|
||||||
|
type: 'DELETE'
|
||||||
|
})
|
||||||
|
|
||||||
|
# Makes a call to get the join meeting url
|
||||||
|
# Returns a response object
|
||||||
|
# The response object contains the URL to join the meeting
|
||||||
|
getJoinMeetingResponse: ->
|
||||||
|
if !@userName
|
||||||
|
showAlert(I18n.enter_name, 4000, 'danger')
|
||||||
|
return false
|
||||||
|
return $.get @getURL() + "/join?name=" + @userName, (data) =>
|
||||||
|
# update name used to join meeting
|
||||||
|
localStorage.setItem('lastJoinedName', @getUserName())
|
||||||
|
|
||||||
|
if data.messageKey == 'ok'
|
||||||
|
key = ''
|
||||||
|
if @type == 'meetings'
|
||||||
|
key = 'joinedMeetings'
|
||||||
|
else if @type == 'rooms'
|
||||||
|
key = 'joinedRooms-'+@adminId
|
||||||
|
|
||||||
|
# update previously joined meetings/rooms on client
|
||||||
|
try
|
||||||
|
joinedMeetings = localStorage.getItem(key) || ''
|
||||||
|
joinedMeetings = joinedMeetings.split(',')
|
||||||
|
joinedMeetings = joinedMeetings.filter (item) => item != @meetingId.toString()
|
||||||
|
if joinedMeetings.length >= 5
|
||||||
|
joinedMeetings.splice(0, 1)
|
||||||
|
joinedMeetings.push(@meetingId)
|
||||||
|
localStorage.setItem(key, joinedMeetings.join(','))
|
||||||
|
catch err
|
||||||
|
localStorage.setItem(key, @meetingId)
|
||||||
|
|
||||||
|
getMeetingId: ->
|
||||||
|
return @meetingId
|
||||||
|
|
||||||
|
setMeetingId: (id) ->
|
||||||
|
@meetingId = id
|
||||||
|
return this
|
||||||
|
|
||||||
|
getAdminId: ->
|
||||||
|
return @adminId
|
||||||
|
|
||||||
|
setAdminId: (id) ->
|
||||||
|
@adminId = id
|
||||||
|
return this
|
||||||
|
|
||||||
|
getType: ->
|
||||||
|
return @type
|
||||||
|
|
||||||
|
setType: (type) ->
|
||||||
|
@type = type
|
||||||
|
return this
|
||||||
|
|
||||||
|
getURL: ->
|
||||||
|
return Meeting.buildMeetingURL(@meetingId, @type, @adminId)
|
||||||
|
|
||||||
|
getUserName: ->
|
||||||
|
return @userName
|
||||||
|
|
||||||
|
setUserName: (name) ->
|
||||||
|
@userName = name
|
||||||
|
return this
|
||||||
|
|
||||||
|
getModJoined: ->
|
||||||
|
return @modJoined
|
||||||
|
|
||||||
|
setModJoined: (modJoined) ->
|
||||||
|
@modJoined = modJoined
|
||||||
|
return this
|
||||||
|
|
||||||
|
getWaitingForMod: ->
|
||||||
|
return @waitingForMod
|
||||||
|
|
||||||
|
setWaitingForMod: (wMod) ->
|
||||||
|
@waitingForMod = wMod
|
||||||
|
return this
|
|
@ -0,0 +1,55 @@
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Previous Meetings class
|
||||||
|
|
||||||
|
class @PreviousMeetings
|
||||||
|
MAX_MEETINGS = 5
|
||||||
|
|
||||||
|
# initializes and populates previous meetings list with entries from localStorage
|
||||||
|
@init: (type) ->
|
||||||
|
$('.center-panel-wrapper').off 'click', '.fill-meeting-name'
|
||||||
|
$('.center-panel-wrapper').on 'click', '.fill-meeting-name', (event, msg) ->
|
||||||
|
name = $(this).text()
|
||||||
|
$('input.meeting-name').val(name).trigger('input')
|
||||||
|
|
||||||
|
$('ul.previously-joined').empty()
|
||||||
|
joinedMeetings = localStorage.getItem(type)
|
||||||
|
if joinedMeetings && joinedMeetings.length > 0
|
||||||
|
joinedMeetings = joinedMeetings.split(',')
|
||||||
|
PreviousMeetings.append(joinedMeetings.reverse())
|
||||||
|
|
||||||
|
# adds to previous meetings list if its unique
|
||||||
|
@uniqueAdd: (names) ->
|
||||||
|
meetings = $('ul.previously-joined > li').toArray().map( (li) ->
|
||||||
|
return li.innerText
|
||||||
|
)
|
||||||
|
index = meetings.indexOf('')
|
||||||
|
if index > 1
|
||||||
|
meetings.splice(index, 1)
|
||||||
|
if Array.isArray(names)
|
||||||
|
names = names.filter( (value) ->
|
||||||
|
return $.inArray(value, meetings) == -1
|
||||||
|
)
|
||||||
|
PreviousMeetings.append(names)
|
||||||
|
|
||||||
|
@append: (meeting_names) ->
|
||||||
|
for m in meeting_names
|
||||||
|
if $('ul.previously-joined > li').length > MAX_MEETINGS
|
||||||
|
break
|
||||||
|
$('ul.previously-joined').append('<li><a class="fill-meeting-name">'+m+'</a></li>')
|
||||||
|
|
||||||
|
$('.center-panel-wrapper .previously-joined-wrapper').removeClass('hidden')
|
|
@ -1,48 +0,0 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// Handle changing of settings tabs.
|
|
||||||
$(document).on('turbolinks:load', function(){
|
|
||||||
var controller = $("body").data('controller');
|
|
||||||
var action = $("body").data('action');
|
|
||||||
|
|
||||||
if (controller == "rooms" && action == "show"
|
|
||||||
|| controller == "rooms" && action == "update"
|
|
||||||
|| controller == "users" && action == "recordings"
|
|
||||||
|| controller == "admins" && action == "server_recordings"){
|
|
||||||
// Handle recording emails.
|
|
||||||
$('.email-link').each(function(){
|
|
||||||
$(this).click(function(){
|
|
||||||
var subject = $(".username").text() + " " + getLocalizedString('javascript.room.mailer.subject');
|
|
||||||
var body = getLocalizedString('javascript.room.mailer.body') + "\n\n" + $(this).attr("data-pres-link");
|
|
||||||
var autogenerated = getLocalizedString('javascript.room.mailer.autogenerated') + "\n";
|
|
||||||
var footer = getLocalizedString('javascript.room.mailer.footer');
|
|
||||||
|
|
||||||
var url = "mailto:?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body) + encodeURIComponent(autogenerated) + encodeURIComponent(footer);
|
|
||||||
var win = window.open(url, '_blank');
|
|
||||||
|
|
||||||
win.focus();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle recording delete modal
|
|
||||||
$(".delete-rec").click(function() {
|
|
||||||
$("#delete-rec-confirm").parent().attr("action", $(this).data("path"))
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -0,0 +1,430 @@
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Recordings class
|
||||||
|
|
||||||
|
_recordingsInstance = null
|
||||||
|
|
||||||
|
class @Recordings
|
||||||
|
# adding or removing a column will require updates to all subsequent column positions
|
||||||
|
COLUMNS = [
|
||||||
|
'DATE',
|
||||||
|
'NAME',
|
||||||
|
'PREVIEW',
|
||||||
|
'DURATION',
|
||||||
|
'PARTICIPANTS',
|
||||||
|
'PLAYBACK',
|
||||||
|
'VISIBILITY',
|
||||||
|
'LISTED',
|
||||||
|
'ACTION'
|
||||||
|
]
|
||||||
|
COLUMN = {}
|
||||||
|
i = 0
|
||||||
|
for c in COLUMNS
|
||||||
|
COLUMN[c] = i++
|
||||||
|
|
||||||
|
constructor: ->
|
||||||
|
recordingsObject = this
|
||||||
|
|
||||||
|
# configure the datatable for recordings
|
||||||
|
this.table = $('#recordings').dataTable({
|
||||||
|
data: [],
|
||||||
|
rowId: 'id',
|
||||||
|
paging: false,
|
||||||
|
dom: 't',
|
||||||
|
info: false,
|
||||||
|
order: [[ COLUMN.DATE, "desc" ]],
|
||||||
|
language: {
|
||||||
|
emptyTable: '<h3>'+I18n.no_recordings_yet+'</h3>',
|
||||||
|
zeroRecords: '<h3>'+I18n.no_recordings+'</h3>'
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{ data: "start_time" },
|
||||||
|
{ data: "name", visible: $(".page-wrapper.rooms").data('main-room') },
|
||||||
|
{ data: "previews", orderable: false },
|
||||||
|
{ data: "duration" },
|
||||||
|
{ data: "participants" },
|
||||||
|
{ data: "playbacks", orderable: false },
|
||||||
|
{ data: "published" },
|
||||||
|
{ data: "listed", visible: false },
|
||||||
|
{ data: "id", orderable: false }
|
||||||
|
],
|
||||||
|
columnDefs: [
|
||||||
|
{
|
||||||
|
targets: COLUMN.DATE,
|
||||||
|
render: (data, type, row) ->
|
||||||
|
if type == 'display'
|
||||||
|
date = new Date(data)
|
||||||
|
title = date
|
||||||
|
.toLocaleString($('html').attr('lang'),
|
||||||
|
{month: 'long', day: 'numeric', year: 'numeric',
|
||||||
|
hour12: 'true', hour: '2-digit', minute: '2-digit'})
|
||||||
|
timeago = '<time datetime="'+date.toISOString()+'" data-time-ago="'+date.toISOString()+'">'+date.toISOString()+'</time>'
|
||||||
|
return title+'<span class="timeago">('+timeago+')</span>'
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targets: COLUMN.PREVIEW,
|
||||||
|
render: (data, type, row) ->
|
||||||
|
if type == 'display'
|
||||||
|
str = ''
|
||||||
|
if row.published
|
||||||
|
for d in data
|
||||||
|
str += '<img height="50" width="50" class="img-thumbnail" src="'+d.url+'" alt="'+d.alt+'"></img><img class="img-thumbnail large" src="'+d.url+'"></img>'
|
||||||
|
return str
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targets: COLUMN.PLAYBACK,
|
||||||
|
render: (data, type, row) ->
|
||||||
|
if type == 'display'
|
||||||
|
str = ''
|
||||||
|
if row.published
|
||||||
|
if data.length == 1
|
||||||
|
str = '<a class="btn btn-default play-tooltip" href="'+data[0].url+'" target="_blank"><i class="fa fa-play-circle"></i></a>'
|
||||||
|
else
|
||||||
|
for d in data
|
||||||
|
str += '<a href="'+d.url+'" target="_blank">'+d.type_i18n+'</a> '
|
||||||
|
return str
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targets: COLUMN.VISIBILITY,
|
||||||
|
render: (data, type, row) ->
|
||||||
|
visibility = ['unpublished', 'unlisted', 'published']
|
||||||
|
if row.published
|
||||||
|
if row.listed
|
||||||
|
state = visibility[2]
|
||||||
|
else
|
||||||
|
state = visibility[1]
|
||||||
|
else
|
||||||
|
state = visibility[0]
|
||||||
|
if type == 'display'
|
||||||
|
return I18n[state]
|
||||||
|
return state
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targets: COLUMN.ACTION,
|
||||||
|
render: (data, type, row) ->
|
||||||
|
if type == 'display'
|
||||||
|
roomName = Meeting.getInstance().getMeetingId()
|
||||||
|
recordingActions = $('.hidden-elements').find('.recording-actions')
|
||||||
|
classes = ['recording-unpublished', 'recording-unlisted', 'recording-published']
|
||||||
|
if row.published
|
||||||
|
if row.listed
|
||||||
|
cls = classes[2]
|
||||||
|
else
|
||||||
|
cls = classes[1]
|
||||||
|
else
|
||||||
|
cls = classes[0]
|
||||||
|
trigger = recordingActions.find('.recording-update-trigger')
|
||||||
|
trigger.removeClass(classes.join(' '))
|
||||||
|
trigger.addClass(cls)
|
||||||
|
|
||||||
|
return recordingActions.html()
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
options = {
|
||||||
|
selector: '.delete-tooltip',
|
||||||
|
placement: 'bottom',
|
||||||
|
title: I18n.delete_recording
|
||||||
|
};
|
||||||
|
$('#recordings').tooltip(options)
|
||||||
|
|
||||||
|
options.selector = '.visibility-tooltip'
|
||||||
|
options.title = I18n.change_visibility
|
||||||
|
$('#recordings').tooltip(options)
|
||||||
|
|
||||||
|
options.selector = '.play-tooltip'
|
||||||
|
options.title = I18n.play_recording
|
||||||
|
$('#recordings').tooltip(options)
|
||||||
|
|
||||||
|
options.selector = '.youtube-tooltip'
|
||||||
|
options.title = I18n.upload_youtube
|
||||||
|
$('#recordings').tooltip(options)
|
||||||
|
|
||||||
|
options.selector = '.upload-tooltip'
|
||||||
|
options.title = I18n.share
|
||||||
|
$('#recordings').tooltip(options)
|
||||||
|
|
||||||
|
options.selector = '.mail-tooltip'
|
||||||
|
options.title = I18n.mail_recording
|
||||||
|
$('#recordings').tooltip(options)
|
||||||
|
|
||||||
|
$(document).one "turbolinks:before-cache", =>
|
||||||
|
@getTable().api().clear().draw().destroy()
|
||||||
|
|
||||||
|
# enable popovers
|
||||||
|
# can't use trigger:'focus' because it doesn't work will with buttons inside
|
||||||
|
# the popover
|
||||||
|
options = {
|
||||||
|
selector: '.has-popover',
|
||||||
|
html: true,
|
||||||
|
trigger: 'click',
|
||||||
|
title: ->
|
||||||
|
return $(this).data("popover-title");
|
||||||
|
content: ->
|
||||||
|
bodySelector = $(this).data("popover-body")
|
||||||
|
return $(bodySelector).html()
|
||||||
|
}
|
||||||
|
$('#recordings').popover(options)
|
||||||
|
|
||||||
|
# close popovers manually when clicking outside of them or in buttons
|
||||||
|
# with [data-dismiss="popover"]
|
||||||
|
# careful to hide only the open popover and not all of them, otherwise they won't reopen
|
||||||
|
$('body').on 'click', (e) ->
|
||||||
|
$('.has-popover').each ->
|
||||||
|
if !$(this).is(e.target) and $(this).has(e.target).length == 0 and $('.popover.in').has(e.target).length == 0
|
||||||
|
if $(this).next(".popover.in").length > 0
|
||||||
|
$(this).popover('hide')
|
||||||
|
$(document).on 'click', '[data-dismiss="popover"]', (e) ->
|
||||||
|
$('.has-popover').each ->
|
||||||
|
if $(this).next(".popover.in").length > 0
|
||||||
|
$(this).popover('hide')
|
||||||
|
|
||||||
|
# Gets the current instance or creates a new one
|
||||||
|
@getInstance: ->
|
||||||
|
if _recordingsInstance && Recordings.initialized()
|
||||||
|
return _recordingsInstance
|
||||||
|
_recordingsInstance = new Recordings()
|
||||||
|
return _recordingsInstance
|
||||||
|
|
||||||
|
@initialize: ->
|
||||||
|
Recordings.getInstance()
|
||||||
|
|
||||||
|
@initialized: ->
|
||||||
|
return $.fn.DataTable.isDataTable('#recordings') && _recordingsInstance
|
||||||
|
|
||||||
|
draw: ->
|
||||||
|
if !@isOwner()
|
||||||
|
@table.api().columns(COLUMN.LISTED).search('true')
|
||||||
|
@table.api().columns.adjust().draw()
|
||||||
|
|
||||||
|
# refresh the recordings from the server
|
||||||
|
refresh: ->
|
||||||
|
table_api = this.table.api()
|
||||||
|
$.get @getRecordingsURL(), (data) =>
|
||||||
|
@setOwner(data.is_owner)
|
||||||
|
if !@owner
|
||||||
|
table_api.column(COLUMN.ACTION).visible(false)
|
||||||
|
table_api.column(COLUMN.VISIBILITY).visible(false)
|
||||||
|
for recording in data.recordings
|
||||||
|
# NOTE: Temporary fix for the minutes to milliseconds bug some recordings may have
|
||||||
|
# experienced when transitioning from BigBlueButton 1.1 to BigBlueButton 2.0.
|
||||||
|
recording.duration = if recording.duration < 1000 then recording.duration else parseInt(recording.length / 60000)
|
||||||
|
data.recordings.sort (a,b) ->
|
||||||
|
return new Date(b.start_time) - new Date(a.start_time)
|
||||||
|
table_api.clear()
|
||||||
|
table_api.rows.add(data.recordings)
|
||||||
|
@draw()
|
||||||
|
|
||||||
|
if $(".page-wrapper.rooms").data('main-room')
|
||||||
|
recording_names = data.recordings.map (r) ->
|
||||||
|
return r.name
|
||||||
|
output = {}
|
||||||
|
for key in [0...recording_names.length]
|
||||||
|
output[recording_names[key]] = recording_names[key]
|
||||||
|
PreviousMeetings.uniqueAdd(value for key, value of output)
|
||||||
|
|
||||||
|
|
||||||
|
# setup click handlers for the action buttons
|
||||||
|
setupActionHandlers: ->
|
||||||
|
table_api = this.table.api()
|
||||||
|
recordingsObject = this
|
||||||
|
selectedUpload = null
|
||||||
|
canUpload = false
|
||||||
|
|
||||||
|
@getTable().on 'click', '.recording-update', (event) ->
|
||||||
|
btn = $(this)
|
||||||
|
row = table_api.row($(this).closest('tr')).data()
|
||||||
|
url = recordingsObject.getRecordingsURL()
|
||||||
|
id = row.id
|
||||||
|
|
||||||
|
published = btn.data('visibility') == "unlisted" ||
|
||||||
|
btn.data('visibility') == "published"
|
||||||
|
listed = btn.data('visibility') == "published"
|
||||||
|
|
||||||
|
btn.prop('disabled', true)
|
||||||
|
|
||||||
|
data = { published: published.toString() }
|
||||||
|
data["meta_" + GreenLight.META_LISTED] = listed.toString();
|
||||||
|
$.ajax({
|
||||||
|
method: 'PATCH',
|
||||||
|
url: url+'/'+id,
|
||||||
|
data: data
|
||||||
|
}).done((data) ->
|
||||||
|
btn.prop('disabled', false)
|
||||||
|
).fail((xhr, text) ->
|
||||||
|
btn.prop('disabled', false)
|
||||||
|
)
|
||||||
|
|
||||||
|
@getTable().on 'click', '.recording-delete', (event) ->
|
||||||
|
btn = $(this)
|
||||||
|
row = table_api.row($(this).closest('tr'))
|
||||||
|
url = recordingsObject.getRecordingsURL()
|
||||||
|
id = row.data().id
|
||||||
|
btn.prop('disabled', true)
|
||||||
|
$.ajax({
|
||||||
|
method: 'DELETE',
|
||||||
|
url: url+'/'+id
|
||||||
|
}).done((data) ->
|
||||||
|
btn.prop('disabled', false)
|
||||||
|
).fail((xhr, text) ->
|
||||||
|
btn.prop('disabled', false)
|
||||||
|
if xhr.status == 404
|
||||||
|
row.remove();
|
||||||
|
recordingsObject.draw()
|
||||||
|
showAlert(I18n.recording_deleted, 4000);
|
||||||
|
)
|
||||||
|
|
||||||
|
@getTable().on 'click', '.upload-button', (event) ->
|
||||||
|
btn = $(this)
|
||||||
|
row = table_api.row($(this).closest('tr')).data()
|
||||||
|
url = recordingsObject.getRecordingsURL()
|
||||||
|
id = row.id
|
||||||
|
|
||||||
|
title = $('#video-title').val()
|
||||||
|
privacy_status = $('input[name=privacy_status]:checked').val()
|
||||||
|
|
||||||
|
if title == ''
|
||||||
|
title = row.name
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
method: 'POST',
|
||||||
|
url: url+'/'+id
|
||||||
|
data: {video_title: title, privacy_status: privacy_status}
|
||||||
|
success: (data) ->
|
||||||
|
|
||||||
|
if data['url'] != null
|
||||||
|
window.location.href = data['url']
|
||||||
|
else
|
||||||
|
cloud = selectedUpload.find('.cloud-blue')
|
||||||
|
check = selectedUpload.find('.green-check')
|
||||||
|
spinner = selectedUpload.find('.load-spinner')
|
||||||
|
|
||||||
|
showAlert(I18n.successful_upload, 4000);
|
||||||
|
|
||||||
|
spinner.hide()
|
||||||
|
check.show()
|
||||||
|
|
||||||
|
setTimeout ( ->
|
||||||
|
cloud.show()
|
||||||
|
check.hide()
|
||||||
|
), 2500
|
||||||
|
})
|
||||||
|
|
||||||
|
selectedUpload.find('.cloud-blue').hide()
|
||||||
|
selectedUpload.find('.load-spinner').show()
|
||||||
|
|
||||||
|
@getTable().on 'click', '.mail-recording', (event) ->
|
||||||
|
btn = $(this)
|
||||||
|
row = table_api.row($(this).closest('tr')).data()
|
||||||
|
url = recordingsObject.getRecordingsURL()
|
||||||
|
id = row.id
|
||||||
|
|
||||||
|
# Take the username from the header.
|
||||||
|
username = $('#title-header').text().replace('Welcome ', '').trim()
|
||||||
|
|
||||||
|
recording_url = row.playbacks[0].url
|
||||||
|
webcams_url = getHostName(recording_url) + '/presentation/' + id + '/video/webcams.webm'
|
||||||
|
subject = username + I18n.recording_mail_subject
|
||||||
|
body = I18n.recording_mail_body + "\n\n" + recording_url + "\n\n" + I18n.email_footer_1 + "\n" + I18n.email_footer_2
|
||||||
|
|
||||||
|
mailto = "mailto:?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body);
|
||||||
|
window.open(mailto);
|
||||||
|
|
||||||
|
@getTable().on 'click', '.youtube-upload', (event) ->
|
||||||
|
row = table_api.row($(this).closest('tr')).data()
|
||||||
|
$('#video-title').attr('value', row.name)
|
||||||
|
|
||||||
|
@getTable().on 'click', '.cloud-upload', (event) ->
|
||||||
|
btn = $(this)
|
||||||
|
row = table_api.row($(this).closest('tr')).data()
|
||||||
|
id = row.id
|
||||||
|
|
||||||
|
selectedUpload = $(this)
|
||||||
|
|
||||||
|
# Determine if the recording can be uploaded to Youtube.
|
||||||
|
$.ajax({
|
||||||
|
method: 'POST',
|
||||||
|
data: {'rec_id': id},
|
||||||
|
async: false,
|
||||||
|
url: recordingsObject.getRecordingsURL() + '/can_upload'
|
||||||
|
}).success((res_data) ->
|
||||||
|
canUpload = res_data['uploadable']
|
||||||
|
)
|
||||||
|
|
||||||
|
youtube_button = $('.share-popover').find('.youtube-upload')
|
||||||
|
|
||||||
|
attr = $(this).attr('data-popover-body');
|
||||||
|
|
||||||
|
# Check if the cloud button has a popover body.
|
||||||
|
if (typeof attr == typeof undefined || attr == false)
|
||||||
|
switch canUpload
|
||||||
|
# We can upload the recording.
|
||||||
|
when 'true'
|
||||||
|
youtube_button.attr('title', I18n.upload_youtube)
|
||||||
|
youtube_button.removeClass('disabled-button')
|
||||||
|
youtube_button.addClass('has-popover')
|
||||||
|
youtube_button.show()
|
||||||
|
# Can't upload because uploading is disabled.
|
||||||
|
when 'uploading_disabled'
|
||||||
|
youtube_button.hide()
|
||||||
|
# Can't upload because account is not authenticated with Google.
|
||||||
|
when 'invalid_provider'
|
||||||
|
youtube_button.attr('title', I18n.invalid_provider)
|
||||||
|
youtube_button.addClass('disabled-button')
|
||||||
|
youtube_button.removeClass('has-popover')
|
||||||
|
youtube_button.show()
|
||||||
|
# Can't upload because recording does not contain video.
|
||||||
|
else
|
||||||
|
youtube_button.attr('title', I18n.no_video)
|
||||||
|
youtube_button.addClass('disabled-button')
|
||||||
|
youtube_button.removeClass('has-popover')
|
||||||
|
youtube_button.show()
|
||||||
|
|
||||||
|
$(this).attr('data-popover-body', '.share-popover')
|
||||||
|
$(this).popover('show')
|
||||||
|
else
|
||||||
|
$(this).popover('hide')
|
||||||
|
$(this).removeAttr('data-popover-body')
|
||||||
|
|
||||||
|
|
||||||
|
@getTable().on 'draw.dt', (event) ->
|
||||||
|
$('time[data-time-ago]').timeago();
|
||||||
|
|
||||||
|
getTable: ->
|
||||||
|
@table
|
||||||
|
|
||||||
|
getHostName = (url) ->
|
||||||
|
parser = document.createElement('a');
|
||||||
|
parser.href = url;
|
||||||
|
parser.origin;
|
||||||
|
|
||||||
|
getRecordingsURL: ->
|
||||||
|
if $(".page-wrapper.rooms").data('main-room')
|
||||||
|
base_url = Meeting.buildRootURL()+'/'+$('body').data('resource')+'/'+Meeting.getInstance().getAdminId()
|
||||||
|
else
|
||||||
|
base_url = $('.meeting-url').val()
|
||||||
|
base_url+'/recordings'
|
||||||
|
|
||||||
|
isOwner: ->
|
||||||
|
@owner
|
||||||
|
|
||||||
|
setOwner: (owner) ->
|
||||||
|
@owner = owner
|
|
@ -1,162 +0,0 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
$(document).on('turbolinks:load', function(){
|
|
||||||
var controller = $("body").data('controller');
|
|
||||||
var action = $("body").data('action');
|
|
||||||
|
|
||||||
if(controller == "rooms" && action == "show"
|
|
||||||
|| controller == "rooms" && action == "update"
|
|
||||||
|| controller == "users" && action == "recordings"
|
|
||||||
|| controller == "admins" && action == "server_recordings"){
|
|
||||||
|
|
||||||
// Set a room header rename event
|
|
||||||
var configure_room_header = function(room_title){
|
|
||||||
|
|
||||||
function register_room_title_event(e){
|
|
||||||
// Remove current window events
|
|
||||||
$(window).off('mousedown keydown');
|
|
||||||
|
|
||||||
if(e.type == 'focusout'){
|
|
||||||
submit_rename_request(room_title);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
room_title.addClass("dotted_underline");
|
|
||||||
room_title.find('#user-text').fadeTo('medium', 0.7);
|
|
||||||
room_title.find('#user-text').attr("contenteditable", true);
|
|
||||||
room_title.find('#user-text').focus();
|
|
||||||
|
|
||||||
// Stop automatic refresh
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
register_window_event(room_title, 'user-text', '#edit-room', 'edit-room');
|
|
||||||
}
|
|
||||||
|
|
||||||
room_title.find('#user-text').on('dblclick focusout', function(e){
|
|
||||||
if(room_title.find('#edit-room').length){
|
|
||||||
register_room_title_event(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
room_title.find('.fa-edit').on('click', function(e){
|
|
||||||
register_room_title_event(e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set a recording row rename event
|
|
||||||
var configure_recording_row = function(recording_title){
|
|
||||||
|
|
||||||
function register_recording_title_event(e){
|
|
||||||
// Remove current window events
|
|
||||||
$(window).off('mousedown keydown');
|
|
||||||
|
|
||||||
if(e.type == 'focusout'){
|
|
||||||
submit_rename_request(recording_title);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
recording_title.addClass("dotted_underline");
|
|
||||||
recording_title.fadeTo('medium', 0.7);
|
|
||||||
recording_title.find('span').attr("contenteditable", true);
|
|
||||||
recording_title.find('span').focus();
|
|
||||||
|
|
||||||
// Stop automatic refresh
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
register_window_event(recording_title, 'recording-text', '#edit-record', 'edit-recordid');
|
|
||||||
}
|
|
||||||
|
|
||||||
recording_title.find('a').on('click focusout', function(e){
|
|
||||||
register_recording_title_event(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
recording_title.find('#recording-text').on('dblclick focusout', function(e){
|
|
||||||
register_recording_title_event(e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register window event to submit new name
|
|
||||||
// upon click or upon pressing the enter key
|
|
||||||
var register_window_event = function(element, textfield_id, edit_button_id, edit_button_data){
|
|
||||||
$(window).on('mousedown keydown', function(clickEvent){
|
|
||||||
|
|
||||||
// Return if the text is clicked
|
|
||||||
if(clickEvent.type == "mousedown" && clickEvent.target.id == textfield_id){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return if the edit icon is clicked
|
|
||||||
if(clickEvent.type == "mousedown" && $(clickEvent.target).is(edit_button_id) &&
|
|
||||||
$(clickEvent.target).data(edit_button_data) === element.find(edit_button_id).data(edit_button_data)){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if event is keydown and enter key is not pressed
|
|
||||||
if(clickEvent.type == "keydown" && clickEvent.which !== 13){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
clickEvent.preventDefault();
|
|
||||||
submit_rename_request(element);
|
|
||||||
|
|
||||||
// Remove window event when ajax call to update name is submitted
|
|
||||||
$(window).off('mousedown keydown');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply ajax request depending on the element that triggered the event
|
|
||||||
var submit_rename_request = function(element){
|
|
||||||
if(element.is('#room-title')){
|
|
||||||
submit_update_request({
|
|
||||||
setting: "rename_header",
|
|
||||||
name: element.find('#user-text').text(),
|
|
||||||
}, element.data('path'), "POST");
|
|
||||||
}
|
|
||||||
else if(element.is('#recording-title')){
|
|
||||||
submit_update_request({
|
|
||||||
setting: "rename_recording",
|
|
||||||
record_id: element.data('recordid'),
|
|
||||||
record_name: element.find('span').text(),
|
|
||||||
room_uid: element.data('room-uid'),
|
|
||||||
}, element.data('path'), "PATCH");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper for submitting ajax requests
|
|
||||||
var submit_update_request = function(data, path, action){
|
|
||||||
// Send ajax request for update
|
|
||||||
$.ajax({
|
|
||||||
url: path,
|
|
||||||
type: action,
|
|
||||||
data: data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elements that can be renamed
|
|
||||||
var room_title = $('#room-title');
|
|
||||||
var recording_rows = $('#recording-table').find('tr');
|
|
||||||
|
|
||||||
// Configure renaming for room header
|
|
||||||
configure_room_header(room_title);
|
|
||||||
|
|
||||||
// Configure renaming for recording rows
|
|
||||||
recording_rows.each(function(){
|
|
||||||
var recording_title = $(this).find('#recording-title');
|
|
||||||
configure_recording_row(recording_title);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,502 +0,0 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// Room specific js for copy button and email link.
|
|
||||||
$(document).on('turbolinks:load', function(){
|
|
||||||
var controller = $("body").data('controller');
|
|
||||||
var action = $("body").data('action');
|
|
||||||
|
|
||||||
// highlight current room
|
|
||||||
$('.room-block').removeClass('current');
|
|
||||||
$('a[href="' + window.location.pathname + '"] .room-block').addClass('current');
|
|
||||||
|
|
||||||
// Only run on room pages.
|
|
||||||
if (controller == "rooms" && action == "show"){
|
|
||||||
// Display and update all fields related to creating a room in the createRoomModal
|
|
||||||
$("#create-room-block").click(function(){
|
|
||||||
showCreateRoom(this)
|
|
||||||
})
|
|
||||||
|
|
||||||
checkIfAutoJoin()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Autofocus on the Room Name label when creating a room only
|
|
||||||
$('#createRoomModal').on('shown.bs.modal', function (){
|
|
||||||
if ($(".create-only").css("display") == "block"){
|
|
||||||
$('#create-room-name').focus()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (controller == "rooms" && action == "show" || controller == "admins" && action == "server_rooms"){
|
|
||||||
// Display and update all fields related to creating a room in the createRoomModal
|
|
||||||
$(".update-room").click(function(){
|
|
||||||
showUpdateRoom(this)
|
|
||||||
})
|
|
||||||
|
|
||||||
// share room pop up accessibility
|
|
||||||
manageAccessAccessibility();
|
|
||||||
|
|
||||||
$(".delete-room").click(function() {
|
|
||||||
showDeleteRoom(this)
|
|
||||||
})
|
|
||||||
|
|
||||||
// For keyboard users to be able to generate access code
|
|
||||||
generateAccessCodeAccessibility()
|
|
||||||
|
|
||||||
$('.selectpicker').selectpicker({
|
|
||||||
liveSearchPlaceholder: getLocalizedString('javascript.search.start')
|
|
||||||
});
|
|
||||||
// Fixes turbolinks issue with bootstrap select
|
|
||||||
$(window).trigger('load.bs.select.data-api');
|
|
||||||
|
|
||||||
$(".share-room").click(function() {
|
|
||||||
// Update the path of save button
|
|
||||||
$("#save-access").attr("data-path", $(this).data("path"))
|
|
||||||
$("#room-owner-uid").val($(this).data("owner"))
|
|
||||||
|
|
||||||
// Get list of users shared with and display them
|
|
||||||
displaySharedUsers($(this).data("users-path"))
|
|
||||||
})
|
|
||||||
|
|
||||||
$("#shareRoomModal").on("show.bs.modal", function() {
|
|
||||||
$(".selectpicker").selectpicker('val','')
|
|
||||||
})
|
|
||||||
|
|
||||||
$(".bootstrap-select").on("click", function() {
|
|
||||||
$(".bs-searchbox").siblings().hide()
|
|
||||||
})
|
|
||||||
|
|
||||||
$("#share-room-select ~ button").on("click", function() {
|
|
||||||
$(".bs-searchbox").siblings().hide()
|
|
||||||
})
|
|
||||||
|
|
||||||
$(".bs-searchbox input").on("input", function() {
|
|
||||||
if ($(".bs-searchbox input").val() == '' || $(".bs-searchbox input").val().length < 3) {
|
|
||||||
$(".select-options").remove()
|
|
||||||
$(".bs-searchbox").siblings().hide()
|
|
||||||
} else {
|
|
||||||
// Manually populate the dropdown
|
|
||||||
$.get($("#share-room-select").data("path"), { search: $(".bs-searchbox input").val(), owner_uid: $("#room-owner-uid").val() }, function(users) {
|
|
||||||
$(".select-options").remove()
|
|
||||||
if (users.length > 0) {
|
|
||||||
users.forEach(function(user) {
|
|
||||||
let opt = document.createElement("option")
|
|
||||||
$(opt).val(user.uid)
|
|
||||||
$(opt).text(user.name)
|
|
||||||
$(opt).addClass("select-options")
|
|
||||||
$(opt).attr("data-subtext", user.uid)
|
|
||||||
$("#share-room-select").append(opt)
|
|
||||||
})
|
|
||||||
// Only refresh the select dropdown if there are results to show
|
|
||||||
$('#share-room-select').selectpicker('refresh');
|
|
||||||
}
|
|
||||||
$(".bs-searchbox").siblings().show()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$(".remove-share-room").click(function() {
|
|
||||||
$("#remove-shared-confirm").parent().attr("action", $(this).data("path"))
|
|
||||||
})
|
|
||||||
|
|
||||||
// User selects an option from the Room Access dropdown
|
|
||||||
$(".bootstrap-select").on("changed.bs.select", function(){
|
|
||||||
// Get the uid of the selected user
|
|
||||||
let uid = $(".selectpicker").selectpicker('val')
|
|
||||||
|
|
||||||
// If the value was changed to blank, ignore it
|
|
||||||
if (uid == "") return
|
|
||||||
|
|
||||||
let currentListItems = $("#user-list li").toArray().map(user => $(user).data("uid"))
|
|
||||||
|
|
||||||
// Check to make sure that the user is not already there
|
|
||||||
if (!currentListItems.includes(uid)) {
|
|
||||||
// Create the faded list item and display it
|
|
||||||
let option = $("option[value='" + uid + "']")
|
|
||||||
|
|
||||||
let listItem = document.createElement("li")
|
|
||||||
listItem.setAttribute('class', 'list-group-item text-left not-saved add-access');
|
|
||||||
listItem.setAttribute("data-uid", uid)
|
|
||||||
|
|
||||||
let spanItemAvatar = document.createElement("span"),
|
|
||||||
spanItemName = document.createElement("span"),
|
|
||||||
spanItemUser = document.createElement("span");
|
|
||||||
spanItemAvatar.setAttribute('class', 'avatar float-left mr-2');
|
|
||||||
spanItemAvatar.innerText = option.text().charAt(0);
|
|
||||||
spanItemName.setAttribute('class', 'shared-user');
|
|
||||||
spanItemName.innerText = option.text();
|
|
||||||
spanItemUser.setAttribute('class', 'text-muted');
|
|
||||||
spanItemUser.innerText = option.data('subtext');
|
|
||||||
spanItemName.append(spanItemUser);
|
|
||||||
|
|
||||||
listItem.innerHTML = "<span class='text-primary float-right shared-user cursor-pointer' onclick='removeSharedUser(this)'><i class='fas fa-times'></i></span>"
|
|
||||||
listItem.prepend(spanItemName);
|
|
||||||
listItem.prepend(spanItemAvatar);
|
|
||||||
|
|
||||||
$("#user-list").append(listItem)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$("#presentation-upload").change(function(data) {
|
|
||||||
var file = data.target.files[0]
|
|
||||||
|
|
||||||
// Check file type and size to make sure they aren't over the limit
|
|
||||||
if (validFileUpload(file)) {
|
|
||||||
$("#presentation-upload-label").text(file.name)
|
|
||||||
} else {
|
|
||||||
$("#invalid-file-type").show()
|
|
||||||
$("#presentation-upload").val("")
|
|
||||||
$("#presentation-upload-label").text($("#presentation-upload-label").data("placeholder"))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$(".preupload-room").click(function() {
|
|
||||||
updatePreuploadPresentationModal(this)
|
|
||||||
})
|
|
||||||
|
|
||||||
$("#remove-presentation").click(function(data) {
|
|
||||||
removePreuploadPresentation($(this).data("remove"))
|
|
||||||
})
|
|
||||||
|
|
||||||
// trigger initial room filter
|
|
||||||
filterRooms();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function copyInvite() {
|
|
||||||
$('#invite-url').select()
|
|
||||||
if (document.execCommand("copy")) {
|
|
||||||
$('#invite-url').blur();
|
|
||||||
copy = $("#copy-invite")
|
|
||||||
copy.addClass('btn-success');
|
|
||||||
copy.html("<i class='fas fa-check mr-1'></i>" + getLocalizedString("copied"))
|
|
||||||
setTimeout(function(){
|
|
||||||
copy.removeClass('btn-success');
|
|
||||||
copy.html("<i class='fas fa-copy mr-1'></i>" + getLocalizedString("copy"))
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyAccess(target) {
|
|
||||||
input = target ? $("#copy-" + target + "-code") : $("#copy-code")
|
|
||||||
input.attr("type", "text")
|
|
||||||
input.select()
|
|
||||||
if (document.execCommand("copy")) {
|
|
||||||
input.attr("type", "hidden")
|
|
||||||
copy = target ? $("#copy-" + target + "-access") : $("#copy-access")
|
|
||||||
copy.addClass('btn-success');
|
|
||||||
copy.html("<i class='fas fa-check mr-1'></i>" + getLocalizedString("copied"))
|
|
||||||
setTimeout(function(){
|
|
||||||
copy.removeClass('btn-success');
|
|
||||||
originalString = target ? getLocalizedString("room.copy_" + target + "_access") : getLocalizedString("room.copy_access")
|
|
||||||
copy.html("<i class='fas fa-copy mr-1'></i>" + originalString)
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showCreateRoom(target) {
|
|
||||||
$("#create-room-name").val("")
|
|
||||||
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code_placeholder"))
|
|
||||||
$("#create-room-moderator-access-code").text(getLocalizedString("modal.create_room.moderator_access_code_placeholder"))
|
|
||||||
$("#room_access_code").val(null)
|
|
||||||
$("#room_moderator_access_code").val(null)
|
|
||||||
|
|
||||||
$("#createRoomModal form").attr("action", $("body").data('relative-root'))
|
|
||||||
$("#room_mute_on_join").prop("checked", $("#room_mute_on_join").data("default"))
|
|
||||||
$("#room_require_moderator_approval").prop("checked", $("#room_require_moderator_approval").data("default"))
|
|
||||||
$("#room_anyone_can_start").prop("checked", $("#room_anyone_can_start").data("default"))
|
|
||||||
$("#room_all_join_moderator").prop("checked", $("#room_all_join_moderator").data("default"))
|
|
||||||
$("#room_recording").prop("checked", $("#room_recording").data("default"))
|
|
||||||
|
|
||||||
//show all elements & their children with a create-only class
|
|
||||||
$(".create-only").each(function() {
|
|
||||||
$(this).show()
|
|
||||||
if($(this).children().length > 0) { $(this).children().show() }
|
|
||||||
})
|
|
||||||
|
|
||||||
//hide all elements & their children with a update-only class
|
|
||||||
$(".update-only").each(function() {
|
|
||||||
$(this).attr('style',"display:none !important")
|
|
||||||
if($(this).children().length > 0) { $(this).children().attr('style',"display:none !important") }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function showUpdateRoom(target) {
|
|
||||||
var modal = $(target)
|
|
||||||
var update_path = modal.closest(".room-block").data("path")
|
|
||||||
var settings_path = modal.data("settings-path")
|
|
||||||
$("#create-room-name").val(modal.closest(".room-block").find(".room-name-text").text().trim())
|
|
||||||
$("#createRoomModal form").attr("action", update_path)
|
|
||||||
|
|
||||||
//show all elements & their children with a update-only class
|
|
||||||
$(".update-only").each(function() {
|
|
||||||
$(this).show()
|
|
||||||
if($(this).children().length > 0) { $(this).children().show() }
|
|
||||||
})
|
|
||||||
|
|
||||||
//hide all elements & their children with a create-only class
|
|
||||||
$(".create-only").each(function() {
|
|
||||||
$(this).attr('style',"display:none !important")
|
|
||||||
if($(this).children().length > 0) { $(this).children().attr('style',"display:none !important") }
|
|
||||||
})
|
|
||||||
|
|
||||||
updateCurrentSettings(settings_path)
|
|
||||||
|
|
||||||
var accessCode = modal.closest(".room-block").data("room-access-code")
|
|
||||||
|
|
||||||
if(accessCode){
|
|
||||||
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code") + ": " + accessCode)
|
|
||||||
$("#room_access_code").val(accessCode)
|
|
||||||
} else {
|
|
||||||
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code_placeholder"))
|
|
||||||
$("#room_access_code").val(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
var moderatorAccessCode = modal.closest(".room-block").data("room-moderator-access-code")
|
|
||||||
|
|
||||||
if(moderatorAccessCode){
|
|
||||||
$("#create-room-moderator-access-code").text(getLocalizedString("modal.create_room.moderator_access_code") + ": " + moderatorAccessCode)
|
|
||||||
$("#room_moderator_access_code").val(moderatorAccessCode)
|
|
||||||
} else {
|
|
||||||
$("#create-room-moderator-access-code").text(getLocalizedString("modal.create_room.moderator_access_code_placeholder"))
|
|
||||||
$("#room_moderator_access_code").val(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showDeleteRoom(target) {
|
|
||||||
$("#delete-header").text(getLocalizedString("modal.delete_room.confirm").replace("%{room}", $(target).data("name")))
|
|
||||||
$("#delete-confirm").parent().attr("action", $(target).data("path"))
|
|
||||||
}
|
|
||||||
|
|
||||||
//Update the createRoomModal to show the correct current settings
|
|
||||||
function updateCurrentSettings(settings_path){
|
|
||||||
// Get current room settings and set checkbox
|
|
||||||
$.get(settings_path, function(settings) {
|
|
||||||
$("#room_mute_on_join").prop("checked", $("#room_mute_on_join").data("default") || settings.muteOnStart)
|
|
||||||
$("#room_require_moderator_approval").prop("checked", $("#room_require_moderator_approval").data("default") || settings.requireModeratorApproval)
|
|
||||||
$("#room_anyone_can_start").prop("checked", $("#room_anyone_can_start").data("default") || settings.anyoneCanStart)
|
|
||||||
$("#room_all_join_moderator").prop("checked", $("#room_all_join_moderator").data("default") || settings.joinModerator)
|
|
||||||
$("#room_recording").prop("checked", $("#room_recording").data("default") || Boolean(settings.recording))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateAccessCode(){
|
|
||||||
const accessCodeLength = 6
|
|
||||||
var validCharacters = "0123456789"
|
|
||||||
var accessCode = ""
|
|
||||||
|
|
||||||
for( var i = 0; i < accessCodeLength; i++){
|
|
||||||
accessCode += validCharacters.charAt(Math.floor(Math.random() * validCharacters.length));
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code") + ": " + accessCode)
|
|
||||||
$("#room_access_code").val(accessCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
function ResetAccessCode(){
|
|
||||||
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code_placeholder"))
|
|
||||||
$("#room_access_code").val(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateModeratorAccessCode(){
|
|
||||||
const accessCodeLength = 6
|
|
||||||
var validCharacters = "abcdefghijklmopqrstuvwxyz"
|
|
||||||
var accessCode = ""
|
|
||||||
|
|
||||||
for( var i = 0; i < accessCodeLength; i++){
|
|
||||||
accessCode += validCharacters.charAt(Math.floor(Math.random() * validCharacters.length));
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#create-room-moderator-access-code").text(getLocalizedString("modal.create_room.moderator_access_code") + ": " + accessCode)
|
|
||||||
$("#room_moderator_access_code").val(accessCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
function ResetModeratorAccessCode(){
|
|
||||||
$("#create-room-moderator-access-code").text(getLocalizedString("modal.create_room.moderator_access_code_placeholder"))
|
|
||||||
$("#room_moderator_access_code").val(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveAccessChanges() {
|
|
||||||
let listItemsToAdd = $("#user-list li:not(.remove-shared)").toArray().map(user => $(user).data("uid"))
|
|
||||||
|
|
||||||
$.post($("#save-access").data("path"), {add: listItemsToAdd})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get list of users shared with and display them
|
|
||||||
function displaySharedUsers(path) {
|
|
||||||
$.get(path, function(users) {
|
|
||||||
// Create list element and add to user list
|
|
||||||
var user_list_html = ""
|
|
||||||
|
|
||||||
users.forEach(function(user) {
|
|
||||||
user_list_html += "<li class='list-group-item text-left' data-uid='" + user.uid + "'>"
|
|
||||||
|
|
||||||
if (user.image) {
|
|
||||||
user_list_html += "<img id='user-image' class='avatar float-left mr-2' src='" + user.image + "'></img>"
|
|
||||||
} else {
|
|
||||||
user_list_html += "<span class='avatar float-left mr-2'>" + user.name.charAt(0) + "</span>"
|
|
||||||
}
|
|
||||||
user_list_html += "<span class='shared-user'>" + user.name + "<span class='text-muted ml-1'>" + user.uid + "</span></span>"
|
|
||||||
user_list_html += "<span class='text-primary float-right shared-user cursor-pointer' onclick='removeSharedUser(this)'><i class='fas fa-times'></i></span>"
|
|
||||||
user_list_html += "</li>"
|
|
||||||
})
|
|
||||||
|
|
||||||
$("#user-list").html(user_list_html)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removes the user from the list of shared users
|
|
||||||
function removeSharedUser(target) {
|
|
||||||
let parentLI = target.closest("li")
|
|
||||||
|
|
||||||
if (parentLI.classList.contains("not-saved")) {
|
|
||||||
parentLI.parentNode.removeChild(parentLI)
|
|
||||||
} else {
|
|
||||||
parentLI.removeChild(target)
|
|
||||||
parentLI.classList.add("remove-shared")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePreuploadPresentationModal(target) {
|
|
||||||
$.get($(target).data("settings-path"), function(presentation) {
|
|
||||||
if(presentation.attached) {
|
|
||||||
$("#current-presentation").show()
|
|
||||||
$("#presentation-name").text(presentation.name)
|
|
||||||
$("#change-pres").show()
|
|
||||||
$("#use-pres").hide()
|
|
||||||
} else {
|
|
||||||
$("#current-presentation").hide()
|
|
||||||
$("#change-pres").hide()
|
|
||||||
$("#use-pres").show()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#preuploadPresentationModal form").attr("action", $(target).data("path"))
|
|
||||||
$("#remove-presentation").data("remove", $(target).data("remove"))
|
|
||||||
|
|
||||||
// Reset values to original to prevent confusion
|
|
||||||
$("#presentation-upload").val("")
|
|
||||||
$("#presentation-upload-label").text($("#presentation-upload-label").data("placeholder"))
|
|
||||||
$("#invalid-file-type").hide()
|
|
||||||
}
|
|
||||||
|
|
||||||
function removePreuploadPresentation(path) {
|
|
||||||
$.post(path, {})
|
|
||||||
}
|
|
||||||
|
|
||||||
function validFileUpload(file) {
|
|
||||||
return file.size/1024/1024 <= 30
|
|
||||||
}
|
|
||||||
|
|
||||||
// Automatically click the join button if this is an action cable reload
|
|
||||||
function checkIfAutoJoin() {
|
|
||||||
var url = new URL(window.location.href)
|
|
||||||
|
|
||||||
if (url.searchParams.get("reload") == "true") {
|
|
||||||
$("#joiner-consent").click()
|
|
||||||
$("#room-join").click()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterRooms() {
|
|
||||||
let search = $('#room-search').val()
|
|
||||||
|
|
||||||
if (search == undefined) { return }
|
|
||||||
|
|
||||||
let search_term = search.toLowerCase(),
|
|
||||||
rooms = $('#room_block_container > div:not(:last-child)');
|
|
||||||
clear_room_search = $('#clear-room-search');
|
|
||||||
|
|
||||||
if (search_term) {
|
|
||||||
clear_room_search.show();
|
|
||||||
} else {
|
|
||||||
clear_room_search.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
rooms.each(function(i, room) {
|
|
||||||
let text = $(this).find('h4').text();
|
|
||||||
room.style.display = (text.toLowerCase().indexOf(search_term) < 0) ? 'none' : 'block';
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearRoomSearch() {
|
|
||||||
$('#room-search').val('');
|
|
||||||
filterRooms()
|
|
||||||
}
|
|
||||||
|
|
||||||
function manageAccessAccessibility() {
|
|
||||||
// share room pop up accessibility
|
|
||||||
var holdModal = false;
|
|
||||||
$("#shareRoomModal").on("show.bs.modal", function() {
|
|
||||||
// for screen reader to be able to read results
|
|
||||||
$("#shareRoomModal .form-control").attr("aria-atomic", true);
|
|
||||||
$("#shareRoomModal .dropdown-menu div.inner").attr("role", "alert");
|
|
||||||
$("#shareRoomModal ul.dropdown-menu").attr("role", "listbox");
|
|
||||||
$("#shareRoomModal div.dropdown-menu").find("*").keyup(function(event) {
|
|
||||||
$("#shareRoomModal ul.dropdown-menu li").attr("aria-selected", false);
|
|
||||||
$("#shareRoomModal ul.dropdown-menu li.active").attr("aria-selected", true);
|
|
||||||
$("#shareRoomModal ul.dropdown-menu li.active a").attr("aria-selected", true);
|
|
||||||
});
|
|
||||||
// for keyboard support
|
|
||||||
// so that it can escape / close search user without closing the modal
|
|
||||||
$("#shareRoomModal div.dropdown-menu input").keydown(function(event) {
|
|
||||||
if (event.keyCode === 27) {
|
|
||||||
holdModal = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// reset escape button if the search is closed / done
|
|
||||||
$("#shareRoomModal").on("hide.bs.modal", function(e) {
|
|
||||||
if (holdModal) {
|
|
||||||
holdModal = false;
|
|
||||||
e.stopPropagation();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateAccessCodeAccessibility() {
|
|
||||||
// For keyboard users to be able to generate access code
|
|
||||||
$("#generate-room-access-code").keyup(function(event) {
|
|
||||||
if (event.keyCode === 13 || event.keyCode === 32) {
|
|
||||||
generateAccessCode();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// For keyboard users to be able to reset access code
|
|
||||||
$("#reset-access-code").keyup(function(event) {
|
|
||||||
if (event.keyCode === 13 || event.keyCode === 32) {
|
|
||||||
ResetAccessCode();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// For keyboard users to be able to generate access code
|
|
||||||
// for moderator
|
|
||||||
$("#generate-moderator-room-access-code").keyup(function(event) {
|
|
||||||
if (event.keyCode === 13 || event.keyCode === 32) {
|
|
||||||
generateModeratorAccessCode();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// For keyboard users to be able to reset access code
|
|
||||||
// for moderator
|
|
||||||
$("#reset-moderator-access-code").keyup(function(event) {
|
|
||||||
if (event.keyCode === 13 || event.keyCode === 32) {
|
|
||||||
ResetModeratorAccessCode();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
$(document).on('turbolinks:load', function(){
|
|
||||||
var controller = $("body").data('controller');
|
|
||||||
var action = $("body").data('action');
|
|
||||||
|
|
||||||
if ((controller == "admins" && action == "index") ||
|
|
||||||
(controller == "rooms" && action == "show") ||
|
|
||||||
(controller == "rooms" && action == "update") ||
|
|
||||||
(controller == "rooms" && action == "join") ||
|
|
||||||
(controller == "users" && action == "recordings") ||
|
|
||||||
(controller == "admins" && action == "server_recordings") ||
|
|
||||||
(controller == "admins" && action == "server_rooms")) {
|
|
||||||
// Submit search if the user hits enter
|
|
||||||
$("#search-input").keypress(function(key) {
|
|
||||||
if (key.which == 13) {
|
|
||||||
searchPage()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add listeners for sort
|
|
||||||
$("th[data-order]").click(function(data){
|
|
||||||
var header_elem = $(data.target)
|
|
||||||
|
|
||||||
if(header_elem.data('order') === 'asc'){ // asc
|
|
||||||
header_elem.data('order', 'desc');
|
|
||||||
}
|
|
||||||
else if(header_elem.data('order') === 'desc'){ // desc
|
|
||||||
header_elem.data('order', 'none');
|
|
||||||
}
|
|
||||||
else{ // none
|
|
||||||
header_elem.data('order', 'asc');
|
|
||||||
}
|
|
||||||
|
|
||||||
var search = $("#search-input").val();
|
|
||||||
|
|
||||||
var url = window.location.pathname + "?page=1&search=" + search + "&column=" + header_elem.data("header") +
|
|
||||||
"&direction=" + header_elem.data('order')
|
|
||||||
|
|
||||||
window.location.replace(addRecordingTable(url))
|
|
||||||
})
|
|
||||||
|
|
||||||
if(controller === "rooms" && action === "show"){
|
|
||||||
$(".page-item > a").each(function(){
|
|
||||||
if(!$(this).attr('href').endsWith("#")){
|
|
||||||
$(this).attr('href', $(this).attr('href') + "#recordings-table")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Searches the user table for the given string
|
|
||||||
function searchPage() {
|
|
||||||
var search = $("#search-input").val();
|
|
||||||
|
|
||||||
// Check if the user filtered by role
|
|
||||||
var role = new URL(location.href).searchParams.get('role')
|
|
||||||
var tab = new URL(location.href).searchParams.get('tab')
|
|
||||||
|
|
||||||
var url = window.location.pathname + "?page=1&search=" + search
|
|
||||||
|
|
||||||
if (role) { url += "&role=" + role }
|
|
||||||
if (tab) { url += "&tab=" + tab }
|
|
||||||
|
|
||||||
window.location.replace(addRecordingTable(url));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clears the search bar
|
|
||||||
function clearSearch() {
|
|
||||||
var role = new URL(location.href).searchParams.get('role')
|
|
||||||
var tab = new URL(location.href).searchParams.get('tab')
|
|
||||||
|
|
||||||
var url = window.location.pathname + "?page=1"
|
|
||||||
|
|
||||||
if (role) { url += "&role=" + role }
|
|
||||||
if (tab) { url += "&tab=" + tab }
|
|
||||||
|
|
||||||
window.location.replace(addRecordingTable(url));
|
|
||||||
|
|
||||||
var search_params = new URLSearchParams(window.location.search)
|
|
||||||
}
|
|
||||||
|
|
||||||
function addRecordingTable(url) {
|
|
||||||
if($("body").data('controller') === "rooms" && $("body").data('action') === "show") {
|
|
||||||
url += "#recordings-table"
|
|
||||||
}
|
|
||||||
return url
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// Handle changing of settings tabs.
|
|
||||||
$(document).on('turbolinks:load', function(){
|
|
||||||
var controller = $("body").data('controller');
|
|
||||||
var action = $("body").data('action');
|
|
||||||
|
|
||||||
// Only run on the settings page.
|
|
||||||
if ((controller == "users" && action == "edit") || (controller == "users" && action == "update")){
|
|
||||||
var settingsButtons = $('.setting-btn');
|
|
||||||
var settingsViews = $('.setting-view');
|
|
||||||
|
|
||||||
settingsButtons.each(function(i, btn) {
|
|
||||||
if(!$(btn).hasClass("active")){ $(settingsViews[i]).hide(); }
|
|
||||||
$(btn).click(function(){
|
|
||||||
$(btn).addClass("active");
|
|
||||||
settingsViews.each(function(i, view){
|
|
||||||
if($(view).attr("id") == $(btn).attr("id")){
|
|
||||||
$(view).show();
|
|
||||||
} else {
|
|
||||||
$(settingsButtons[i]).removeClass("active");
|
|
||||||
$(view).hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
$.ajaxSetup({
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var loopJoin = function() {
|
||||||
|
var jqxhr = Meeting.getInstance().getJoinMeetingResponse();
|
||||||
|
jqxhr.done(function(data) {
|
||||||
|
if (data.messageKey === 'wait_for_moderator') {
|
||||||
|
setTimeout(loopJoin, 5000);
|
||||||
|
} else {
|
||||||
|
$(location).attr("href", data.response.join_url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
jqxhr.fail(function(xhr, status, error) {
|
||||||
|
console.info("meeting join failed");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var alertTimeout = null;
|
||||||
|
var showAlert = function(html, timeout_delay, type) {
|
||||||
|
type = type || 'success'
|
||||||
|
|
||||||
|
if (!html) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.alert-template .alert-message').html(html);
|
||||||
|
$('.alert-template .alert').removeClass('alert-success alert-danger alert-info alert-warning').addClass('alert-'+type);
|
||||||
|
$('#alerts').html($('.alert-template').html());
|
||||||
|
|
||||||
|
if (timeout_delay) {
|
||||||
|
clearTimeout(alertTimeout);
|
||||||
|
alertTimeout = setTimeout(function() {
|
||||||
|
$('#alerts > .alert').alert('close');
|
||||||
|
}, timeout_delay);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// remove flash alerts after 5 seconds
|
||||||
|
var flashAlertDelayedDismiss = function() {
|
||||||
|
setTimeout(function(){
|
||||||
|
$('.flash-alerts').remove();
|
||||||
|
}, 5000);
|
||||||
|
};
|
||||||
|
|
||||||
|
var displayRoomURL = function() {
|
||||||
|
$('.meeting-url').val(Meeting.getInstance().getURL());
|
||||||
|
};
|
||||||
|
|
||||||
|
var showNotification = function(title, options) {
|
||||||
|
if (Notification.permission === "granted") {
|
||||||
|
var icon = '<%= asset_path("bbb-logo.png") %>';
|
||||||
|
options = $.extend(options, {
|
||||||
|
icon: icon,
|
||||||
|
tag: 'UserWaiting'
|
||||||
|
});
|
||||||
|
var notification = new Notification(title, options);
|
||||||
|
notification.onclick = function() {
|
||||||
|
window.focus();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var isRoomOwner = function() {
|
||||||
|
return $('body').data('current-user') === $('.page-wrapper.rooms').data('admin-id')
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now there are notifications only for users signed in and when they
|
||||||
|
// are in their room's page
|
||||||
|
$(document).on("turbolinks:load", function() {
|
||||||
|
if (isRoomOwner()) {
|
||||||
|
Notification.requestPermission();
|
||||||
|
}
|
||||||
|
flashAlertDelayedDismiss();
|
||||||
|
});
|
|
@ -1,105 +0,0 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
$(document).on('turbolinks:load', function(){
|
|
||||||
var controller = $("body").data('controller');
|
|
||||||
var action = $("body").data('action');
|
|
||||||
|
|
||||||
if(controller == "rooms" && action == "show"
|
|
||||||
|| controller == "rooms" && action == "update"
|
|
||||||
|| controller == "users" && action == "recordings"
|
|
||||||
|| controller == "admins" && action == "server_recordings"){
|
|
||||||
|
|
||||||
// Choose active header
|
|
||||||
// (Name, Length or Users)
|
|
||||||
$('th').each(function(){
|
|
||||||
if($(this).data("header")){
|
|
||||||
$(this).on('click', function(){
|
|
||||||
set_active_header($(this).data("header"));
|
|
||||||
sort_by($(this).data("header"), $(this).data('order'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Based on the header (Name, Length or Users) clicked,
|
|
||||||
// Modify the ui for the tables
|
|
||||||
var set_active_header = function(active_header){
|
|
||||||
$('th').each(function(){
|
|
||||||
if($(this).data("header") == active_header){
|
|
||||||
configure_order($(this));
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
$(this).text($(this).data("header"));
|
|
||||||
$(this).data('order', 'none');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Based on the header (Name, Length or Users) clicked,
|
|
||||||
// Modify the ui for the tables
|
|
||||||
var configure_order = function(header_elem){
|
|
||||||
if(header_elem.data('order') === 'asc'){ // asc
|
|
||||||
header_elem.data('order', 'desc');
|
|
||||||
}
|
|
||||||
else if(header_elem.data('order') === 'desc'){ // desc
|
|
||||||
header_elem.data('order', 'none');
|
|
||||||
}
|
|
||||||
else{ // none
|
|
||||||
header_elem.data('order', 'asc');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given a label and an order, sort recordings by order
|
|
||||||
// under a given label
|
|
||||||
var sort_by = function(label, order){
|
|
||||||
var recording_list_tbody = $('.table-responsive').find('tbody');
|
|
||||||
if(label === "Name"){
|
|
||||||
sort_recordings(recording_list_tbody, order, "#recording-title");
|
|
||||||
}
|
|
||||||
else if(label === "Length"){
|
|
||||||
sort_recordings(recording_list_tbody, order, "#recording-length");
|
|
||||||
}
|
|
||||||
else if(label === "Users"){
|
|
||||||
sort_recordings(recording_list_tbody, order, "#recording-users");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generalized function for sorting recordings
|
|
||||||
var sort_recordings = function(recording_list_tbody, order, recording_id){
|
|
||||||
recording_list_tbody.find('tr').sort(function(a, b){
|
|
||||||
var a_val, b_val;
|
|
||||||
if (recording_id == "#recording-length") {
|
|
||||||
a_val = $.trim($(a).find(recording_id).data("full-length"));
|
|
||||||
b_val = $.trim($(b).find(recording_id).data("full-length"));
|
|
||||||
} else {
|
|
||||||
a_val = $.trim($(a).find(recording_id).text());
|
|
||||||
b_val = $.trim($(b).find(recording_id).text());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(order === "asc"){
|
|
||||||
return a_val.localeCompare(b_val);
|
|
||||||
}
|
|
||||||
else if(order === "desc"){
|
|
||||||
return b_val.localeCompare(a_val);
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}).appendTo(recording_list_tbody);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,46 +0,0 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
$(document).on('turbolinks:load', function(){
|
|
||||||
var controller = $("body").data('controller');
|
|
||||||
var action = $("body").data('action');
|
|
||||||
if ((controller == "admins" && action == "edit_user") || (controller == "users" && action == "edit")) {
|
|
||||||
// Hack to make it play nice with turbolinks
|
|
||||||
if ($("#role-dropdown:visible").length == 0){
|
|
||||||
$(window).trigger('load.bs.select.data-api')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check to see if the role dropdown was set up
|
|
||||||
if ($("#role-dropdown").length != 0){
|
|
||||||
$("#role-dropdown").selectpicker('val', $("#user_role_id").val())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update hidden field with new value
|
|
||||||
$("#role-dropdown").on("changed.bs.select", function(){
|
|
||||||
$("#user_role_id").val($("#role-dropdown").selectpicker('val'))
|
|
||||||
})
|
|
||||||
|
|
||||||
// Update hidden field with new value
|
|
||||||
// $("#language-dropdown").on("show.bs.select", function(){
|
|
||||||
// $("#language-dropdown").selectpicker('val', $("#user_language").val())
|
|
||||||
// })
|
|
||||||
|
|
||||||
// Update hidden field with new value
|
|
||||||
$("#language-dropdown").on("changed.bs.select", function(){
|
|
||||||
$("#user_language").val($("#language-dropdown").selectpicker('val'))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -1,67 +0,0 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// Handle client request to join when meeting starts.
|
|
||||||
$(document).on("turbolinks:load", function(){
|
|
||||||
var controller = $("body").data('controller');
|
|
||||||
var action = $("body").data('action');
|
|
||||||
|
|
||||||
if(controller == "rooms" && action == "join"){
|
|
||||||
App.waiting = App.cable.subscriptions.create({
|
|
||||||
channel: "WaitingChannel",
|
|
||||||
roomuid: $(".background").attr("room"),
|
|
||||||
useruid: $(".background").attr("user")
|
|
||||||
}, {
|
|
||||||
connected: function() {
|
|
||||||
console.log("connected");
|
|
||||||
setTimeout(startRefreshTimeout, 120000);
|
|
||||||
},
|
|
||||||
|
|
||||||
disconnected: function(data) {
|
|
||||||
console.log("disconnected");
|
|
||||||
console.log(data);
|
|
||||||
},
|
|
||||||
|
|
||||||
rejected: function() {
|
|
||||||
console.log("rejected");
|
|
||||||
},
|
|
||||||
|
|
||||||
received: function(data){
|
|
||||||
console.log(data);
|
|
||||||
if(data.action == "started"){
|
|
||||||
request_to_join_meeting();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var join_attempts = 0;
|
|
||||||
|
|
||||||
function request_to_join_meeting() {
|
|
||||||
$.post(window.location.pathname, { join_name: $(".background").attr("join-name") }, function() {
|
|
||||||
//Successful post - set up retry incase
|
|
||||||
if(join_attempts < 4){ setTimeout(request_to_join_meeting, 10000); }
|
|
||||||
join_attempts++;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh the page after 2 mins and attempt to reconnect to ActionCable
|
|
||||||
function startRefreshTimeout() {
|
|
||||||
var url = new URL(window.location.href)
|
|
||||||
url.searchParams.set("reload","true")
|
|
||||||
window.location.href = url.href
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
* version.
|
|
||||||
*
|
|
||||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "tabler/functions";
|
|
||||||
|
|
||||||
@import "tabler/core";
|
|
||||||
@import "tabler/type";
|
|
||||||
@import "tabler/grid";
|
|
||||||
|
|
||||||
@import "tabler/layout";
|
|
||||||
@import "tabler/aside";
|
|
||||||
@import "tabler/header";
|
|
||||||
@import "tabler/footer";
|
|
||||||
@import "tabler/colors";
|
|
||||||
@import "tabler/text";
|
|
||||||
@import "tabler/utilities";
|
|
||||||
|
|
||||||
@import "tabler/nav";
|
|
||||||
@import "tabler/button";
|
|
||||||
@import "tabler/alert";
|
|
||||||
//@import "tabler/close";
|
|
||||||
//@import "tabler/badge";
|
|
||||||
@import "tabler/tables";
|
|
||||||
//@import "tabler/breadcrumb";
|
|
||||||
//@import "tabler/pagination";
|
|
||||||
@import "tabler/cards";
|
|
||||||
//@import "tabler/popover";
|
|
||||||
@import "tabler/dropdown";
|
|
||||||
@import "tabler/list";
|
|
||||||
@import "tabler/list-group";
|
|
||||||
@import "tabler/avatar";
|
|
||||||
//@import "tabler/product";
|
|
||||||
@import "tabler/progress";
|
|
||||||
@import "tabler/icon";
|
|
||||||
//@import "tabler/image";
|
|
||||||
//@import "tabler/link";
|
|
||||||
//@import "tabler/media";
|
|
||||||
@import "tabler/form";
|
|
||||||
//@import "tabler/sparkline";
|
|
||||||
@import "tabler/social";
|
|
||||||
//@import "tabler/maps";
|
|
||||||
//@import "tabler/statuses";
|
|
||||||
//@import "tabler/charts";
|
|
||||||
//@import "tabler/chips";
|
|
||||||
@import "tabler/stamp";
|
|
||||||
//@import "tabler/chat";
|
|
||||||
//@import "tabler/example";
|
|
||||||
@import "tabler/tag";
|
|
||||||
//@import "tabler/syntax";
|
|
||||||
//@import "tabler/infobox";
|
|
||||||
//@import "tabler/carousel";
|
|
||||||
|
|
||||||
//@import "tabler/forms/custom-range";
|
|
||||||
//@import "tabler/forms/custom-selectgroup";
|
|
||||||
@import "tabler/forms/custom-switch";
|
|
||||||
//@import "tabler/forms/custom-imagecheck";
|
|
||||||
@import "tabler/forms/custom-colorinput";
|
|
||||||
|
|
||||||
//@import "tabler/timeline";
|
|
||||||
|
|
||||||
//@import "tabler/browser";
|
|
||||||
//@import "tabler/flag";
|
|
||||||
//@import "tabler/payments";
|
|
||||||
//@import "tabler/jvectormap";
|
|
||||||
//@import "tabler/selectize";
|
|
||||||
|
|
||||||
//@import "tabler/fonts/feather";
|
|
|
@ -1,105 +0,0 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
#users-table {
|
|
||||||
.user-role {
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-email {
|
|
||||||
max-width: 250px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#clear-search {
|
|
||||||
z-index: 9;
|
|
||||||
position: absolute;
|
|
||||||
right: 55px;
|
|
||||||
top: 8px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag i {
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#branding-image{
|
|
||||||
z-index: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.authentication-required{
|
|
||||||
padding-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#site_settings {
|
|
||||||
.colorinput-color {
|
|
||||||
text-align: center;
|
|
||||||
padding-top: 4px;
|
|
||||||
height: 2rem;
|
|
||||||
width: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sort-disabled{
|
|
||||||
background: #e6e6e6 !important;
|
|
||||||
color: rgb(110, 118, 135) !important;
|
|
||||||
opacity: 0.75;
|
|
||||||
&:hover{
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-disable{
|
|
||||||
background-color: #e6e6e6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.role-colour-picker{
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-role-tag{
|
|
||||||
color: white !important;
|
|
||||||
// Make it consistent with the manage users tab tags
|
|
||||||
padding-top: 1px;
|
|
||||||
padding-bottom: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-role-tag{
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#manage-users-nav.nav-tabs .nav-item {
|
|
||||||
margin-bottom: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#merge-account-arrow {
|
|
||||||
position: absolute;
|
|
||||||
top: 47%;
|
|
||||||
right: 47%;
|
|
||||||
z-index: 999;
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-tabs {
|
|
||||||
justify-content: space-around;
|
|
||||||
}
|
|
||||||
|
|
||||||
#rooms-table-div {
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
||||||
|
* listed below.
|
||||||
|
*
|
||||||
|
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
||||||
|
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
||||||
|
*
|
||||||
|
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
||||||
|
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
||||||
|
* files in this directory. Styles in this file should be added after the last require_* statement.
|
||||||
|
* It is generally better to create a new file per style scope.
|
||||||
|
*
|
||||||
|
*= require jquery-ui
|
||||||
|
*= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
|
||||||
|
*= require_tree ./main
|
||||||
|
*= require_self
|
||||||
|
*/
|
|
@ -1,193 +0,0 @@
|
||||||
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
* version.
|
|
||||||
*
|
|
||||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
|
||||||
* listed below.
|
|
||||||
*
|
|
||||||
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
|
||||||
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
|
||||||
*
|
|
||||||
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
|
||||||
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
|
||||||
* files in this directory. Styles in this file should be added after the last require_* statement.
|
|
||||||
* It is generally better to create a new file per style scope.
|
|
||||||
*
|
|
||||||
*= require_self
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "tabler/variables";
|
|
||||||
@import "bootstrap";
|
|
||||||
@import "jquery-ui/sortable";
|
|
||||||
@import "tabler-custom";
|
|
||||||
@import "font-awesome-sprockets";
|
|
||||||
@import "font-awesome";
|
|
||||||
@import "monolith.min.scss";
|
|
||||||
@import "bootstrap-select.min";
|
|
||||||
|
|
||||||
@import "utilities/variables";
|
|
||||||
@import "admins";
|
|
||||||
@import "main";
|
|
||||||
@import "rooms";
|
|
||||||
@import "sessions";
|
|
||||||
@import "utilities/fonts";
|
|
||||||
@import "users";
|
|
||||||
|
|
||||||
* {
|
|
||||||
outline: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
font-family: "Source Sans Pro" !important;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none !important;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dotted_underline {
|
|
||||||
border-bottom: 3px dashed #d3d3d3;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disable-click {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
height: $header-height;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
position: relative;
|
|
||||||
height: auto;
|
|
||||||
min-height: calc(100% - #{$header-height} - #{$footer-height});
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
height: $footer-height;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background {
|
|
||||||
background-color: $background-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-section {
|
|
||||||
background-color: $error-background-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.font-weight-400 {
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-size: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.force-bottom {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invite-link-input {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-border-top {
|
|
||||||
td {
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.force-text-normal {
|
|
||||||
color: #495057;
|
|
||||||
}
|
|
||||||
|
|
||||||
.terms {
|
|
||||||
overflow: scroll;
|
|
||||||
height: 55vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
[contenteditable]:focus {
|
|
||||||
outline: 0px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cookies-banner {
|
|
||||||
color: white;
|
|
||||||
background-color: $button-color-blue;
|
|
||||||
#cookies-agree-button {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus {
|
|
||||||
border-color: $primary !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group button:focus {
|
|
||||||
box-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-group-item-action.active {
|
|
||||||
color: $primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header .header-nav {
|
|
||||||
color: $text-muted !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
padding-bottom: 21px;
|
|
||||||
border-bottom: 1px solid $primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: $primary !important;
|
|
||||||
padding-bottom: 21px;
|
|
||||||
border-bottom: 1px solid $primary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
thead {
|
|
||||||
th[data-order]:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cursor-pointer{
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#delete-confirm:disabled {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn i {
|
|
||||||
transition: all .15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-icon i {
|
|
||||||
width: 35px;
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
* version.
|
|
||||||
*
|
|
||||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Place all the styles related to the Errors controller here.
|
|
||||||
// They will automatically be included in application.css.
|
|
||||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
|
@ -1,6 +1,6 @@
|
||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
//
|
//
|
||||||
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
@ -14,23 +14,5 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
$(document).on('turbolinks:load', function(){
|
@import "font-awesome-sprockets";
|
||||||
// Stores the current url when the user clicks the sign in button
|
@import "font-awesome";
|
||||||
$(".sign-in-button").click(function(){
|
|
||||||
var url = location.href
|
|
||||||
// Add the slash at the end if it's missing
|
|
||||||
url += url.endsWith("/") ? "" : "/"
|
|
||||||
document.cookie ="return_to=" + url
|
|
||||||
})
|
|
||||||
|
|
||||||
// Checks to see if the user provided an image url and displays it if they did
|
|
||||||
$("#user-image")
|
|
||||||
.on("load", function() {
|
|
||||||
$("#user-image").show()
|
|
||||||
$("#user-avatar").hide()
|
|
||||||
})
|
|
||||||
.on("error", function() {
|
|
||||||
$("#user-image").hide()
|
|
||||||
$("#user-avatar").show()
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,178 +0,0 @@
|
||||||
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
* version.
|
|
||||||
*
|
|
||||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Place all the styles related to the Main controller here.
|
|
||||||
// They will automatically be included in application.css.
|
|
||||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
||||||
|
|
||||||
.responsive-header {
|
|
||||||
font-size: 3.3vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.landing-section {
|
|
||||||
position: relative;
|
|
||||||
height: 40%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lead {
|
|
||||||
font-size:18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-stamp {
|
|
||||||
.stamp {
|
|
||||||
padding:1em 1.5em;
|
|
||||||
height:auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.or-line {
|
|
||||||
div {
|
|
||||||
width: 100%;
|
|
||||||
height: 16px;
|
|
||||||
border-bottom: 1px solid #d3d3d3;
|
|
||||||
text-align: center;
|
|
||||||
span {
|
|
||||||
font-size: 120%;
|
|
||||||
background-color: #ffffff;
|
|
||||||
padding: 0 10px;
|
|
||||||
color: #d3d3d3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.display-4 {
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.customBtn {
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
background: #cccccc;
|
|
||||||
color: #ffffff;
|
|
||||||
box-shadow: 0 2px 4px 0 rgba(0,0,0,.25);
|
|
||||||
-webkit-transition: background-color .218s,border-color .218s,box-shadow .218s;
|
|
||||||
transition: background-color .218s,border-color .218s,box-shadow .218s;
|
|
||||||
white-space: nowrap;
|
|
||||||
border-radius: 2px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
margin-bottom:20px;
|
|
||||||
&:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
color: #ffffff;
|
|
||||||
-webkit-box-shadow: 0 0 3px 3px rgba(66,133,244,.3);
|
|
||||||
box-shadow: 0 0 3px 3px rgba(66,133,244,.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.customBtn-icon {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
padding:13px 15px 13px 15px;
|
|
||||||
float:left;
|
|
||||||
background: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.customBtn-text {
|
|
||||||
font-size: 19px;
|
|
||||||
line-height: 48px;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: .21px;
|
|
||||||
padding:0 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.customBtn-image {
|
|
||||||
background: #ffffff;
|
|
||||||
background-size: 18px 18px;
|
|
||||||
padding:10px 10px 10px 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.customBtn-google {
|
|
||||||
@extend .customBtn;
|
|
||||||
background: #4688f1;
|
|
||||||
|
|
||||||
.customBtn-image {
|
|
||||||
background: #ffffff image-url("google-logo.png") no-repeat left top;
|
|
||||||
background-size: 18px 18px;
|
|
||||||
padding:10px 10px 10px 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.customBtn-twitter {
|
|
||||||
@extend .customBtn;
|
|
||||||
background: #1da1f2;
|
|
||||||
|
|
||||||
.customBtn-image {
|
|
||||||
background: #ffffff image-url("twitter-logo.png") no-repeat left top;
|
|
||||||
background-size: 18px 18px;
|
|
||||||
padding:10px 10px 10px 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.customBtn-office365 {
|
|
||||||
@extend .customBtn;
|
|
||||||
background: #f65314;
|
|
||||||
|
|
||||||
.customBtn-image {
|
|
||||||
background: #ffffff image-url("office365-logo.jpeg") no-repeat left top;
|
|
||||||
background-size: 18px 18px;
|
|
||||||
padding:10px 10px 10px 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.customBtn-microsoft_windows {
|
|
||||||
@extend .customBtn;
|
|
||||||
background: #00a1f1;
|
|
||||||
|
|
||||||
.customBtn-image {
|
|
||||||
background: #ffffff image-url("windows-logo.png") no-repeat left top;
|
|
||||||
background-size: 18px 18px;
|
|
||||||
padding:10px 10px 10px 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.customBtn-ldap {
|
|
||||||
@extend .customBtn;
|
|
||||||
background: #d61515;
|
|
||||||
|
|
||||||
.customBtn-image {
|
|
||||||
background: #ffffff image-url("ldap-logo.png") no-repeat left top;
|
|
||||||
background-size: 18px 18px;
|
|
||||||
padding:10px 10px 10px 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.customBtn-openid_connect {
|
|
||||||
@extend .customBtn;
|
|
||||||
background: #ef8e1f;
|
|
||||||
|
|
||||||
.customBtn-image {
|
|
||||||
background: #ffffff image-url("openid-logo.png") no-repeat left top;
|
|
||||||
background-size: 18px 18px;
|
|
||||||
padding:10px 10px 10px 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.signin-button {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-responsive tbody td:first-child > *:first-child {
|
|
||||||
max-height: 3em;
|
|
||||||
overflow: hidden;
|
|
||||||
max-width: 200px;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,133 @@
|
||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
.meeting-url-button-group {
|
||||||
|
padding-top: 5px;
|
||||||
|
width: 150px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meeting-url-qrcode-group {
|
||||||
|
padding-top: 5px;
|
||||||
|
width: 128px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previously-joined, .actives {
|
||||||
|
list-style-type: none;
|
||||||
|
margin:auto;
|
||||||
|
width: 200px;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meetings {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.rooms {
|
||||||
|
|
||||||
|
.table-wrapper {
|
||||||
|
padding: 40px 50px 10px 50px;
|
||||||
|
|
||||||
|
#recordings {
|
||||||
|
thead {
|
||||||
|
th:after {
|
||||||
|
content: none; //removes the sort icon in table header
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.dataTables_empty {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.timeago {
|
||||||
|
margin-left: 5px;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-thumbnail{
|
||||||
|
padding: 4px;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #dddddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&.large{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover + &.large{
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.meeting-url-group {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recording-update-trigger {
|
||||||
|
&.recording-unpublished {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.recording-unlisted {
|
||||||
|
color: #e3a91e;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.recording-published {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-spinner {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-button {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.youtube-red {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cloud-blue {
|
||||||
|
color: cornflowerblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green-check {
|
||||||
|
color: limegreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-buffer {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#youtube-footer{
|
||||||
|
font-size: 10px;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
.login {
|
||||||
|
.center-panel {
|
||||||
|
.center-panel-size {
|
||||||
|
max-width: 400px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
@import "bootstrap-sprockets";
|
||||||
|
@import "bootstrap";
|
||||||
|
@import "bootstrap-social";
|
||||||
|
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-fluid {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background {
|
||||||
|
height: 350px;
|
||||||
|
width: 100%;
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
body[data-controller=landing].app-background {
|
||||||
|
@extend .background;
|
||||||
|
}
|
||||||
|
|
||||||
|
#alerts {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -250px;
|
||||||
|
width: 500px;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding: 20px 40px;
|
||||||
|
margin-bottom: 160px;
|
||||||
|
.logo {
|
||||||
|
max-width: 150px;
|
||||||
|
max-height: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-block {
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
.center-panel-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.center-panel {
|
||||||
|
height: 100%;
|
||||||
|
.center-panel-size {
|
||||||
|
max-width: 1200px
|
||||||
|
}
|
||||||
|
.center-panel-content-size {
|
||||||
|
height: 100%;
|
||||||
|
max-width: 1100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-spacing {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-footer {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-body {
|
||||||
|
padding-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-wrapper {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-form-wrapper {
|
||||||
|
.center-block {
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
.join-form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
width: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.meeting-url-wrapper {
|
||||||
|
.meeting-url {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-wrapper {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.signin-link {
|
||||||
|
&:hover, &:focus {
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.signin-link {
|
||||||
|
.signin-icon {
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
.signin-button {
|
||||||
|
background: white;
|
||||||
|
width: 250px;
|
||||||
|
border: thin solid #888;
|
||||||
|
border-radius: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 5px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
.signin-icon-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
.signin-text-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
.signin-text {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.verticle-line {
|
||||||
|
// parent must be position relative to work
|
||||||
|
width: 1px;
|
||||||
|
background-color: lightgray;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invite-join-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-link {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
padding-right: 3px;
|
||||||
|
}
|
|
@ -1,146 +0,0 @@
|
||||||
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
* version.
|
|
||||||
*
|
|
||||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Place all the styles related to the Rooms controller here.
|
|
||||||
// They will automatically be included in application.css.
|
|
||||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
||||||
|
|
||||||
.start-button {
|
|
||||||
font-size: 26px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumbnail {
|
|
||||||
height: 60px !important;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.join-form {
|
|
||||||
font-size: 20px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.moderator-code-label {
|
|
||||||
margin-top: 150px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-indicator {
|
|
||||||
font-size: 22px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-del-room {
|
|
||||||
width: 70% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit_hover_class a{
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit_hover_class:hover a {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
#room-settings-dropdown-label {
|
|
||||||
vertical-align: middle;
|
|
||||||
padding-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.room-block {
|
|
||||||
&:not(.current) {
|
|
||||||
.stamp {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#create-room-block {
|
|
||||||
border: 1px dashed lightgray;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: rgba(0, 0, 0, 0.04);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.allow-icon-click{
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cant-create-rooms-title{
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-xxxl{
|
|
||||||
width: 8rem;
|
|
||||||
height: 8rem;
|
|
||||||
line-height: 8rem;
|
|
||||||
max-width: 8rem;
|
|
||||||
margin-top: -6rem;
|
|
||||||
font-size: 5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bootstrap-select .dropdown-menu li.active small.text-muted{
|
|
||||||
color: #9aa0ac !important
|
|
||||||
}
|
|
||||||
|
|
||||||
.not-saved {
|
|
||||||
color: grey;
|
|
||||||
background: rgba(0, 40, 100, 0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu.show {
|
|
||||||
min-height: 0px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.remove-shared {
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
|
|
||||||
.enabled-setting {
|
|
||||||
background: lightgray;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#recording-table .edit_hover_class {
|
|
||||||
word-break: break-word;
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
#room-owner-name {
|
|
||||||
line-height: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.create-room-button {
|
|
||||||
width: 49%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#presentation-upload-label {
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
padding-right: 75px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#clear-room-search {
|
|
||||||
z-index: 9;
|
|
||||||
position: absolute;
|
|
||||||
right: 15px;
|
|
||||||
top: 8px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
* version.
|
|
||||||
*
|
|
||||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Place all the styles related to the Sessions controller here.
|
|
||||||
// They will automatically be included in application.css.
|
|
||||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
||||||
|
|
||||||
.login {
|
|
||||||
.center-panel {
|
|
||||||
.center-panel-size {
|
|
||||||
max-width: 400px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a.signin-link {
|
|
||||||
&:hover, &:focus {
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.signin-link {
|
|
||||||
.signin-icon {
|
|
||||||
vertical-align: middle;
|
|
||||||
width: 35px;
|
|
||||||
height: 35px;
|
|
||||||
}
|
|
||||||
.signin-button {
|
|
||||||
background: white;
|
|
||||||
width: 250px;
|
|
||||||
border: thin solid #888;
|
|
||||||
border-radius: 2px;
|
|
||||||
white-space: nowrap;
|
|
||||||
padding: 5px;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
.signin-icon-wrapper {
|
|
||||||
display: inline-block;
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
.signin-text-wrapper {
|
|
||||||
display: inline-block;
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
.signin-text {
|
|
||||||
vertical-align: middle;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
* version.
|
|
||||||
*
|
|
||||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Place all the styles related to the Users controller here.
|
|
||||||
// They will automatically be included in application.css.
|
|
||||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
||||||
|
|
||||||
.user-role-tag{
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shared-user {
|
|
||||||
line-height: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bootstrap-select {
|
|
||||||
border: 1px solid rgba(0, 40, 100, 0.12);
|
|
||||||
}
|
|
|
@ -1,504 +0,0 @@
|
||||||
/* cyrillic-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
|
||||||
}
|
|
||||||
/* cyrillic */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
|
||||||
}
|
|
||||||
/* greek-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+1F00-1FFF;
|
|
||||||
}
|
|
||||||
/* greek */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0370-03FF;
|
|
||||||
}
|
|
||||||
/* vietnamese */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
|
||||||
}
|
|
||||||
/* latin-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
||||||
}
|
|
||||||
/* latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
||||||
}
|
|
||||||
/* cyrillic-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
|
||||||
}
|
|
||||||
/* cyrillic */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
|
||||||
}
|
|
||||||
/* greek-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+1F00-1FFF;
|
|
||||||
}
|
|
||||||
/* greek */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0370-03FF;
|
|
||||||
}
|
|
||||||
/* vietnamese */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
|
||||||
}
|
|
||||||
/* latin-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
||||||
}
|
|
||||||
/* latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
||||||
}
|
|
||||||
/* cyrillic-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
|
||||||
}
|
|
||||||
/* cyrillic */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
|
||||||
}
|
|
||||||
/* greek-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+1F00-1FFF;
|
|
||||||
}
|
|
||||||
/* greek */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0370-03FF;
|
|
||||||
}
|
|
||||||
/* vietnamese */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
|
||||||
}
|
|
||||||
/* latin-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
||||||
}
|
|
||||||
/* latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
||||||
}
|
|
||||||
/* cyrillic-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
|
||||||
}
|
|
||||||
/* cyrillic */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
|
||||||
}
|
|
||||||
/* greek-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+1F00-1FFF;
|
|
||||||
}
|
|
||||||
/* greek */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0370-03FF;
|
|
||||||
}
|
|
||||||
/* vietnamese */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
|
||||||
}
|
|
||||||
/* latin-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
||||||
}
|
|
||||||
/* latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
||||||
}
|
|
||||||
/* cyrillic-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
|
||||||
}
|
|
||||||
/* cyrillic */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
|
||||||
}
|
|
||||||
/* greek-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
|
||||||
unicode-range: U+1F00-1FFF;
|
|
||||||
}
|
|
||||||
/* greek */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0370-03FF;
|
|
||||||
}
|
|
||||||
/* vietnamese */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
|
||||||
}
|
|
||||||
/* latin-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
||||||
}
|
|
||||||
/* latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 300;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
||||||
}
|
|
||||||
/* cyrillic-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
|
||||||
}
|
|
||||||
/* cyrillic */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
|
||||||
}
|
|
||||||
/* greek-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
|
||||||
unicode-range: U+1F00-1FFF;
|
|
||||||
}
|
|
||||||
/* greek */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0370-03FF;
|
|
||||||
}
|
|
||||||
/* vietnamese */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
|
||||||
}
|
|
||||||
/* latin-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
||||||
}
|
|
||||||
/* latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
||||||
}
|
|
||||||
/* cyrillic-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
|
||||||
}
|
|
||||||
/* cyrillic */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
|
||||||
}
|
|
||||||
/* greek-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+1F00-1FFF;
|
|
||||||
}
|
|
||||||
/* greek */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0370-03FF;
|
|
||||||
}
|
|
||||||
/* vietnamese */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
|
||||||
}
|
|
||||||
/* latin-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
||||||
}
|
|
||||||
/* latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 600;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
||||||
}
|
|
||||||
/* cyrillic-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
|
||||||
}
|
|
||||||
/* cyrillic */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
|
||||||
}
|
|
||||||
/* greek-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+1F00-1FFF;
|
|
||||||
}
|
|
||||||
/* greek */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0370-03FF;
|
|
||||||
}
|
|
||||||
/* vietnamese */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
|
||||||
}
|
|
||||||
/* latin-ext */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
|
||||||
}
|
|
||||||
/* latin */
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Source Sans Pro';
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
font-display: swap;
|
|
||||||
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
|
||||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
* version.
|
|
||||||
*
|
|
||||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
// Declare all variables here.
|
|
||||||
// They will automatically be included in application.css.
|
|
||||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
|
||||||
$background-color: #F5F7FB;
|
|
||||||
$error-background-color: #EFE6E6;
|
|
||||||
$button-color-blue: #467FCF;
|
|
||||||
$header-height: 65px;
|
|
||||||
$footer-height: 65px;
|
|
|
@ -1,8 +1,6 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
class MeetingUpdatesChannel < ApplicationCable::Channel
|
||||||
|
def subscribed
|
||||||
|
|
||||||
|
full_id = if params[:meeting_id].present?
|
||||||
|
"#{params[:admin_id]}-#{params[:meeting_id]}"
|
||||||
|
else
|
||||||
|
params[:admin_id]
|
||||||
|
end
|
||||||
|
stream_from "#{full_id}_meeting_updates_channel"
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,26 @@
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
class RecordingUpdatesChannel < ApplicationCable::Channel
|
||||||
|
def subscribed
|
||||||
|
full_id = if params[:meeting_id].present?
|
||||||
|
"#{params[:admin_id]}-#{params[:meeting_id]}"
|
||||||
|
else
|
||||||
|
params[:admin_id]
|
||||||
|
end
|
||||||
|
stream_from "#{full_id}_recording_updates_channel"
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,8 +1,6 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
@ -16,8 +14,8 @@
|
||||||
# You should have received a copy of the GNU Lesser General Public License along
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
class WaitingChannel < ApplicationCable::Channel
|
class RefreshMeetingsChannel < ApplicationCable::Channel
|
||||||
def subscribed
|
def subscribed
|
||||||
stream_from "#{params[:roomuid]}_waiting_channel"
|
stream_from "refresh_meetings"
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,78 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
# Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
# version.
|
|
||||||
#
|
|
||||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
class AccountActivationsController < ApplicationController
|
|
||||||
include Emailer
|
|
||||||
include Authenticator
|
|
||||||
|
|
||||||
before_action :ensure_unauthenticated
|
|
||||||
before_action :find_user_by_token, only: :edit
|
|
||||||
before_action :find_user_by_digest, only: :resend
|
|
||||||
|
|
||||||
# GET /account_activations
|
|
||||||
def show
|
|
||||||
end
|
|
||||||
|
|
||||||
# GET /account_activations/edit
|
|
||||||
def edit
|
|
||||||
# If the user exists and is not verified and provided the correct token
|
|
||||||
if @user && !@user.activated?
|
|
||||||
# Verify user
|
|
||||||
@user.set_role(initial_user_role(@user.email)) if @user.role.nil?
|
|
||||||
@user.activate
|
|
||||||
|
|
||||||
# Redirect user to root with account pending flash if account is still pending
|
|
||||||
return redirect_to root_path,
|
|
||||||
flash: { success: I18n.t("registration.approval.signup") } if @user.has_role?(:pending)
|
|
||||||
|
|
||||||
# Redirect user to sign in path with success flash
|
|
||||||
redirect_to signin_path, flash: { success: "#{I18n.t('verify.activated')} #{I18n.t('verify.signin')}" }
|
|
||||||
else
|
|
||||||
redirect_to root_path, flash: { alert: I18n.t("verify.invalid") }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /account_activations/resend
|
|
||||||
def resend
|
|
||||||
if @user.activated?
|
|
||||||
# User is already verified
|
|
||||||
flash[:alert] = I18n.t("verify.already_verified")
|
|
||||||
else
|
|
||||||
# Resend
|
|
||||||
send_activation_email(@user, @user.create_activation_token)
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_to root_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def find_user_by_token
|
|
||||||
return redirect_to root_path, flash: { alert: I18n.t("verify.invalid") } unless params[:token].present?
|
|
||||||
|
|
||||||
@user = User.find_by!(activation_digest: User.hash_token(params[:token]), provider: @user_domain)
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_user_by_digest
|
|
||||||
@user = User.find_by!(activation_digest: params[:digest], provider: @user_domain)
|
|
||||||
end
|
|
||||||
|
|
||||||
def ensure_unauthenticated
|
|
||||||
redirect_to current_user.main_room || root_path if current_user
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,379 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
# Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
# version.
|
|
||||||
#
|
|
||||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
class AdminsController < ApplicationController
|
|
||||||
include Pagy::Backend
|
|
||||||
include Themer
|
|
||||||
include Emailer
|
|
||||||
include Recorder
|
|
||||||
include Rolify
|
|
||||||
include Populator
|
|
||||||
|
|
||||||
manage_users = [:edit_user, :promote, :demote, :ban_user, :unban_user, :approve, :reset, :merge_user]
|
|
||||||
manage_deleted_users = [:undelete]
|
|
||||||
authorize_resource class: false
|
|
||||||
before_action :find_user, only: manage_users
|
|
||||||
before_action :find_deleted_user, only: manage_deleted_users
|
|
||||||
before_action :verify_admin_of_user, only: [manage_users, manage_deleted_users]
|
|
||||||
|
|
||||||
# GET /admins
|
|
||||||
def index
|
|
||||||
# Initializa the data manipulation variables
|
|
||||||
@search = params[:search] || ""
|
|
||||||
@order_column = params[:column] && params[:direction] != "none" ? params[:column] : "created_at"
|
|
||||||
@order_direction = params[:direction] && params[:direction] != "none" ? params[:direction] : "DESC"
|
|
||||||
@tab = params[:tab] || "active"
|
|
||||||
@role = params[:role] ? Role.find_by(name: params[:role], provider: @user_domain) : nil
|
|
||||||
|
|
||||||
users = if @tab == "invited"
|
|
||||||
invited_users_list
|
|
||||||
else
|
|
||||||
manage_users_list
|
|
||||||
end
|
|
||||||
|
|
||||||
@pagy, @users = pagy(users)
|
|
||||||
end
|
|
||||||
|
|
||||||
# GET /admins/site_settings
|
|
||||||
def site_settings
|
|
||||||
@tab = params[:tab] || "appearance"
|
|
||||||
end
|
|
||||||
|
|
||||||
# GET /admins/server_recordings
|
|
||||||
def server_recordings
|
|
||||||
@search = params[:search] || ""
|
|
||||||
|
|
||||||
if @search.present?
|
|
||||||
if @search.include? "@"
|
|
||||||
user_email = @search
|
|
||||||
else
|
|
||||||
room_uid = @search
|
|
||||||
end
|
|
||||||
else
|
|
||||||
@latest = true
|
|
||||||
end
|
|
||||||
|
|
||||||
@pagy, @recordings = pagy_array(recordings_to_show(user_email, room_uid))
|
|
||||||
end
|
|
||||||
|
|
||||||
# GET /admins/rooms
|
|
||||||
def server_rooms
|
|
||||||
@search = params[:search] || ""
|
|
||||||
@order_column = params[:column] && params[:direction] != "none" ? params[:column] : "status"
|
|
||||||
@order_direction = params[:direction] && params[:direction] != "none" ? params[:direction] : "DESC"
|
|
||||||
|
|
||||||
begin
|
|
||||||
meetings = all_running_meetings[:meetings]
|
|
||||||
rescue BigBlueButton::BigBlueButtonException
|
|
||||||
flash[:alert] = I18n.t("administrator.rooms.timeout", server: I18n.t("bigbluebutton"))
|
|
||||||
meetings = []
|
|
||||||
end
|
|
||||||
|
|
||||||
@order_column = "created_at" if meetings.empty?
|
|
||||||
@running_room_bbb_ids = meetings.pluck(:meetingID)
|
|
||||||
|
|
||||||
@participants_count = {}
|
|
||||||
meetings.each do |meet|
|
|
||||||
@participants_count[meet[:meetingID]] = meet[:participantCount]
|
|
||||||
end
|
|
||||||
|
|
||||||
@pagy, @rooms = pagy_array(server_rooms_list)
|
|
||||||
end
|
|
||||||
|
|
||||||
# GET /admins/room_configuration
|
|
||||||
def room_configuration
|
|
||||||
end
|
|
||||||
|
|
||||||
# MANAGE USERS
|
|
||||||
|
|
||||||
# GET /admins/edit/:user_uid
|
|
||||||
def edit_user
|
|
||||||
session[:prev_url] = request.referer if request.referer.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /admins/ban/:user_uid
|
|
||||||
def ban_user
|
|
||||||
@user.set_role :denied
|
|
||||||
|
|
||||||
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.banned") }
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /admins/unban/:user_uid
|
|
||||||
def unban_user
|
|
||||||
@user.set_role :user
|
|
||||||
|
|
||||||
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.unbanned") }
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /admins/approve/:user_uid
|
|
||||||
def approve
|
|
||||||
@user.set_role :user
|
|
||||||
|
|
||||||
send_user_approved_email(@user)
|
|
||||||
|
|
||||||
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.approved") }
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /admins/approve/:user_uid
|
|
||||||
def undelete
|
|
||||||
# Undelete the user and all of his rooms
|
|
||||||
@user.undelete!
|
|
||||||
@user.rooms.deleted.each(&:undelete!)
|
|
||||||
|
|
||||||
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.restored") }
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /admins/invite
|
|
||||||
def invite
|
|
||||||
emails = params[:invite_user][:email].split(",")
|
|
||||||
|
|
||||||
emails.each do |email|
|
|
||||||
invitation = create_or_update_invite(email)
|
|
||||||
|
|
||||||
send_invitation_email(current_user.name, email, invitation)
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_back fallback_location: admins_path,
|
|
||||||
flash: { success: I18n.t("administrator.flash.invite", email: emails.join(", ")) }
|
|
||||||
end
|
|
||||||
|
|
||||||
# GET /admins/reset
|
|
||||||
def reset
|
|
||||||
send_password_reset_email(@user, @user.create_reset_digest)
|
|
||||||
|
|
||||||
if session[:prev_url].present?
|
|
||||||
redirect_path = session[:prev_url]
|
|
||||||
session.delete(:prev_url)
|
|
||||||
else
|
|
||||||
redirect_path = admins_path
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_to redirect_path, flash: { success: I18n.t("administrator.flash.reset_password") }
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /admins/merge/:user_uid
|
|
||||||
def merge_user
|
|
||||||
begin
|
|
||||||
# Get uid of user that will be merged into the other account
|
|
||||||
uid_to_merge = params[:merge]
|
|
||||||
logger.info "#{current_user.uid} is attempting to merge #{uid_to_merge} into #{@user.uid}"
|
|
||||||
|
|
||||||
# Check to make sure the 2 users are unique
|
|
||||||
raise "Can not merge the user into themself" if uid_to_merge == @user.uid
|
|
||||||
|
|
||||||
# Find user to merge
|
|
||||||
user_to_merge = User.find_by(uid: uid_to_merge)
|
|
||||||
|
|
||||||
# Move over user's rooms
|
|
||||||
user_to_merge.rooms.each do |room|
|
|
||||||
room.owner = @user
|
|
||||||
|
|
||||||
room.name = "(#{I18n.t('merged')}) #{room.name}"
|
|
||||||
|
|
||||||
room.save!
|
|
||||||
end
|
|
||||||
|
|
||||||
# Reload user to update merge rooms
|
|
||||||
user_to_merge.reload
|
|
||||||
|
|
||||||
# Delete merged user
|
|
||||||
user_to_merge.destroy(true)
|
|
||||||
rescue => e
|
|
||||||
logger.info "Failed to merge #{uid_to_merge} into #{@user.uid}: #{e}"
|
|
||||||
flash[:alert] = I18n.t("administrator.flash.merge_fail")
|
|
||||||
else
|
|
||||||
logger.info "#{current_user.uid} successfully merged #{uid_to_merge} into #{@user.uid}"
|
|
||||||
flash[:success] = I18n.t("administrator.flash.merge_success")
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_back fallback_location: admins_path
|
|
||||||
end
|
|
||||||
|
|
||||||
# GET /admins/merge_list
|
|
||||||
def merge_list
|
|
||||||
# Returns a list of users that can merged into another user
|
|
||||||
initial_list = User.without_role(:super_admin)
|
|
||||||
.where.not(uid: current_user.uid)
|
|
||||||
.merge_list_search(params[:search])
|
|
||||||
|
|
||||||
initial_list = initial_list.where(provider: @user_domain) if Rails.configuration.loadbalanced_configuration
|
|
||||||
|
|
||||||
# Respond with JSON object of users
|
|
||||||
respond_to do |format|
|
|
||||||
format.json { render body: initial_list.pluck_to_hash(:uid, :name, :email).to_json }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# SITE SETTINGS
|
|
||||||
|
|
||||||
# POST /admins/update_settings
|
|
||||||
def update_settings
|
|
||||||
tab = params[:tab] || "settings"
|
|
||||||
@settings.update_value(params[:setting], params[:value])
|
|
||||||
|
|
||||||
flash_message = I18n.t("administrator.flash.settings")
|
|
||||||
|
|
||||||
if params[:value] == "Default Recording Visibility"
|
|
||||||
flash_message += ". #{I18n.t('administrator.site_settings.recording_visibility.warning')}"
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_to admin_site_settings_path(tab: tab), flash: { success: flash_message }
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /admins/color
|
|
||||||
def coloring
|
|
||||||
@settings.update_value("Primary Color", params[:value])
|
|
||||||
@settings.update_value("Primary Color Lighten", color_lighten(params[:value]))
|
|
||||||
@settings.update_value("Primary Color Darken", color_darken(params[:value]))
|
|
||||||
redirect_to admin_site_settings_path(tab: "appearance"), flash: { success: I18n.t("administrator.flash.settings") }
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /admins/registration_method/:method
|
|
||||||
def registration_method
|
|
||||||
new_method = Rails.configuration.registration_methods[params[:value].to_sym]
|
|
||||||
|
|
||||||
# Only allow change to Join by Invitation if user has emails enabled
|
|
||||||
if !Rails.configuration.enable_email_verification && new_method == Rails.configuration.registration_methods[:invite]
|
|
||||||
redirect_to admin_site_settings_path(tab: "settings"),
|
|
||||||
flash: { alert: I18n.t("administrator.flash.invite_email_verification") }
|
|
||||||
else
|
|
||||||
@settings.update_value("Registration Method", new_method)
|
|
||||||
redirect_to admin_site_settings_path(tab: "settings"),
|
|
||||||
flash: { success: I18n.t("administrator.flash.registration_method_updated") }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /admins/clear_auth
|
|
||||||
def clear_auth
|
|
||||||
User.include_deleted.where(provider: @user_domain).update_all(social_uid: nil)
|
|
||||||
|
|
||||||
redirect_to admin_site_settings_path(tab: "settings"), flash: { success: I18n.t("administrator.flash.settings") }
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /admins/clear_cache
|
|
||||||
def clear_cache
|
|
||||||
Rails.cache.delete("#{@user_domain}/getUser")
|
|
||||||
Rails.cache.delete("#{@user_domain}/getUserGreenlightCredentials")
|
|
||||||
|
|
||||||
redirect_to admin_site_settings_path(tab: "settings"), flash: { success: I18n.t("administrator.flash.settings") }
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /admins/log_level
|
|
||||||
def log_level
|
|
||||||
Rails.logger.level = params[:value].to_i
|
|
||||||
|
|
||||||
redirect_to admin_site_settings_path(tab: "administration"), flash: { success: I18n.t("administrator.flash.settings") }
|
|
||||||
end
|
|
||||||
|
|
||||||
# ROOM CONFIGURATION
|
|
||||||
# POST /admins/update_room_configuration
|
|
||||||
def update_room_configuration
|
|
||||||
@settings.update_value(params[:setting], params[:value])
|
|
||||||
|
|
||||||
flash_message = I18n.t("administrator.flash.room_configuration")
|
|
||||||
|
|
||||||
redirect_to admin_room_configuration_path, flash: { success: flash_message }
|
|
||||||
end
|
|
||||||
|
|
||||||
# ROLES
|
|
||||||
|
|
||||||
# GET /admins/roles
|
|
||||||
def roles
|
|
||||||
@roles = all_roles(params[:selected_role])
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /admins/role
|
|
||||||
# This method creates a new role scoped to the users provider
|
|
||||||
def new_role
|
|
||||||
new_role = create_role(params[:role][:name])
|
|
||||||
|
|
||||||
return redirect_to admin_roles_path, flash: { alert: I18n.t("administrator.roles.invalid_create") } if new_role.nil?
|
|
||||||
|
|
||||||
redirect_to admin_roles_path(selected_role: new_role.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
# PATCH /admin/roles/order
|
|
||||||
# This updates the priority of a site's roles
|
|
||||||
# Note: A lower priority role will always get used before a higher priority one
|
|
||||||
def change_role_order
|
|
||||||
unless update_priority(params[:role])
|
|
||||||
redirect_to admin_roles_path, flash: { alert: I18n.t("administrator.roles.invalid_order") }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /admin/role/:role_id
|
|
||||||
# This method updates the permissions assigned to a role
|
|
||||||
def update_role
|
|
||||||
role = Role.find(params[:role_id])
|
|
||||||
flash[:alert] = I18n.t("administrator.roles.invalid_update") unless update_permissions(role)
|
|
||||||
redirect_to admin_roles_path(selected_role: role.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
# DELETE admins/role/:role_id
|
|
||||||
# This deletes a role
|
|
||||||
def delete_role
|
|
||||||
role = Role.find(params[:role_id])
|
|
||||||
|
|
||||||
# Make sure no users are assigned to the role and the role isn't a reserved role
|
|
||||||
# before deleting
|
|
||||||
if role.users.count.positive?
|
|
||||||
flash[:alert] = I18n.t("administrator.roles.role_has_users", user_count: role.users.count)
|
|
||||||
return redirect_to admin_roles_path(selected_role: role.id)
|
|
||||||
elsif Role::RESERVED_ROLE_NAMES.include?(role) || role.provider != @user_domain ||
|
|
||||||
role.priority <= current_user.role.priority
|
|
||||||
return redirect_to admin_roles_path(selected_role: role.id)
|
|
||||||
else
|
|
||||||
role.role_permissions.delete_all
|
|
||||||
role.delete
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_to admin_roles_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def find_user
|
|
||||||
@user = User.find_by(uid: params[:user_uid])
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_deleted_user
|
|
||||||
@user = User.deleted.find_by(uid: params[:user_uid])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Verifies that admin is an administrator of the user in the action
|
|
||||||
def verify_admin_of_user
|
|
||||||
redirect_to admins_path,
|
|
||||||
flash: { alert: I18n.t("administrator.flash.unauthorized") } unless current_user.admin_of?(@user, "can_manage_users")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates the invite if it doesn't exist, or updates the updated_at time if it does
|
|
||||||
def create_or_update_invite(email)
|
|
||||||
invite = Invitation.find_by(email: email, provider: @user_domain)
|
|
||||||
|
|
||||||
# Invite already exists
|
|
||||||
if invite.present?
|
|
||||||
# Updates updated_at to now
|
|
||||||
invite.touch
|
|
||||||
else
|
|
||||||
# Creates invite
|
|
||||||
invite = Invitation.create(email: email, provider: @user_domain)
|
|
||||||
end
|
|
||||||
|
|
||||||
invite
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,8 +1,6 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
@ -16,301 +14,46 @@
|
||||||
# You should have received a copy of the GNU Lesser General Public License along
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
require 'bigbluebutton_api'
|
||||||
|
require 'digest/sha1'
|
||||||
|
|
||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
include BbbServer
|
protect_from_forgery with: :exception
|
||||||
include Errors
|
before_action :set_locale
|
||||||
|
MEETING_NAME_LIMIT = 200
|
||||||
|
USER_NAME_LIMIT = 100
|
||||||
|
|
||||||
before_action :block_unknown_hosts, :redirect_to_https, :set_user_domain, :set_user_settings, :maintenance_mode?,
|
def set_locale
|
||||||
:migration_error?, :user_locale, :check_admin_password, :check_user_role
|
I18n.locale = http_accept_language.language_region_compatible_from(I18n.available_locales)
|
||||||
|
end
|
||||||
|
|
||||||
protect_from_forgery with: :exceptions
|
|
||||||
|
|
||||||
# Retrieves the current user.
|
|
||||||
def current_user
|
def current_user
|
||||||
@current_user ||= User.includes(:role, :main_room).find_by(id: session[:user_id])
|
@current_user ||= User.find_by(id: session[:user_id])
|
||||||
|
|
||||||
if Rails.configuration.loadbalanced_configuration && (@current_user && !@current_user.has_role?(:super_admin) &&
|
|
||||||
@current_user.provider != @user_domain)
|
|
||||||
@current_user = nil
|
|
||||||
session.clear
|
|
||||||
end
|
|
||||||
|
|
||||||
@current_user
|
|
||||||
end
|
end
|
||||||
helper_method :current_user
|
helper_method :current_user
|
||||||
|
|
||||||
def bbb_server
|
|
||||||
@bbb_server ||= Rails.configuration.loadbalanced_configuration ? bbb(@user_domain) : bbb("greenlight")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Block unknown hosts to mitigate host header injection attacks
|
|
||||||
def block_unknown_hosts
|
|
||||||
return if Rails.configuration.hosts.blank?
|
|
||||||
raise UnsafeHostError, "#{request.host} is not a safe host" unless Rails.configuration.hosts.include?(request.host)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Force SSL
|
|
||||||
def redirect_to_https
|
|
||||||
if Rails.configuration.loadbalanced_configuration && request.headers["X-Forwarded-Proto"] == "http"
|
|
||||||
redirect_to protocol: "https://"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Sets the user domain variable
|
|
||||||
def set_user_domain
|
|
||||||
if Rails.env.test? || !Rails.configuration.loadbalanced_configuration
|
|
||||||
@user_domain = "greenlight"
|
|
||||||
else
|
|
||||||
@user_domain = parse_user_domain(request.host)
|
|
||||||
|
|
||||||
check_provider_exists
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Sets the settinfs variable
|
|
||||||
def set_user_settings
|
|
||||||
@settings = Setting.includes(:features).find_or_create_by(provider: @user_domain)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Redirects the user to a Maintenance page if turned on
|
|
||||||
def maintenance_mode?
|
|
||||||
if ENV["MAINTENANCE_MODE"] == "true"
|
|
||||||
render "errors/greenlight_error", status: 503, formats: :html,
|
|
||||||
locals: {
|
|
||||||
status_code: 503,
|
|
||||||
message: I18n.t("errors.maintenance.message"),
|
|
||||||
help: I18n.t("errors.maintenance.help"),
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
maintenance_string = @settings.get_value("Maintenance Banner").presence || Rails.configuration.maintenance_window
|
|
||||||
if maintenance_string.present? && cookies[:maintenance_window] != maintenance_string
|
|
||||||
flash.now[:maintenance] = maintenance_string
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Show an information page when migration fails and there is a version error.
|
|
||||||
def migration_error?
|
|
||||||
render :migration_error, status: 500 unless ENV["DB_MIGRATE_FAILED"].blank?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Determines proper locale to be used by calling user_locale with params based on if room owner exists
|
|
||||||
def determine_locale(user)
|
|
||||||
if user && user.language != 'default'
|
|
||||||
user.language
|
|
||||||
else
|
|
||||||
Rails.configuration.default_locale.presence || http_accept_language.language_region_compatible_from(I18n.available_locales)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Sets the appropriate locale.
|
|
||||||
def user_locale(user = current_user)
|
|
||||||
locale = determine_locale(user)
|
|
||||||
begin
|
|
||||||
I18n.locale = locale.tr('-', '_') unless locale.nil?
|
|
||||||
rescue
|
|
||||||
# Default to English if there are any issues in language
|
|
||||||
logger.error("Support: User locale is not supported (#{locale}")
|
|
||||||
I18n.locale = "en"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
helper_method :user_locale
|
|
||||||
|
|
||||||
# Checks to make sure that the admin has changed his password from the default
|
|
||||||
def check_admin_password
|
|
||||||
if current_user&.has_role?(:admin) && current_user.email == "admin@example.com" &&
|
|
||||||
current_user&.greenlight_account? && current_user&.authenticate(Rails.configuration.admin_password_default)
|
|
||||||
|
|
||||||
flash.now[:alert] = I18n.t("default_admin",
|
|
||||||
edit_link: change_password_path(user_uid: current_user.uid)).html_safe
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Checks if the user is banned and logs him out if he is
|
|
||||||
def check_user_role
|
|
||||||
if current_user&.has_role? :denied
|
|
||||||
session.delete(:user_id)
|
|
||||||
redirect_to root_path, flash: { alert: I18n.t("registration.banned.fail") }
|
|
||||||
elsif current_user&.has_role? :pending
|
|
||||||
session.delete(:user_id)
|
|
||||||
redirect_to root_path, flash: { alert: I18n.t("registration.approval.fail") }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Relative root helper (when deploying to subdirectory).
|
|
||||||
def relative_root
|
def relative_root
|
||||||
Rails.configuration.relative_url_root || ""
|
Rails.configuration.relative_url_root || ""
|
||||||
end
|
end
|
||||||
helper_method :relative_root
|
helper_method :relative_root
|
||||||
|
|
||||||
# Determines if the BigBlueButton endpoint is configured (or set to default).
|
def meeting_name_limit
|
||||||
|
MEETING_NAME_LIMIT
|
||||||
|
end
|
||||||
|
helper_method :meeting_name_limit
|
||||||
|
|
||||||
|
def user_name_limit
|
||||||
|
USER_NAME_LIMIT
|
||||||
|
end
|
||||||
|
helper_method :user_name_limit
|
||||||
|
|
||||||
def bigbluebutton_endpoint_default?
|
def bigbluebutton_endpoint_default?
|
||||||
return false if Rails.configuration.loadbalanced_configuration
|
|
||||||
Rails.configuration.bigbluebutton_endpoint_default == Rails.configuration.bigbluebutton_endpoint
|
Rails.configuration.bigbluebutton_endpoint_default == Rails.configuration.bigbluebutton_endpoint
|
||||||
end
|
end
|
||||||
helper_method :bigbluebutton_endpoint_default?
|
helper_method :bigbluebutton_endpoint_default?
|
||||||
|
|
||||||
def allow_greenlight_accounts?
|
def qrcode_generation_enabled?
|
||||||
return Rails.configuration.allow_user_signup unless Rails.configuration.loadbalanced_configuration
|
Rails.configuration.enable_qrcode_generation
|
||||||
return false unless @user_domain && !@user_domain.empty? && Rails.configuration.allow_user_signup
|
|
||||||
return false if @user_domain == "greenlight"
|
|
||||||
# Proceed with retrieving the provider info
|
|
||||||
begin
|
|
||||||
provider_info = retrieve_provider_info(@user_domain, 'api2', 'getUserGreenlightCredentials')
|
|
||||||
provider_info['provider'] == 'greenlight'
|
|
||||||
rescue => e
|
|
||||||
logger.error "Error in checking if greenlight accounts are allowed: #{e}"
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
helper_method :allow_greenlight_accounts?
|
|
||||||
|
|
||||||
# Determine if Greenlight is configured to allow user signups.
|
|
||||||
def allow_user_signup?
|
|
||||||
Rails.configuration.allow_user_signup
|
|
||||||
end
|
|
||||||
helper_method :allow_user_signup?
|
|
||||||
|
|
||||||
# Gets all configured omniauth providers.
|
|
||||||
def configured_providers
|
|
||||||
Rails.configuration.providers.select do |provider|
|
|
||||||
Rails.configuration.send("omniauth_#{provider}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
helper_method :configured_providers
|
|
||||||
|
|
||||||
# Indicates whether users are allowed to share rooms
|
|
||||||
def shared_access_allowed
|
|
||||||
@settings.get_value("Shared Access") == "true"
|
|
||||||
end
|
|
||||||
helper_method :shared_access_allowed
|
|
||||||
|
|
||||||
# Indicates whether users should consent recoding when joining rooms
|
|
||||||
def recording_consent_required?
|
|
||||||
@settings.get_value("Require Recording Consent") == "true"
|
|
||||||
end
|
|
||||||
helper_method :recording_consent_required?
|
|
||||||
|
|
||||||
# Indicates whether users are allowed to add moderator access codes to rooms
|
|
||||||
def moderator_code_allowed?
|
|
||||||
@settings.get_value("Room Configuration Moderator Access Codes") == "optional"
|
|
||||||
end
|
|
||||||
helper_method :moderator_code_allowed?
|
|
||||||
|
|
||||||
# Returns a list of allowed file types
|
|
||||||
def allowed_file_types
|
|
||||||
Rails.configuration.allowed_file_types
|
|
||||||
end
|
|
||||||
helper_method :allowed_file_types
|
|
||||||
|
|
||||||
# Allows admins to edit a user's details
|
|
||||||
def can_edit_user?(user_to_edit, editting_user)
|
|
||||||
return user_to_edit.greenlight_account? if user_to_edit == editting_user
|
|
||||||
|
|
||||||
editting_user.admin_of?(user_to_edit, "can_manage_users")
|
|
||||||
end
|
|
||||||
helper_method :can_edit_user?
|
|
||||||
|
|
||||||
# Returns the page that the logo redirects to when clicked on
|
|
||||||
def home_page
|
|
||||||
return admins_path if current_user.has_role? :super_admin
|
|
||||||
return current_user.main_room if current_user.role.get_permission("can_create_rooms")
|
|
||||||
cant_create_rooms_path
|
|
||||||
end
|
|
||||||
helper_method :home_page
|
|
||||||
|
|
||||||
# Parses the url for the user domain
|
|
||||||
def parse_user_domain(hostname)
|
|
||||||
return hostname.split('.').first if Rails.configuration.url_host.empty?
|
|
||||||
Rails.configuration.url_host.split(',').each do |url_host|
|
|
||||||
return hostname.chomp(url_host).chomp('.') if hostname.include?(url_host)
|
|
||||||
end
|
|
||||||
''
|
|
||||||
end
|
|
||||||
|
|
||||||
# Include user domain in lograge logs
|
|
||||||
def append_info_to_payload(payload)
|
|
||||||
super
|
|
||||||
payload[:host] = @user_domain
|
|
||||||
end
|
|
||||||
|
|
||||||
# Manually handle BigBlueButton errors
|
|
||||||
rescue_from BigBlueButton::BigBlueButtonException do |ex|
|
|
||||||
logger.error "BigBlueButtonException: #{ex}"
|
|
||||||
render "errors/bigbluebutton_error"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Manually deal with 401 errors
|
|
||||||
rescue_from CanCan::AccessDenied do |_exception|
|
|
||||||
if current_user
|
|
||||||
render "errors/greenlight_error"
|
|
||||||
else
|
|
||||||
# Store the current url as a cookie to redirect to after sigining in
|
|
||||||
cookies[:return_to] = request.url
|
|
||||||
|
|
||||||
# Get the correct signin path
|
|
||||||
path = if allow_greenlight_accounts?
|
|
||||||
signin_path
|
|
||||||
elsif Rails.configuration.loadbalanced_configuration
|
|
||||||
"#{Rails.configuration.relative_url_root}/auth/bn_launcher"
|
|
||||||
else
|
|
||||||
signin_path
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_to path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def check_provider_exists
|
|
||||||
# Checks to see if the user exists
|
|
||||||
begin
|
|
||||||
# Check if the session has already checked that the user exists
|
|
||||||
# and return true if they did for this domain
|
|
||||||
return if session[:provider_exists] == @user_domain
|
|
||||||
|
|
||||||
retrieve_provider_info(@user_domain, 'api2', 'getUserGreenlightCredentials')
|
|
||||||
|
|
||||||
# Add a session variable if the provider exists
|
|
||||||
session[:provider_exists] = @user_domain
|
|
||||||
rescue => e
|
|
||||||
logger.error "Error in retrieve provider info: #{e}"
|
|
||||||
@hide_signin = true
|
|
||||||
case e.message
|
|
||||||
when "No user with that id exists"
|
|
||||||
set_default_settings
|
|
||||||
|
|
||||||
render "errors/greenlight_error", locals: { message: I18n.t("errors.not_found.user_not_found.message"),
|
|
||||||
help: I18n.t("errors.not_found.user_not_found.help") }
|
|
||||||
when "Provider not included."
|
|
||||||
set_default_settings
|
|
||||||
|
|
||||||
render "errors/greenlight_error", locals: { message: I18n.t("errors.not_found.user_missing.message"),
|
|
||||||
help: I18n.t("errors.not_found.user_missing.help") }
|
|
||||||
when "That user has no configured provider."
|
|
||||||
if Setting.exists?(provider: @user_domain)
|
|
||||||
# Keep the branding
|
|
||||||
@settings = Setting.find_by(provider: @user_domain)
|
|
||||||
else
|
|
||||||
set_default_settings
|
|
||||||
end
|
|
||||||
|
|
||||||
render "errors/greenlight_error", locals: { status_code: 501,
|
|
||||||
message: I18n.t("errors.no_provider.message"),
|
|
||||||
help: I18n.t("errors.no_provider.help") }
|
|
||||||
else
|
|
||||||
set_default_settings
|
|
||||||
|
|
||||||
render "errors/greenlight_error", locals: { status_code: 500, message: I18n.t("errors.internal.message"),
|
|
||||||
help: I18n.t("errors.internal.help"), display_back: true }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_default_settings
|
|
||||||
# Use the default site settings
|
|
||||||
@user_domain = "greenlight"
|
|
||||||
@settings = Setting.find_or_create_by(provider: @user_domain)
|
|
||||||
end
|
end
|
||||||
|
helper_method :qrcode_generation_enabled?
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,383 @@
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
class BbbController < ApplicationController
|
||||||
|
include BbbApi
|
||||||
|
|
||||||
|
before_action :authorize_recording_owner!, only: [:update_recordings, :delete_recordings]
|
||||||
|
before_action :load_and_authorize_room_owner!, only: [:end]
|
||||||
|
|
||||||
|
skip_before_action :verify_authenticity_token, only: :callback
|
||||||
|
|
||||||
|
# GET /:resource/:id/join
|
||||||
|
# GET /:resource/:room_id/:id/join
|
||||||
|
def join
|
||||||
|
if params[:name].blank?
|
||||||
|
return render_bbb_response(
|
||||||
|
messageKey: "missing_parameter",
|
||||||
|
message: "user name was not included",
|
||||||
|
status: :unprocessable_entity
|
||||||
|
)
|
||||||
|
elsif params[:name].size > user_name_limit
|
||||||
|
return render_bbb_response(
|
||||||
|
messageKey: "invalid_parameter",
|
||||||
|
message: "user name is too long",
|
||||||
|
status: :unprocessable_entity
|
||||||
|
)
|
||||||
|
elsif params[:id].size > meeting_name_limit
|
||||||
|
return render_bbb_response(
|
||||||
|
messageKey: "invalid_parameter",
|
||||||
|
message: "meeting name is too long",
|
||||||
|
status: :unprocessable_entity
|
||||||
|
)
|
||||||
|
else
|
||||||
|
if params[:room_id]
|
||||||
|
user = User.find_by encrypted_id: params[:room_id]
|
||||||
|
if !user
|
||||||
|
return render_bbb_response(
|
||||||
|
messageKey: "not_found",
|
||||||
|
message: "User Not Found",
|
||||||
|
status: :not_found
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
meeting_id = "#{params[:room_id]}-#{params[:id]}"
|
||||||
|
meeting_name = params[:id]
|
||||||
|
meeting_path = "#{URI.encode(params[:room_id])}/#{URI.encode(params[:id]).gsub('/','%2F')}"
|
||||||
|
else
|
||||||
|
user = User.find_by encrypted_id: params[:id]
|
||||||
|
meeting_id = params[:id]
|
||||||
|
meeting_path = URI.encode(meeting_id).gsub('/','%2F')
|
||||||
|
end
|
||||||
|
|
||||||
|
options = if user
|
||||||
|
{
|
||||||
|
wait_for_moderator: true,
|
||||||
|
meeting_recorded: true,
|
||||||
|
meeting_name: meeting_name,
|
||||||
|
room_owner: params[:room_id],
|
||||||
|
user_is_moderator: current_user == user
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
user_is_moderator: true
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
base_url = "#{request.base_url}#{relative_root}/#{params[:resource]}/#{meeting_path}"
|
||||||
|
options[:meeting_logout_url] = base_url
|
||||||
|
options[:hook_url] = "#{base_url}/callback"
|
||||||
|
options[:moderator_message] = t('moderator_default_message', url: "<a href=\"#{base_url}\" target=\"_blank\"><u>#{base_url}</u></a>")
|
||||||
|
|
||||||
|
bbb_res = bbb_join_url(
|
||||||
|
meeting_id,
|
||||||
|
params[:name],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
|
||||||
|
# the user can join the meeting
|
||||||
|
if user
|
||||||
|
if bbb_res[:returncode] && current_user == user
|
||||||
|
JoinMeetingJob.perform_later(user, params[:id], base_url)
|
||||||
|
WaitingList.empty(options[:room_owner], options[:meeting_name])
|
||||||
|
# the user can't join because they are on mobile and HTML5 is not enabled.
|
||||||
|
elsif bbb_res[:messageKey] == 'unable_to_join'
|
||||||
|
NotifyUserCantJoinJob.perform_later(user.encrypted_id, params[:id], params[:name])
|
||||||
|
# user will be waiting for a moderator
|
||||||
|
else
|
||||||
|
NotifyUserWaitingJob.perform_later(user.encrypted_id, params[:id], params[:name])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
render_bbb_response bbb_res, bbb_res[:response]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /:resource/:room_id/:id/callback
|
||||||
|
# Endpoint for webhook calls from BigBlueButton
|
||||||
|
def callback
|
||||||
|
# respond with 200 anyway so BigBlueButton knows the hook call was ok
|
||||||
|
# but abort execution
|
||||||
|
head(:ok) && return unless validate_checksum
|
||||||
|
|
||||||
|
begin
|
||||||
|
event = params['event']
|
||||||
|
data = event.is_a?(String) ? JSON.parse(params['event']) : event
|
||||||
|
treat_callback_event(data)
|
||||||
|
rescue Exception => e
|
||||||
|
logger.error "Error parsing webhook data. Data: #{data}, exception: #{e.inspect}"
|
||||||
|
|
||||||
|
# respond with 200 anyway so BigBlueButton knows the hook call was ok
|
||||||
|
head(:ok) && return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# DELETE /rooms/:room_id/:id/end
|
||||||
|
def end
|
||||||
|
load_and_authorize_room_owner!
|
||||||
|
|
||||||
|
bbb_res = bbb_end_meeting "#{@user.encrypted_id}-#{params[:id]}"
|
||||||
|
if bbb_res[:returncode] || bbb_res[:status] == :not_found
|
||||||
|
EndMeetingJob.perform_later(@user.encrypted_id, params[:id])
|
||||||
|
bbb_res[:status] = :ok
|
||||||
|
end
|
||||||
|
render_bbb_response bbb_res
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /rooms/:room_id/recordings
|
||||||
|
# GET /rooms/:room_id/:id/recordings
|
||||||
|
def recordings
|
||||||
|
load_room!
|
||||||
|
|
||||||
|
# bbb_res = bbb_get_recordings "#{@user.encrypted_id}-#{params[:id]}"
|
||||||
|
options = { "meta_room-id": @user.encrypted_id }
|
||||||
|
if params[:id]
|
||||||
|
options["meta_meeting-name"] = params[:id]
|
||||||
|
end
|
||||||
|
bbb_res = bbb_get_recordings(options)
|
||||||
|
render_bbb_response bbb_res, bbb_res[:recordings]
|
||||||
|
end
|
||||||
|
|
||||||
|
# PATCH /rooms/:room_id/recordings/:record_id
|
||||||
|
# PATCH /rooms/:room_id/:id/recordings/:record_id
|
||||||
|
def update_recordings
|
||||||
|
published = params[:published] == 'true'
|
||||||
|
metadata = params.select{ |k, v| k.match(/^meta_/) }
|
||||||
|
bbb_res = bbb_update_recordings(params[:record_id], published, metadata)
|
||||||
|
if bbb_res[:returncode]
|
||||||
|
RecordingUpdatesJob.perform_later(@user.encrypted_id, params[:record_id])
|
||||||
|
end
|
||||||
|
render_bbb_response bbb_res
|
||||||
|
end
|
||||||
|
|
||||||
|
# DELETE /rooms/:room_id/recordings/:record_id
|
||||||
|
# DELETE /rooms/:room_id/:id/recordings/:record_id
|
||||||
|
def delete_recordings
|
||||||
|
recording = bbb_get_recordings({recordID: params[:record_id]})[:recordings].first
|
||||||
|
bbb_res = bbb_delete_recordings(params[:record_id])
|
||||||
|
if bbb_res[:returncode]
|
||||||
|
RecordingDeletesJob.perform_later(@user.encrypted_id, params[:record_id], recording[:metadata][:"meeting-name"])
|
||||||
|
end
|
||||||
|
render_bbb_response bbb_res
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /rooms/:room_id/recordings/:record_id
|
||||||
|
# POST /rooms/:room_id/:id/recordings/:record_id
|
||||||
|
def youtube_publish
|
||||||
|
# If we can't get the client, then they don't have a Youtube account.
|
||||||
|
begin
|
||||||
|
client = Yt::Account.new(access_token: current_user.token)
|
||||||
|
video = client.upload_video(get_webcams_url(params[:record_id]),
|
||||||
|
title: params[:video_title],
|
||||||
|
description: t('youtube_description', url: 'https://bigbluebutton.org/'),
|
||||||
|
privacy_status: params[:privacy_status])
|
||||||
|
rescue => e
|
||||||
|
errors = e.response_body['error']['errors']
|
||||||
|
# Many complications, start by getting them to refresh their token.
|
||||||
|
if errors.length > 1
|
||||||
|
redirect_url = user_login_url
|
||||||
|
else
|
||||||
|
error = errors[0]
|
||||||
|
if error['message'] == "Unauthorized"
|
||||||
|
redirect_url = 'https://m.youtube.com/create_channel'
|
||||||
|
else
|
||||||
|
# In this case, they don't have a youtube channel connected to their account, so prompt to create one.
|
||||||
|
redirect_url = user_login_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
render json: {:url => redirect_url}
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /rooms/:room_id/recordings/can_upload
|
||||||
|
def can_upload
|
||||||
|
# The recording is uploadable if it contains webcam data and they are logged in through Google.
|
||||||
|
if Rails.configuration.enable_youtube_uploading == false then
|
||||||
|
uploadable = 'uploading_disabled'
|
||||||
|
elsif current_user.provider != 'google'
|
||||||
|
uploadable = 'invalid_provider'
|
||||||
|
else
|
||||||
|
uploadable = (Faraday.head(get_webcams_url(params[:rec_id])).status == 200 && current_user.provider == 'google').to_s
|
||||||
|
end
|
||||||
|
render json: {:uploadable => uploadable}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_webcams_url(recording_id)
|
||||||
|
uri = URI.parse(Rails.configuration.bigbluebutton_endpoint)
|
||||||
|
uri.scheme + '://' + uri.host + '/presentation/' + recording_id + '/video/webcams.webm'
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_room!
|
||||||
|
@user = User.find_by encrypted_id: params[:room_id]
|
||||||
|
if !@user
|
||||||
|
render head(:not_found) && return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_and_authorize_room_owner!
|
||||||
|
load_room!
|
||||||
|
|
||||||
|
if !current_user || current_user != @user
|
||||||
|
render head(:unauthorized) && return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorize_recording_owner!
|
||||||
|
load_and_authorize_room_owner!
|
||||||
|
|
||||||
|
recordings = bbb_get_recordings({recordID: params[:record_id]})[:recordings]
|
||||||
|
recordings.each do |recording|
|
||||||
|
if recording[:recordID] == params[:record_id]
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
render head(:not_found) && return
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_bbb_response(bbb_res, response={})
|
||||||
|
@messageKey = bbb_res[:messageKey]
|
||||||
|
@message = bbb_res[:message]
|
||||||
|
@status = bbb_res[:status]
|
||||||
|
@response = response
|
||||||
|
render status: @status
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_body(request)
|
||||||
|
request.body.read.force_encoding("UTF-8")
|
||||||
|
end
|
||||||
|
|
||||||
|
def treat_callback_event(event)
|
||||||
|
# Check if the event is a BigBlueButton 2.0 event.
|
||||||
|
if event.has_key?('envelope')
|
||||||
|
eventName = (event.present? && event['envelope'].present?) ? event['envelope']['name'] : nil
|
||||||
|
else # The event came from BigBlueButton 1.1 (or earlier).
|
||||||
|
eventName = (event.present? && event['header'].present?) ? event['header']['name'] : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# a recording is ready
|
||||||
|
if eventName == "publish_ended"
|
||||||
|
if event['payload'] && event['payload']['metadata'] && event['payload']['meeting_id']
|
||||||
|
token = event['payload']['metadata'][META_TOKEN]
|
||||||
|
room_id = event['payload']['metadata']['room-id']
|
||||||
|
record_id = event['payload']['meeting_id']
|
||||||
|
duration_data = event['payload']['duration']
|
||||||
|
|
||||||
|
# the webhook event doesn't have all the data we need, so we need
|
||||||
|
# to send a getRecordings anyway
|
||||||
|
# TODO: if the webhooks included all data in the event we wouldn't need this
|
||||||
|
rec_info = bbb_get_recordings({recordID: record_id})
|
||||||
|
rec_info = rec_info[:recordings].first
|
||||||
|
RecordingCreatedJob.perform_later(token, room_id, parse_recording_for_view(rec_info))
|
||||||
|
|
||||||
|
rec_info[:duration] = duration_data.to_json
|
||||||
|
|
||||||
|
# send an email to the owner of this recording, if defined
|
||||||
|
if Rails.configuration.mail_notifications
|
||||||
|
owner = User.find_by(encrypted_id: room_id)
|
||||||
|
RecordingReadyEmailJob.perform_later(owner, parse_recording_for_view(rec_info)) if owner.present?
|
||||||
|
end
|
||||||
|
else
|
||||||
|
logger.error "Bad format for event #{event}, won't process"
|
||||||
|
end
|
||||||
|
elsif eventName == "meeting_created_message" || eventName == "MeetingCreatedEvtMsg"
|
||||||
|
# Fire an Actioncable event that updates _previously_joined for the client.
|
||||||
|
actioncable_event('create')
|
||||||
|
elsif eventName == "meeting_destroyed_event" || eventName == "MeetingEndedEvtMsg"
|
||||||
|
actioncable_event('destroy')
|
||||||
|
|
||||||
|
# Since the meeting is destroyed we have no way get the callback url to remove the meeting, so we must build it.
|
||||||
|
remove_url = build_callback_url(params[:id], params[:room_id])
|
||||||
|
|
||||||
|
# Remove webhook for the meeting.
|
||||||
|
webhook_remove(remove_url)
|
||||||
|
elsif eventName == "user_joined_message"
|
||||||
|
actioncable_event('join', {user_id: event['payload']['user']['extern_userid'], user: event['payload']['user']['name'], role: event['payload']['user']['role']})
|
||||||
|
elsif eventName == "UserJoinedMeetingEvtMsg"
|
||||||
|
actioncable_event('join', {user_id: event['core']['body']['intId'], user: event['core']['body']['name'], role: event['core']['body']['role']})
|
||||||
|
elsif eventName == "user_left_message"
|
||||||
|
actioncable_event('leave', {user_id: event['payload']['user']['extern_userid']})
|
||||||
|
elsif eventName == "UserLeftMeetingEvtMsg"
|
||||||
|
actioncable_event('leave', {user_id: event['core']['body']['intId']})
|
||||||
|
else
|
||||||
|
logger.info "Callback event will not be treated. Event name: #{eventName}"
|
||||||
|
end
|
||||||
|
|
||||||
|
render head(:ok) && return
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_callback_url(id, room_id)
|
||||||
|
"#{request.base_url}#{relative_root}/rooms/#{room_id}/#{URI.encode(id)}/callback"
|
||||||
|
end
|
||||||
|
|
||||||
|
def actioncable_event(method, data = {})
|
||||||
|
data = {method: method, meeting: params[:id], room: params[:room_id]}.merge(data)
|
||||||
|
ActionCable.server.broadcast('refresh_meetings', data)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Validates the checksum received in a callback call.
|
||||||
|
# If the checksum doesn't match, renders an ok and aborts execution.
|
||||||
|
def validate_checksum
|
||||||
|
secret = ENV['BIGBLUEBUTTON_SECRET']
|
||||||
|
checksum = params["checksum"]
|
||||||
|
return false unless checksum
|
||||||
|
|
||||||
|
# Message is only encoded if it comes from the bbb-webhooks node application.
|
||||||
|
# The post process script does not encode it's response body.
|
||||||
|
begin
|
||||||
|
# Decode and break the body into parts.
|
||||||
|
parts = URI.decode_www_form(read_body(request))
|
||||||
|
|
||||||
|
# Convert the data into the correct checksum format, replace ruby hash arrows.
|
||||||
|
converted_data = {parts[0][0]=>parts[0][1],parts[1][0]=>parts[1][1].to_i}.to_s.gsub!('=>', ':')
|
||||||
|
|
||||||
|
# Manually remove the space between the two elements.
|
||||||
|
converted_data[converted_data.rindex("timestamp") - 2] = ''
|
||||||
|
callback_url = uri_remove_param(request.original_url, "checksum")
|
||||||
|
checksum_str = "#{callback_url}#{converted_data}#{secret}"
|
||||||
|
rescue
|
||||||
|
# Data was not recieved encoded (sent from post process script).
|
||||||
|
data = read_body(request)
|
||||||
|
callback_url = uri_remove_param(request.original_url, "checksum")
|
||||||
|
checksum_str = "#{callback_url}#{data}#{secret}"
|
||||||
|
end
|
||||||
|
|
||||||
|
calculated_checksum = Digest::SHA1.hexdigest(checksum_str)
|
||||||
|
|
||||||
|
if calculated_checksum != checksum
|
||||||
|
logger.error "Checksum did not match. Calculated: #{calculated_checksum}, received: #{checksum}"
|
||||||
|
false
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Removes parameters from an URI
|
||||||
|
def uri_remove_param(uri, params = nil)
|
||||||
|
return uri unless params
|
||||||
|
params = Array(params)
|
||||||
|
uri_parsed = URI.parse(uri)
|
||||||
|
return uri unless uri_parsed.query
|
||||||
|
new_params = uri_parsed.query.gsub(/&/, '&').split('&').reject { |q| params.include?(q.split('=').first) }
|
||||||
|
uri = uri.split('?').first
|
||||||
|
if new_params.count > 0
|
||||||
|
"#{uri}?#{new_params.join('&')}"
|
||||||
|
else
|
||||||
|
uri
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,130 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
# Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
# version.
|
|
||||||
#
|
|
||||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
module Authenticator
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
|
|
||||||
# Logs a user into GreenLight.
|
|
||||||
def login(user)
|
|
||||||
migrate_twitter_user(user)
|
|
||||||
|
|
||||||
session[:user_id] = user.id
|
|
||||||
user.update(last_login: Time.zone.now)
|
|
||||||
|
|
||||||
logger.info("Support: #{user.email} has successfully logged in.")
|
|
||||||
|
|
||||||
# If there are not terms, or the user has accepted them, check for email verification
|
|
||||||
if !Rails.configuration.terms || user.accepted_terms
|
|
||||||
check_email_verified(user)
|
|
||||||
else
|
|
||||||
redirect_to terms_path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# If email verification is disabled, or the user has verified, go to their room
|
|
||||||
def check_email_verified(user)
|
|
||||||
# Admin users should be redirected to the admin page
|
|
||||||
if user.has_role? :super_admin
|
|
||||||
redirect_to admins_path
|
|
||||||
elsif user.activated?
|
|
||||||
# Dont redirect to any of these urls
|
|
||||||
dont_redirect_to = [root_url, signin_url, ldap_signin_url, ldap_callback_url, signup_url, unauthorized_url,
|
|
||||||
internal_error_url, not_found_url]
|
|
||||||
|
|
||||||
unless ENV['OAUTH2_REDIRECT'].nil?
|
|
||||||
dont_redirect_to.push(File.join(ENV['OAUTH2_REDIRECT'], "auth", "openid_connect", "callback"))
|
|
||||||
end
|
|
||||||
|
|
||||||
url = if cookies[:return_to] && !dont_redirect_to.include?(cookies[:return_to])
|
|
||||||
cookies[:return_to]
|
|
||||||
elsif user.role.get_permission("can_create_rooms")
|
|
||||||
user.main_room
|
|
||||||
else
|
|
||||||
cant_create_rooms_path
|
|
||||||
end
|
|
||||||
|
|
||||||
# Delete the cookie if it exists
|
|
||||||
cookies.delete :return_to if cookies[:return_to]
|
|
||||||
|
|
||||||
redirect_to url
|
|
||||||
else
|
|
||||||
session[:user_id] = nil
|
|
||||||
user.create_activation_token
|
|
||||||
redirect_to account_activation_path(digest: user.activation_digest)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def ensure_unauthenticated_except_twitter
|
|
||||||
redirect_to current_user.main_room || root_path if current_user && params[:old_twitter_user_id].nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Logs current user out of GreenLight.
|
|
||||||
def logout
|
|
||||||
session.delete(:user_id) if current_user
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if the user is using local accounts
|
|
||||||
def auth_changed_to_local?(user)
|
|
||||||
Rails.configuration.loadbalanced_configuration && user.social_uid.present? && allow_greenlight_accounts?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if the user exists under the same email with no social uid and that social accounts are allowed
|
|
||||||
def auth_changed_to_social?(email)
|
|
||||||
Rails.configuration.loadbalanced_configuration &&
|
|
||||||
User.exists?(email: email, provider: @user_domain, social_uid: nil) &&
|
|
||||||
!allow_greenlight_accounts?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Sets the initial user role based on the email mapping
|
|
||||||
def initial_user_role(email)
|
|
||||||
mapping = @settings.get_value("Email Mapping")
|
|
||||||
return "user" unless mapping.present?
|
|
||||||
|
|
||||||
mapping.split(",").each do |map|
|
|
||||||
email_role = map.split("=")
|
|
||||||
return email_role[1] if email.ends_with?(email_role[0])
|
|
||||||
end
|
|
||||||
|
|
||||||
"user" # default to user if role not found
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Migrates all of the twitter users rooms to the new account
|
|
||||||
def migrate_twitter_user(user)
|
|
||||||
if !session["old_twitter_user_id"].nil? && user.provider != "twitter"
|
|
||||||
old_user = User.find(session["old_twitter_user_id"])
|
|
||||||
|
|
||||||
old_user.rooms.each do |room|
|
|
||||||
room.owner = user
|
|
||||||
|
|
||||||
room.name = "Old #{room.name}" if room.id == old_user.main_room.id
|
|
||||||
|
|
||||||
room.save!
|
|
||||||
end
|
|
||||||
|
|
||||||
# Query for the old user again so the migrated rooms don't get deleted
|
|
||||||
old_user.reload
|
|
||||||
old_user.destroy!
|
|
||||||
|
|
||||||
session["old_twitter_user_id"] = nil
|
|
||||||
|
|
||||||
flash[:success] = I18n.t("registration.deprecated.merge_success")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,146 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
# Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
# version.
|
|
||||||
#
|
|
||||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
require 'bigbluebutton_api'
|
|
||||||
|
|
||||||
module BbbServer
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
include BbbApi
|
|
||||||
|
|
||||||
META_LISTED = "gl-listed"
|
|
||||||
|
|
||||||
# Checks if a room is running on the BigBlueButton server.
|
|
||||||
def room_running?(bbb_id)
|
|
||||||
bbb_server.is_meeting_running?(bbb_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns a list of all running meetings
|
|
||||||
def all_running_meetings
|
|
||||||
bbb_server.get_meetings
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_recordings(meeting_id)
|
|
||||||
bbb_server.get_recordings(meetingID: meeting_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_multiple_recordings(meeting_ids)
|
|
||||||
bbb_server.get_recordings(meetingID: meeting_ids)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns a URL to join a user into a meeting.
|
|
||||||
def join_path(room, name, options = {}, uid = nil)
|
|
||||||
# Create the meeting, even if it's running
|
|
||||||
start_session(room, options)
|
|
||||||
|
|
||||||
# Determine the password to use when joining.
|
|
||||||
password = options[:user_is_moderator] ? room.moderator_pw : room.attendee_pw
|
|
||||||
|
|
||||||
# Generate the join URL.
|
|
||||||
join_opts = {}
|
|
||||||
join_opts[:userID] = uid if uid
|
|
||||||
join_opts[:join_via_html5] = true
|
|
||||||
join_opts[:avatarURL] = options[:avatarURL] if options[:avatarURL].present?
|
|
||||||
join_opts[:createTime] = room.last_session.to_datetime.strftime("%Q") if room.last_session
|
|
||||||
|
|
||||||
bbb_server.join_meeting_url(room.bbb_id, name, password, join_opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a meeting on the BigBlueButton server.
|
|
||||||
def start_session(room, options = {})
|
|
||||||
create_options = {
|
|
||||||
record: options[:record].to_s,
|
|
||||||
logoutURL: options[:meeting_logout_url] || '',
|
|
||||||
moderatorPW: room.moderator_pw,
|
|
||||||
attendeePW: room.attendee_pw,
|
|
||||||
moderatorOnlyMessage: options[:moderator_message],
|
|
||||||
"meta_#{META_LISTED}": options[:recording_default_visibility] || false,
|
|
||||||
"meta_bbb-origin-version": Greenlight::Application::VERSION,
|
|
||||||
"meta_bbb-origin": "Greenlight",
|
|
||||||
"meta_bbb-origin-server-name": options[:host]
|
|
||||||
}
|
|
||||||
|
|
||||||
create_options[:muteOnStart] = options[:mute_on_start] if options[:mute_on_start]
|
|
||||||
create_options[:guestPolicy] = "ASK_MODERATOR" if options[:require_moderator_approval]
|
|
||||||
|
|
||||||
# Send the create request.
|
|
||||||
begin
|
|
||||||
meeting = if room.presentation.attached?
|
|
||||||
modules = BigBlueButton::BigBlueButtonModules.new
|
|
||||||
url = rails_blob_url(room.presentation).gsub("&", "%26")
|
|
||||||
logger.info("Support: Room #{room.uid} starting using presentation: #{url}")
|
|
||||||
modules.add_presentation(:url, url)
|
|
||||||
bbb_server.create_meeting(room.name, room.bbb_id, create_options, modules)
|
|
||||||
else
|
|
||||||
bbb_server.create_meeting(room.name, room.bbb_id, create_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
unless meeting[:messageKey] == 'duplicateWarning'
|
|
||||||
room.update_attributes(sessions: room.sessions + 1, last_session: DateTime.strptime(meeting[:createTime].to_s, "%Q"))
|
|
||||||
end
|
|
||||||
rescue BigBlueButton::BigBlueButtonException => e
|
|
||||||
puts "BigBlueButton failed on create: #{e.key}: #{e.message}"
|
|
||||||
raise e
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Gets the number of recordings for this room
|
|
||||||
def recording_count(bbb_id)
|
|
||||||
bbb_server.get_recordings(meetingID: bbb_id)[:recordings].length
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update a recording from a room
|
|
||||||
def update_recording(record_id, meta)
|
|
||||||
meta[:recordID] = record_id
|
|
||||||
bbb_server.send_api_request("updateRecordings", meta)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update a recording from a room
|
|
||||||
def publish_recording(record_id)
|
|
||||||
bbb_server.publish_recordings(record_id, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update a recording from a room
|
|
||||||
def unpublish_recording(record_id)
|
|
||||||
bbb_server.publish_recordings(record_id, false)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Protect a recording
|
|
||||||
def protect_recording(record_id, meta = {})
|
|
||||||
meta[:recordID] = record_id
|
|
||||||
meta[:protect] = true
|
|
||||||
bbb_server.send_api_request("updateRecordings", meta)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Unprotect a recording
|
|
||||||
def unprotect_recording(record_id, meta = {})
|
|
||||||
meta[:recordID] = record_id
|
|
||||||
meta[:protect] = false
|
|
||||||
bbb_server.send_api_request("updateRecordings", meta)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Deletes a recording from a room.
|
|
||||||
def delete_recording(record_id)
|
|
||||||
bbb_server.delete_recordings(record_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Deletes all recordings associated with the room.
|
|
||||||
def delete_all_recordings(bbb_id)
|
|
||||||
record_ids = bbb_server.get_recordings(meetingID: bbb_id)[:recordings].pluck(:recordID)
|
|
||||||
bbb_server.delete_recordings(record_ids) unless record_ids.empty?
|
|
||||||
end
|
|
||||||
end
|
|