first commit

This commit is contained in:
zhongjin
2023-02-13 23:17:00 +08:00
commit bc432f769a
397 changed files with 53066 additions and 0 deletions

29
.dockerignore Normal file
View File

@@ -0,0 +1,29 @@
# git
.git
.gitignore
README.md
SECURITY.md
.github
# Rails
.env
*.rbc
capybara-*.html
.rspec
log
tmp
/db/**/*.sqlite3
/db/**/*.sqlite3-journal
/db/production
/db/production-postgres
public/assets
public/b
coverage/
.rvmrc
vendor/bundle
.bundle
Dockerfile
.gitlab-ci.yml
.rubocop.yml
spec
test

6
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
blank_issues_enabled: false
contact_links:
- name: Greenlight Setup
url: https://groups.google.com/g/bigbluebutton-greenlight
about: Get help with the installation, setup and configuration of Greenlight.

View File

@@ -0,0 +1,24 @@
---
name: Feature Request
about: Suggest an idea for Greenlight
title: ''
labels: 'feature request'
assignees: ''
---
<!--PLEASE DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
This issue tracker is only for Greenlight related issues.
Search for existing feature requests to avoid creating duplicates.-->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -0,0 +1,34 @@
---
name: Report a Bug
about: Template for creating an issue with Greenlight
title: ''
labels: 'bug'
assignees: ''
---
<!--PLEASE DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
This issue tracker is only for Greenlight related issues.
Search for existing issues to avoid creating duplicates.-->
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Actual behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

16
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,16 @@
<!---
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. -->

View File

@@ -0,0 +1,95 @@
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

105
.github/workflows/ci.build.push.yml vendored Normal file
View File

@@ -0,0 +1,105 @@
env:
RUBY_VERSION: 2.7
name: CI Build Push
on:
push:
branches-ignore:
- v1
- v2
- translations*
- snyk*
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') && github.ref != 'refs/heads/master'
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 with amazonlinux
- name: Alternate Amazon Linux Build and Push
if: contains(env.DOCKER_BUILD_ENABLED, 'true') && contains(env.DOCKER_BUILD_ALTERNATE_ENABLED, 'true') && github.ref != 'refs/heads/master'
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

110
.github/workflows/ci.build.release.yml vendored Normal file
View File

@@ -0,0 +1,110 @@
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 Version
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: 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_version.outputs.tag}} | 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_version.outputs.tag}} | 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 }}:v${{ steps.ci_tag_release_version.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=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 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 }}: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 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 }}: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

71
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,71 @@
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
- v1
- v2
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
- name: Run RSpec
run: bundle exec rspec

26
.github/workflows/mirror.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Mirroring
on: [push, delete]
jobs:
to_codecommit:
runs-on: ubuntu-latest
env:
CODECOMMIT_MIRROR_ENABLED: ${{ secrets.CODECOMMIT_MIRROR_ENABLED }}
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: CodeCommit Mirroring
if: contains(env.CODECOMMIT_MIRROR_ENABLED, 'true')
uses: pixta-dev/repository-mirroring-action@v1
with:
target_repo_url:
${{ secrets.CODECOMMIT_TARGET_REPO_URL }}
ssh_private_key:
${{ secrets.CODECOMMIT_SSH_PRIVATE_KEY }}
ssh_username:
${{ secrets.CODECOMMIT_SSH_PRIVATE_KEY_ID }}

53
.gitignore vendored Normal file
View File

@@ -0,0 +1,53 @@
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'
# Ignore bundler config.
/.bundle
# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal
# Ignore static assets.
/public/system/**
/public/assets/**
/public/b/**
# Ignore uploaded files
/storage
# Ignore production paths.
/db/production
/db/production-postgres
# Ignore all logfiles and tempfiles.
/log/*
/tmp
!/log/.keep
# Ignore Byebug command history file.
.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
# Needed for rspec's --only-failures command
spec/failures.txt

71
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,71 @@
stages:
- test
- build
- deploy
before_script:
test:
stage: test
image: ruby:2.5
script:
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
- bundle config path vendor/bundle
- bundle install --jobs 4 --retry 3
- bundle exec rake db:create RAILS_ENV=test
- bundle exec rake test & bundle exec rspec & bundle exec rubocop --parallel
cache:
paths:
- vendor/bundle
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~ Normal file
View File

@@ -0,0 +1,42 @@
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

3
.rspec Normal file
View File

@@ -0,0 +1,3 @@
--require spec_helper
--format documentation
--color

191
.rubocop.yml Normal file
View File

@@ -0,0 +1,191 @@
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: 65
# 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
.ruby-version Normal file
View File

@@ -0,0 +1 @@
2.7.6

77
Dockerfile Normal file
View File

@@ -0,0 +1,77 @@
FROM ruby:2.7.7-alpine3.16 AS base
# Set a variable for the install location.
ARG RAILS_ROOT=/usr/src/app
# Set Rails environment.
ENV RAILS_ENV production
ENV BUNDLE_APP_CONFIG="$RAILS_ROOT/.bundle"
# Make the directory and set as working.
RUN mkdir -p $RAILS_ROOT
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
RUN 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 base
# Set a variable for the install location.
ARG RAILS_ROOT=/usr/src/app
ARG PACKAGES="tzdata curl postgresql-client sqlite-libs yarn nodejs bash gcompat"
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
# Expose port 80.
EXPOSE 80
# Sets the footer of greenlight application with current build version
ARG version_code
ENV VERSION_CODE=$version_code
# Set executable permission to start file
RUN chmod +x bin/start
# Update HTTPClient cacert.pem with the latest Mozilla cacert.pem
RUN wget https://curl.se/ca/cacert.pem https://curl.se/ca/cacert.pem.sha256 -P /tmp
RUN cd /tmp && sha256sum cacert.pem > cacert.pem.sha256sum && cd ${RAILS_ROOT}
RUN diff /tmp/cacert.pem.sha256sum /tmp/cacert.pem.sha256
RUN mv -v /tmp/cacert.pem $(bundle info httpclient --path)/lib/httpclient/ && rm -v /tmp/cacert*
# Update Openssl certs [This is for Faraday adapter for Net::HTTP]
RUN [[ $(id -u) -eq 0 ]] && update-ca-certificates
# Start the application.
CMD ["bin/start"]

80
Gemfile Normal file
View File

@@ -0,0 +1,80 @@
# frozen_string_literal: true
source 'https://rubygems.org'
git_source(:github) do |repo_name|
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'
gem 'aws-sdk-s3', '~> 1.88.1'
gem 'bcrypt', '~> 3.1.7'
gem 'bigbluebutton-api-ruby', '~> 1.9', '>= 1.9.0'
gem 'bn-ldap-authentication', '~> 0.1.4'
gem 'bootsnap', '~> 1.7.2', require: false
gem 'bootstrap', '~> 4.3.1'
gem 'cancancan', '~> 2.3.0'
gem 'coveralls', '~> 0.8.23', require: false
gem 'font-awesome-sass', '~> 5.9.0'
gem 'google-cloud-storage', '~> 1.30.0'
gem 'http_accept_language', '~> 2.1.1'
gem 'i18n-language-mapping', '~> 0.1.3.1'
gem 'jbuilder', '~> 2.11.5'
gem 'jquery-rails', '~> 4.4.0'
gem 'local_time', '~> 2.1.0'
gem 'net-ldap', '~> 0.17.0'
gem 'omniauth', '~> 2.1.0'
gem 'omniauth-bn-launcher', '~> 0.1.5'
gem 'omniauth-bn-office365', '~> 0.1.2'
gem 'omniauth-google-oauth2', '~> 1.0.1'
gem 'omniauth_openid_connect', '~> 0.4.0'
gem 'omniauth-rails_csrf_protection', '~> 1.0.1'
gem 'omniauth-twitter', '~> 1.4.0'
gem 'pagy', '~> 3.11.0'
gem 'pluck_to_hash', '~> 1.0.2'
gem 'puma', '~> 4.3.12'
gem 'rails', '~> 5.2.8.1'
gem 'random_password', '~> 0.1.1'
gem "recaptcha", '~> 5.7.0'
gem 'redcarpet', '~> 3.5.1'
gem 'remote_syslog_logger', '~> 1.0.4'
gem 'repost', '~> 0.3.8'
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.5'
gem 'uglifier', '~> 4.2.0'
group :production do
gem 'hiredis', '~> 0.6.3'
gem "lograge", "~> 0.11.2"
gem 'pg', '~> 0.18'
gem 'redis', '~> 4.2.5'
gem 'sequel', '~> 5.41.0'
end
group :development, :test do
gem 'byebug', '~> 11.1', platform: :mri
gem 'dotenv-rails', '~> 2.8', '>= 2.8.1'
end
group :test do
gem 'action-cable-testing', '~> 0.6', '>= 0.6.1'
gem "factory_bot_rails", "~> 6.2", ">= 6.2.0"
gem 'faker', '~> 2.16'
gem 'rails-controller-testing', '~> 1.0', '>= 1.0.5'
gem 'rspec-rails', '~> 3.9', '>= 3.9.1'
gem 'shoulda-matchers', '~> 3.1', '>= 3.1.3'
gem 'webmock', '~> 3.11'
end
group :development do
gem 'listen', '~> 3.0'
gem 'spring', '~> 2.1'
gem 'spring-watcher-listen', '~> 2.0'
gem 'web-console', '~> 3.7', '>= 3.7.0'
end

557
Gemfile.lock Normal file
View File

@@ -0,0 +1,557 @@
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)
GEM
remote: https://rubygems.org/
specs:
action-cable-testing (0.6.1)
actioncable (>= 5.0)
actioncable (5.2.8.1)
actionpack (= 5.2.8.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailer (5.2.8.1)
actionpack (= 5.2.8.1)
actionview (= 5.2.8.1)
activejob (= 5.2.8.1)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (5.2.8.1)
actionview (= 5.2.8.1)
activesupport (= 5.2.8.1)
rack (~> 2.0, >= 2.0.8)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.2.8.1)
activesupport (= 5.2.8.1)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (5.2.8.1)
activesupport (= 5.2.8.1)
globalid (>= 0.3.6)
activemodel (5.2.8.1)
activesupport (= 5.2.8.1)
activerecord (5.2.8.1)
activemodel (= 5.2.8.1)
activesupport (= 5.2.8.1)
arel (>= 9.0)
activestorage (5.2.8.1)
actionpack (= 5.2.8.1)
activerecord (= 5.2.8.1)
marcel (~> 1.0.0)
activesupport (5.2.8.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
aes_key_wrap (1.1.0)
arel (9.0.0)
ast (2.4.2)
attr_required (1.0.1)
autoprefixer-rails (10.4.7.0)
execjs (~> 2)
aws-eventstream (1.2.0)
aws-partitions (1.592.0)
aws-sdk-core (3.131.1)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.57.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.88.2)
aws-sdk-core (~> 3, >= 3.112.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.5.0)
aws-eventstream (~> 1, >= 1.0.2)
bcrypt (3.1.18)
bigbluebutton-api-ruby (1.9.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)
bindata (2.4.13)
bindex (0.8.1)
bn-ldap-authentication (0.1.4)
net-ldap (~> 0)
bootsnap (1.7.7)
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.1.0)
concurrent-ruby (1.1.10)
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)
diff-lcs (1.5.0)
digest-crc (0.6.4)
rake (>= 12.0.0, < 14.0.0)
docile (1.4.0)
dotenv (2.8.1)
dotenv-rails (2.8.1)
dotenv (= 2.8.1)
railties (>= 3.2)
erubi (1.11.0)
execjs (2.8.1)
factory_bot (6.2.1)
activesupport (>= 5.0.0)
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faker (2.21.0)
i18n (>= 1.8.11, < 2)
faraday (1.10.2)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
ffi (1.15.5)
font-awesome-sass (5.9.0)
sassc (>= 1.11)
globalid (1.0.1)
activesupport (>= 5.0)
google-apis-core (0.5.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
webrick
google-apis-iamcredentials_v1 (0.10.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-storage_v1 (0.14.0)
google-apis-core (>= 0.4, < 2.a)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.2.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.17.1)
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.15)
hashdiff (1.0.1)
hashie (5.0.0)
hiredis (0.6.3)
http_accept_language (2.1.1)
httpclient (2.8.3)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
i18n-language-mapping (0.1.3.1)
jbuilder (2.11.5)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
jmespath (1.6.1)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.6.2)
json-jwt (1.15.3)
activesupport (>= 4.2)
aes_key_wrap
bindata
httpclient
jwt (2.5.0)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
local_time (2.1.0)
lograge (0.11.2)
actionpack (>= 4)
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
loofah (2.19.1)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
mini_mime (>= 0.1.1)
marcel (1.0.2)
memoist (0.16.2)
method_source (1.0.0)
mini_mime (1.1.2)
mini_portile2 (2.8.0)
minitest (5.17.0)
msgpack (1.5.1)
multi_json (1.15.0)
multi_xml (0.6.0)
multipart-post (2.2.3)
net-ldap (0.17.0)
net-protocol (0.1.3)
timeout
net-smtp (0.3.2)
net-protocol
nio4r (2.5.8)
nokogiri (1.13.10)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
oauth (1.1.0)
oauth-tty (~> 1.0, >= 1.0.1)
snaky_hash (~> 2.0)
version_gem (~> 1.1)
oauth-tty (1.0.5)
version_gem (~> 1.1, >= 1.1.1)
oauth2 (1.4.11)
faraday (>= 0.17.3, < 3.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 4)
omniauth (2.1.0)
hashie (>= 3.4.6)
rack (>= 2.2.3)
rack-protection
omniauth-bn-launcher (0.1.5)
omniauth (~> 2.1, >= 2.1.0)
omniauth-oauth2 (= 1.7.2)
omniauth-bn-office365 (0.1.2)
omniauth (~> 2.1, >= 2.1.0)
omniauth-oauth2 (= 1.7.2)
omniauth-google-oauth2 (1.0.1)
jwt (>= 2.0)
oauth2 (~> 1.1)
omniauth (~> 2.0)
omniauth-oauth2 (~> 1.7.1)
omniauth-oauth (1.2.0)
oauth
omniauth (>= 1.0, < 3)
omniauth-oauth2 (1.7.2)
oauth2 (~> 1.4)
omniauth (>= 1.9, < 3)
omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
omniauth-twitter (1.4.0)
omniauth-oauth (~> 1.1)
rack
omniauth_openid_connect (0.4.0)
addressable (~> 2.5)
omniauth (>= 1.9, < 3)
openid_connect (~> 1.1)
openid_connect (1.4.2)
activemodel
attr_required (>= 1.0.0)
json-jwt (>= 1.15.0)
net-smtp
rack-oauth2 (~> 1.21)
swd (~> 1.3)
tzinfo
validate_email
validate_url
webfinger (~> 1.2)
os (1.1.4)
pagy (3.11.0)
parallel (1.22.1)
parser (3.1.2.0)
ast (~> 2.4.1)
pg (0.21.0)
pluck_to_hash (1.0.2)
activerecord (>= 4.0.2)
activesupport (>= 4.0.2)
popper_js (1.16.1)
public_suffix (5.0.0)
puma (4.3.12)
nio4r (~> 2.0)
racc (1.6.1)
rack (2.2.6.2)
rack-oauth2 (1.21.3)
activesupport
attr_required
httpclient
json-jwt (>= 1.11.0)
rack (>= 2.1.0)
rack-protection (3.0.2)
rack
rack-test (2.0.2)
rack (>= 1.3)
rails (5.2.8.1)
actioncable (= 5.2.8.1)
actionmailer (= 5.2.8.1)
actionpack (= 5.2.8.1)
actionview (= 5.2.8.1)
activejob (= 5.2.8.1)
activemodel (= 5.2.8.1)
activerecord (= 5.2.8.1)
activestorage (= 5.2.8.1)
activesupport (= 5.2.8.1)
bundler (>= 1.3.0)
railties (= 5.2.8.1)
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)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.4.4)
loofah (~> 2.19, >= 2.19.1)
railties (5.2.8.1)
actionpack (= 5.2.8.1)
activesupport (= 5.2.8.1)
method_source
rake (>= 0.8.7)
thor (>= 0.19.0, < 2.0)
rainbow (3.1.1)
rake (13.0.6)
random_password (0.1.1)
rb-fsevent (0.11.1)
rb-inotify (0.10.1)
ffi (~> 1.0)
recaptcha (5.7.0)
json
redcarpet (3.5.1)
redis (4.2.5)
regexp_parser (2.4.0)
remote_syslog_logger (1.0.4)
syslog_protocol
repost (0.3.8)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
request_store (1.5.1)
rack (>= 1.4)
retriable (3.1.2)
rexml (3.2.5)
rspec-core (3.9.3)
rspec-support (~> 3.9.3)
rspec-expectations (3.9.4)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-rails (3.9.1)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.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.18.0)
parser (>= 3.1.1.0)
ruby-progressbar (1.11.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
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.16.1)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.0)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simplecov (0.16.1)
docile (~> 1.1)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.2)
snaky_hash (2.0.1)
hashie
version_gem (~> 1.1, >= 1.1.1)
spring (2.1.1)
spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.4.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
sqlite3 (1.3.13)
swd (1.3.0)
activesupport (>= 3)
attr_required (>= 0.0.5)
httpclient (>= 2.4)
sync (0.5.0)
syslog_protocol (0.9.2)
term-ansicolor (1.7.1)
tins (~> 1.0)
thor (1.2.1)
thread_safe (0.3.6)
tilt (2.0.11)
timeout (0.3.0)
tins (1.31.1)
sync
trailblazer-option (0.1.2)
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
tzinfo (1.2.10)
thread_safe (~> 0.1)
tzinfo-data (1.2021.5)
tzinfo (>= 1.0.0)
uber (0.1.0)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (2.1.0)
validate_email (0.1.6)
activemodel (>= 3.0)
mail (>= 2.2.5)
validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
version_gem (1.1.1)
web-console (3.7.0)
actionview (>= 5.0)
activemodel (>= 5.0)
bindex (>= 0.4.0)
railties (>= 5.0)
webfinger (1.2.0)
activesupport
httpclient (>= 2.4)
webmock (3.14.0)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webrick (1.7.0)
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
xml-simple (1.1.9)
rexml
PLATFORMS
ruby
DEPENDENCIES
action-cable-testing (~> 0.6, >= 0.6.1)
aws-sdk-s3 (~> 1.88.1)
bcrypt (~> 3.1.7)
bigbluebutton-api-ruby (~> 1.9, >= 1.9.0)
bn-ldap-authentication (~> 0.1.4)
bootsnap (~> 1.7.2)
bootstrap (~> 4.3.1)
byebug (~> 11.1)
cancancan (~> 2.3.0)
coveralls (~> 0.8.23)
dotenv-rails (~> 2.8, >= 2.8.1)
factory_bot_rails (~> 6.2, >= 6.2.0)
faker (~> 2.16)
font-awesome-sass (~> 5.9.0)
google-cloud-storage (~> 1.30.0)
hiredis (~> 0.6.3)
http_accept_language (~> 2.1.1)
i18n-language-mapping (~> 0.1.3.1)
jbuilder (~> 2.11.5)
jquery-rails (~> 4.4.0)
listen (~> 3.0)
local_time (~> 2.1.0)
lograge (~> 0.11.2)
net-ldap (~> 0.17.0)
omniauth (~> 2.1.0)
omniauth-bn-launcher (~> 0.1.5)
omniauth-bn-office365 (~> 0.1.2)
omniauth-google-oauth2 (~> 1.0.1)
omniauth-rails_csrf_protection (~> 1.0.1)
omniauth-twitter (~> 1.4.0)
omniauth_openid_connect (~> 0.4.0)
pagy (~> 3.11.0)
pg (~> 0.18)
pluck_to_hash (~> 1.0.2)
puma (~> 4.3.12)
rails (~> 5.2.8.1)
rails-controller-testing (~> 1.0, >= 1.0.5)
random_password (~> 0.1.1)
recaptcha (~> 5.7.0)
redcarpet (~> 3.5.1)
redis (~> 4.2.5)
remote_syslog_logger (~> 1.0.4)
repost (~> 0.3.8)
rspec-rails (~> 3.9, >= 3.9.1)
rubocop (~> 1.10.0)
sassc-rails (~> 2.1.2)
sequel (~> 5.41.0)
shoulda-matchers (~> 3.1, >= 3.1.3)
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.5)
uglifier (~> 4.2.0)
web-console (~> 3.7, >= 3.7.0)
webmock (~> 3.11)

165
LICENSE Normal file
View File

@@ -0,0 +1,165 @@
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.

40
README.md Normal file
View File

@@ -0,0 +1,40 @@
<img width="1012" alt="bbb-greenlight-banner" src="https://user-images.githubusercontent.com/1273169/141143584-684766cf-8633-4d66-b35e-f134a368e4c8.png">
# Greenlight
![Coverage
!Status](https://coveralls.io/repos/github/bigbluebutton/greenlight/badge.svg?branch=master)
![Docker Pulls](https://img.shields.io/docker/pulls/bigbluebutton/greenlight.svg)
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:
* Signup/Login with Google, Office365, OpenID Connect, or through the application itself.
* 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)!
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, checkout our Introduction to Greenlight Video:
[![GreenLight Overview](https://img.youtube.com/vi/Hso8yLzkqj8/0.jpg)](https://youtu.be/Hso8yLzkqj8)
## Installation on a BigBlueButton Server
Greenlight is designed to work on a [BigBlueButton 2.0](https://github.com/bigbluebutton/bigbluebutton) (or later) server.
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 & Contributing
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).
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).
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.

15
Rakefile Normal file
View File

@@ -0,0 +1,15 @@
# frozen_string_literal: true
# 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.
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

23
SECURITY.md Normal file
View File

@@ -0,0 +1,23 @@
# Security Policy
## Supported Versions
We actively support Greenlight through the community forums and through security updates.
| Version | Supported |
| ------- | ------------------ |
| v1.x | :x: |
| v2.x | :white_check_mark: |
| > v2.x | :x: |
Since we released v2 to the community all our support efforts are now transitioned to the subsequent releases v2.x.
All the releases are managed as a Cloud version where release tags are used only as a reference. As such, we highly recommend that all administrators deploy the latest available release.
## Reporting a Vulnerability
If you believe you have found a security vunerability in 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 with as much detail as possible.
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.
Regards,... [BigBlueButton Team](https://docs.bigbluebutton.org/support/faq.html#bigbluebutton-committer)

View File

@@ -0,0 +1,19 @@
// 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/>.
//= link_tree ../images
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css

View File

@@ -0,0 +1,93 @@
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.

Binary file not shown.

Binary file not shown.

0
app/assets/images/.keep Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,328 @@
// 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"));
}
}
});
// 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());
});
}
}

View File

@@ -0,0 +1,37 @@
// 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.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// 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 jquery3
//= require tabler
//= require tabler.plugins
//= require jquery_ujs
//= require pickr.min.js
//= require bootstrap-select.min.js
//= require local-time
//= require_tree .

View File

@@ -0,0 +1,32 @@
// 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/>.
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the rails generate channel command.
//
//= require action_cable
//= require_self
//= require_tree ./channels
(function() {
var protocol = (window.location.protocol === "https:" ? "wss://" : "ws://");
var host = window.GreenLight.WEBSOCKET_HOST || window.location.host + window.GreenLight.RELATIVE_ROOT;
var url = protocol + host + '/cable';
this.App || (this.App = {});
App.cable = ActionCable.createConsumer(url);
}).call(this);

View File

View File

@@ -0,0 +1,37 @@
// 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() + ";"
})
})

View File

@@ -0,0 +1,57 @@
// 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")
}
})
})

View File

@@ -0,0 +1,36 @@
// 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(){
// Stores the current url when the user clicks the sign in button
$(".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()
})
})

View File

@@ -0,0 +1,51 @@
// 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
}

View File

@@ -0,0 +1,48 @@
// 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"))
})
}
});

View File

@@ -0,0 +1,162 @@
// 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);
});
}
});

View File

@@ -0,0 +1,517 @@
// 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) {
$("#user-list").html("") // Clear current inputs
users.forEach(function(user) {
listName = document.createElement("li"),
spanAvatar = document.createElement("span"),
spanName = document.createElement("span"),
spanUid = document.createElement("span"),
spanRemove = document.createElement("span"),
spanRemoveIcon = document.createElement("i");
listName.setAttribute('class', 'list-group-item text-left')
listName.setAttribute('data-uid', user.uid)
spanAvatar.innerText = user.name.charAt(0)
spanAvatar.setAttribute('class', 'avatar float-left mr-2')
spanName.setAttribute('class', 'shared-user')
spanName.innerText = user.name
spanUid.setAttribute('class', 'text-muted ml-1')
spanUid.innerText = user.uid
spanRemove.setAttribute('class', 'text-primary float-right shared-user cursor-pointer')
spanRemove.setAttribute('onclick', 'removeSharedUser(this)')
spanRemoveIcon.setAttribute('class', 'fas fa-times')
listName.appendChild(spanAvatar)
listName.appendChild(spanName)
spanName.appendChild(spanUid)
listName.appendChild(spanRemove)
spanRemove.appendChild(spanRemoveIcon)
$("#user-list").append(listName)
})
});
}
// 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();
}
})
}

View File

@@ -0,0 +1,103 @@
// 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
}

View File

@@ -0,0 +1,42 @@
// 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();
}
});
});
});
}
});

View File

@@ -0,0 +1,105 @@
// 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);
}
}
});

View File

@@ -0,0 +1,46 @@
// 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'))
})
}
})

View File

@@ -0,0 +1,67 @@
// 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
}

View File

@@ -0,0 +1,81 @@
/* 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";

View File

@@ -0,0 +1,105 @@
// 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;
}

View File

@@ -0,0 +1,192 @@
/* 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 "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;
}

View File

@@ -0,0 +1,20 @@
/* 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/

178
app/assets/stylesheets/main.scss Executable file
View File

@@ -0,0 +1,178 @@
/* 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;
}

View File

@@ -0,0 +1,146 @@
/* 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;
}
}

View File

@@ -0,0 +1,66 @@
/* 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;
}
}

View File

@@ -0,0 +1,32 @@
/* 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);
}

View File

@@ -0,0 +1,504 @@
/* 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;
}

View File

@@ -0,0 +1,24 @@
/* 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;

View File

@@ -0,0 +1,22 @@
# 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 ApplicationCable
class Channel < ActionCable::Channel::Base
end
end

View File

@@ -0,0 +1,22 @@
# 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 ApplicationCable
class Connection < ActionCable::Connection::Base
end
end

View File

@@ -0,0 +1,23 @@
# 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 WaitingChannel < ApplicationCable::Channel
def subscribed
stream_from "#{params[:roomuid]}_waiting_channel"
end
end

View File

@@ -0,0 +1,78 @@
# 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

View File

@@ -0,0 +1,379 @@
# 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

View File

@@ -0,0 +1,335 @@
# 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 ApplicationController < ActionController::Base
include BbbServer
include Errors
before_action :block_unknown_hosts, :redirect_to_https, :set_user_domain, :set_user_settings, :maintenance_mode?,
:migration_error?, :user_locale, :check_admin_password, :check_user_role
protect_from_forgery with: :exceptions
# Retrieves the current user.
def current_user
@current_user ||= User.includes(:role, :main_room).find_by(id: session[:user_id])
if Rails.configuration.loadbalanced_configuration && (@current_user && !@current_user.has_role?(:super_admin) &&
@current_user.provider != @user_domain)
reset_session
return nil # This stops the session validation checks for loadbalanced configurations.
end
# For backward compatibility and for seamless integration with existing and running deployments:
# The active sessions will be declared as active on first interaction after the update.
# This keeps alive the already active sessions before the upgrade for accounts having no password updates.
session[:activated_at] ||= Time.zone.now.to_i if @current_user&.last_pwd_update.nil?
# Once a request is issued back to the server with a session that had been active before
# the last password update it will automatically get invalidated and the request will get
# redirected back to the root path.
# This solves #3086.
unless session[:activated_at].to_i >= @current_user&.last_pwd_update.to_i
# For backward compatibility and for seamless integration with existing and running deployments:
# The last_pwd_update attribute will default to nil and nil.to_i will always be 0.
# This with the activated_at fallback to the first connection after the upgrade will result in
# keeping alive old sessions and ensuring a seamless intergation.
# In cases where the account has a password update after the upgrade, all of old the active sessions
# which haven't updated their state and all the other active updated sessions before the password
# update event will be cought and declared as invalid where users will get unauthenticated and redirected to root path.
reset_session
redirect_to root_path, flash: { alert: I18n.t("session.expired") } and return
end
@current_user
end
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
Rails.configuration.relative_url_root || ""
end
helper_method :relative_root
# Determines if the BigBlueButton endpoint is configured (or set to default).
def bigbluebutton_endpoint_default?
return false if Rails.configuration.loadbalanced_configuration
Rails.configuration.bigbluebutton_endpoint_default == Rails.configuration.bigbluebutton_endpoint
end
helper_method :bigbluebutton_endpoint_default?
def allow_greenlight_accounts?
return Rails.configuration.allow_user_signup unless Rails.configuration.loadbalanced_configuration
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

View File

View File

@@ -0,0 +1,133 @@
# 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
# Upon login the session metadata activated_at has to match to login event timestamp.
session[:activated_at] = user.last_login.to_i if user.without_terms_acceptance { 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
valid_url = cookies[:return_to] && URI.parse(cookies[:return_to]).host == URI.parse(request.original_url).host
url = if cookies[:return_to] && valid_url && !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)
return true if Rails.configuration.social_switching
Rails.configuration.loadbalanced_configuration &&
User.exists?(email: email, provider: @user_domain) &&
!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

View File

@@ -0,0 +1,146 @@
# 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

View File

@@ -0,0 +1,150 @@
# 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 Emailer
extend ActiveSupport::Concern
# Sends account activation email.
def send_activation_email(user, token)
begin
return unless Rails.configuration.enable_email_verification
UserMailer.verify_email(user, user_verification_link(token), @settings).deliver
rescue => e
logger.error "Support: Error in email delivery: #{e}"
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
else
flash[:success] = I18n.t("email_sent", email_type: t("verify.verification"))
end
end
# Sends password reset email.
def send_password_reset_email(user, token)
begin
return unless Rails.configuration.enable_email_verification
UserMailer.password_reset(user, reset_link(token), @settings).deliver_now
rescue => e
logger.error "Support: Error in email delivery: #{e}"
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
else
flash[:success] = I18n.t("email_sent", email_type: t("reset_password.subtitle"))
end
end
def send_user_promoted_email(user, role)
begin
return unless Rails.configuration.enable_email_verification
UserMailer.user_promoted(user, role, root_url, @settings).deliver_now
rescue => e
logger.error "Support: Error in email delivery: #{e}"
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
end
end
def send_user_demoted_email(user, role)
begin
return unless Rails.configuration.enable_email_verification
UserMailer.user_demoted(user, role, root_url, @settings).deliver_now
rescue => e
logger.error "Support: Error in email delivery: #{e}"
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
end
end
# Sends inivitation to join
def send_invitation_email(name, email, invite)
begin
return unless Rails.configuration.enable_email_verification
UserMailer.invite_email(name, email, invite.updated_at, invitation_link(invite.invite_token), @settings).deliver_now
rescue => e
logger.error "Support: Error in email delivery: #{e}"
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
end
end
def send_user_approved_email(user)
begin
return unless Rails.configuration.enable_email_verification
UserMailer.approve_user(user, root_url, @settings).deliver_now
rescue => e
logger.error "Support: Error in email delivery: #{e}"
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
else
flash[:success] = I18n.t("email_sent", email_type: t("verify.verification"))
end
end
def send_approval_user_signup_email(user)
begin
return unless Rails.configuration.enable_email_verification
admin_emails = admin_emails()
UserMailer.approval_user_signup(user, admins_url(tab: "pending"),
admin_emails, @settings).deliver_now unless admin_emails.empty?
rescue => e
logger.error "Support: Error in email delivery: #{e}"
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
end
end
def send_invite_user_signup_email(user)
begin
return unless Rails.configuration.enable_email_verification
admin_emails = admin_emails()
UserMailer.invite_user_signup(user, admins_url, admin_emails, @settings).deliver_now unless admin_emails.empty?
rescue => e
logger.error "Support: Error in email delivery: #{e}"
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
end
end
private
# Returns the link the user needs to click to verify their account
def user_verification_link(token)
edit_account_activation_url(token: token)
end
def admin_emails
roles = Role.where(provider: @user_domain, role_permissions: { name: "can_manage_users", value: "true" })
.pluck(:name)
admins = User.with_role(roles - ["super_admin"])
admins = admins.where(provider: @user_domain) if Rails.configuration.loadbalanced_configuration
admins.collect(&:email).join(",")
end
def reset_link(token)
edit_password_reset_url(token)
end
def invitation_link(token)
if allow_greenlight_accounts?
signup_url(invite_token: token)
else
root_url(invite_token: token)
end
end
end

View File

@@ -0,0 +1,5 @@
# frozen_string_literal: true
module Errors
class UnsafeHostError < StandardError; end
end

View File

@@ -0,0 +1,122 @@
# 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 Joiner
extend ActiveSupport::Concern
# Displays the join room page to the user
def show_user_join
# Get users name
@name = if current_user
current_user.name
elsif cookies.encrypted[:greenlight_name]
cookies.encrypted[:greenlight_name]
else
""
end
@search, @order_column, @order_direction, pub_recs =
public_recordings(@room.bbb_id, params.permit(:search, :column, :direction), true)
@pagy, @public_recordings = pagy_array(pub_recs)
render :join
end
# create or update cookie to track the three most recent rooms a user joined
def save_recent_rooms
if current_user
recently_joined_rooms = cookies.encrypted["#{current_user.uid}_recently_joined_rooms"].to_a
cookies.encrypted["#{current_user.uid}_recently_joined_rooms"] =
recently_joined_rooms.prepend(@room.id).uniq[0..2]
end
end
def valid_avatar?(url)
return false if URI::DEFAULT_PARSER.make_regexp(%w[http https]).match(url).nil?
uri = URI(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true if uri.scheme == 'https'
response = http.request_head(uri)
return false if response.code != "200"
response['content-length'].to_i < Rails.configuration.max_avatar_size
end
def join_room(opts)
@room_settings = JSON.parse(@room[:room_settings])
moderator_privileges = @room.owned_by?(current_user) || valid_moderator_access_code(session[:moderator_access_code])
if room_running?(@room.bbb_id) || room_setting_with_config("anyoneCanStart") || moderator_privileges
# Determine if the user needs to join as a moderator.
opts[:user_is_moderator] = room_setting_with_config("joinModerator") || @shared_room || moderator_privileges
opts[:record] = record_meeting
opts[:require_moderator_approval] = room_setting_with_config("requireModeratorApproval")
opts[:mute_on_start] = room_setting_with_config("muteOnStart")
if current_user
redirect_to join_path(@room, current_user.name, opts, current_user.uid)
else
join_name = params[:join_name] || params[@room.invite_path][:join_name]
redirect_to join_path(@room, join_name, opts, fetch_guest_id)
end
else
search_params = params[@room.invite_path] || params
@search, @order_column, @order_direction, pub_recs =
public_recordings(@room.bbb_id, search_params.permit(:search, :column, :direction), true)
@pagy, @public_recordings = pagy_array(pub_recs)
# They need to wait until the meeting begins.
render :wait
end
end
def incorrect_user_domain
Rails.configuration.loadbalanced_configuration && @room.owner.provider != @user_domain
end
# Default, unconfigured meeting options.
def default_meeting_options
moderator_message = "#{I18n.t('invite_message')}<br> #{request.base_url + room_path(@room)}"
moderator_message += "<br> #{I18n.t('modal.create_room.access_code')}: #{@room.access_code}" if @room.access_code.present?
{
user_is_moderator: false,
meeting_logout_url: request.base_url + logout_room_path(@room),
moderator_message: moderator_message,
host: request.host,
recording_default_visibility: @settings.get_value("Default Recording Visibility") == "public"
}
end
private
def fetch_guest_id
return cookies[:guest_id] if cookies[:guest_id].present?
guest_id = "gl-guest-#{SecureRandom.hex(12)}"
cookies[:guest_id] = {
value: guest_id,
expires: 1.day.from_now
}
guest_id
end
end

View File

@@ -0,0 +1,121 @@
# 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 Populator
extend ActiveSupport::Concern
# Returns a list of users that are in the same context of the current user
def manage_users_list
initial_list = case @tab
when "active"
User.without_role([:pending, :denied])
when "deleted"
User.deleted
when "pending"
User.with_role(:pending)
when "denied"
User.with_role(:denied)
else
User.all
end
initial_list = initial_list.with_role(@role.name) if @role.present?
initial_list = initial_list.without_role(:super_admin)
initial_list = initial_list.where(provider: @user_domain) if Rails.configuration.loadbalanced_configuration
initial_list.where.not(id: current_user.id)
.admins_search(@search)
.admins_order(@order_column, @order_direction)
end
# Returns a list of rooms that are in the same context of the current user
def server_rooms_list
if Rails.configuration.loadbalanced_configuration
Room.includes(:owner).where(users: { provider: @user_domain })
.admins_search(@search)
.admins_order(@order_column, @order_direction, @running_room_bbb_ids)
else
Room.includes(:owner).admins_search(@search).admins_order(@order_column, @order_direction, @running_room_bbb_ids)
end
end
# Returns the correct recordings based on the users inputs
def recordings_to_show(user = nil, room = nil)
if user.present?
# Find user and get his recordings
rooms = User.find_by(email: user)&.rooms&.pluck(:bbb_id)
return all_recordings(rooms) if user.present? && !rooms.nil?
[] # return no recs if room not found
elsif room.present?
# Find room and get its recordings
room = Room.find_by(uid: room)&.bbb_id
return all_recordings([room]) if room.present?
[]
else
latest_recordings
end
end
# Returns a list off all current invitations
def invited_users_list
list = if Rails.configuration.loadbalanced_configuration
Invitation.where(provider: @user_domain)
else
Invitation.all
end
list.admins_search(@search).order(updated_at: :desc)
end
private
# Returns exactly 1 page of the latest recordings
def latest_recordings
return_length = Rails.configuration.pagination_rows
number_of_rooms = Rails.configuration.pagination_number
recordings = []
counter = 0
# Manually paginate through the rooms
while recordings.length < return_length
rooms = if Rails.configuration.loadbalanced_configuration
Room.includes(:owner)
.where(users: { provider: @user_domain })
.order(last_session: :desc)
.limit(number_of_rooms)
.offset(counter * number_of_rooms)
.pluck(:bbb_id)
else
Room.order(last_session: :desc)
.limit(return_length)
.offset(counter * return_length)
.pluck(:bbb_id)
end
break if rooms.blank?
counter += 1
recordings.push(*all_recordings(rooms))
end
recordings[0..return_length]
end
end

View File

@@ -0,0 +1,132 @@
# 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 Recorder
extend ActiveSupport::Concern
include RecordingsHelper
# Fetches all recordings for a room.
def recordings(room_bbb_id, search_params = {}, ret_search_params = false)
res = get_recordings(room_bbb_id)
format_recordings(res, search_params, ret_search_params)
end
# Fetches a rooms public recordings.
def public_recordings(room_bbb_id, search_params = {}, ret_search_params = false)
search, order_col, order_dir, recs = recordings(room_bbb_id, search_params, ret_search_params)
[search, order_col, order_dir, recs.select { |r| r[:metadata][:"gl-listed"] == "true" }]
end
# Makes paginated API calls to get recordings
def all_recordings(room_bbb_ids, search_params = {}, ret_search_params = false, search_name = false)
res = { recordings: [] }
until room_bbb_ids.empty?
# bbb.get_recordings returns an object
# take only the array portion of the object that is returned
full_res = get_multiple_recordings(room_bbb_ids.pop(Rails.configuration.pagination_number))
res[:recordings].push(*full_res[:recordings])
end
format_recordings(res, search_params, ret_search_params, search_name)
end
# Format, filter, and sort recordings to match their current use in the app
def format_recordings(api_res, search_params, ret_search_params, search_name = false)
search = search_params[:search] || ""
order_col = search_params[:column] && search_params[:direction] != "none" ? search_params[:column] : "end_time"
order_dir = search_params[:column] && search_params[:direction] != "none" ? search_params[:direction] : "desc"
search = search.downcase
api_res[:recordings].each do |r|
next if r.key?(:error)
# Format playbacks in a more pleasant way.
r[:playbacks] = if !r[:playback] || !r[:playback][:format]
[]
elsif r[:playback][:format].is_a?(Array)
r[:playback][:format]
else
[r[:playback][:format]]
end
r.delete(:playback)
end
recs = filter_recordings(api_res, search, search_name)
recs = sort_recordings(recs, order_col, order_dir)
if ret_search_params
[search, order_col, order_dir, recs]
else
recs
end
end
def filter_recordings(api_res, search, search_name = false)
api_res[:recordings].select do |r|
(!r[:metadata].nil? && ((!r[:metadata][:name].nil? &&
r[:metadata][:name].downcase.include?(search)) ||
(r[:metadata][:"gl-listed"] == "true" && search == "public") ||
(r[:metadata][:"gl-listed"] == "false" && search == "unlisted"))) ||
((r[:metadata].nil? || r[:metadata][:name].nil?) &&
r[:name].downcase.include?(search)) ||
r[:participants].include?(search) ||
!r[:playbacks].select { |p| p[:type].downcase.include?(search) }.empty? ||
(search_name && recording_owner(r[:meetingID]).downcase.include?(search))
end
end
def sort_recordings(recs, order_col, order_dir)
recs = case order_col
when "end_time"
recs.sort_by { |r| r[:endTime] }
when "name"
recs.sort_by do |r|
if !r[:metadata].nil? && !r[:metadata][:name].nil?
r[:metadata][:name].downcase
else
r[:name].downcase
end
end
when "length"
recs.sort_by { |r| r[:playbacks].reject { |p| p[:type] == "statistics" }.first[:length] }
when "users"
recs.sort_by { |r| r[:participants] }
when "visibility"
recs.sort_by { |r| r[:metadata][:"gl-listed"] }
when "formats"
recs.sort_by { |r| r[:playbacks].first[:type].downcase }
else
recs.sort_by { |r| r[:endTime] }
end
if order_dir == 'asc'
recs
else
recs.reverse
end
end
private
# Gets the email of the room owner to which the recording belongs to
def recording_owner(room_id)
Room.find_by(bbb_id: room_id).owner.email.presence || Room.find_by(bbb_id: room_id).owner.username
end
end

View File

@@ -0,0 +1,75 @@
# 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 Registrar
extend ActiveSupport::Concern
def approval_registration
@settings.get_value("Registration Method") == Rails.configuration.registration_methods[:approval]
end
def invite_registration
@settings.get_value("Registration Method") == Rails.configuration.registration_methods[:invite]
end
# Returns a hash containing whether the user has been invited and if they
# signed up with the same email that they were invited with
def check_user_invited(email, token, domain)
return { present: true, verified: false } unless invite_registration
return { present: false, verified: false } if token.nil?
invite = Invitation.valid.find_by(invite_token: token, provider: domain)
if invite.present?
# Check if they used the same email to sign up
same_email = email.casecmp(invite.email).zero?
invite.destroy
{ present: true, verified: same_email }
else
{ present: false, verified: false }
end
end
# Checks if the user passes the requirements to be invited
def passes_invite_reqs
# check if user needs to be invited and IS invited
invitation = check_user_invited(@user.email, session[:invite_token], @user_domain)
@user.email_verified = true if invitation[:verified]
invitation[:present]
end
# Add validation errors to model if they exist
def valid_user_or_captcha
valid_user = @user.valid?
valid_captcha = Rails.configuration.recaptcha_enabled ? verify_recaptcha(model: @user) : true
logger.error("Support: #{@user.email} creation failed: User params are not valid.") unless valid_user
valid_user && valid_captcha
end
# Checks if the user trying to sign in with twitter account
def check_if_twitter_account(log_out = false)
unless params[:old_twitter_user_id].nil? && session[:old_twitter_user_id].nil?
logout if log_out
flash.now[:alert] = I18n.t("registration.deprecated.new_signin")
session[:old_twitter_user_id] = params[:old_twitter_user_id] unless params[:old_twitter_user_id].nil?
end
end
end

View File

@@ -0,0 +1,164 @@
# 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 Rolify
extend ActiveSupport::Concern
# Gets all roles
def all_roles(selected_role)
@roles = Role.editable_roles(@user_domain).by_priority
if @roles.count.zero?
Role.create_default_roles(@user_domain)
@roles = Role.editable_roles(@user_domain)
end
@selected_role = if selected_role.nil?
@roles.find_by(name: 'user')
else
@roles.find(selected_role)
end
@roles
end
# Creates a new role
def create_role(new_role_name)
# Make sure that the role name isn't a duplicate or a reserved name like super_admin or empty
return nil if Role.duplicate_name(new_role_name, @user_domain) || new_role_name.strip.empty?
Role.create_new_role(new_role_name, @user_domain)
end
# Updates a user's roles
def update_roles(role_id)
return true if role_id.blank?
# Check to make sure user can edit roles
return false unless current_user.role.get_permission("can_manage_users")
return true if @user.role_id == role_id.to_i
new_role = Role.find_by(id: role_id, provider: @user_domain)
# Return false if new role doesn't exist
return false if new_role.nil?
return false if new_role.priority < current_user.role.priority
# Send promoted/demoted emails
send_user_promoted_email(@user, new_role) if new_role.get_permission("send_promoted_email")
@user.set_role(new_role.name)
end
# Updates a roles priority
def update_priority(role_to_update)
user_role = Role.find_by(name: "user", provider: @user_domain)
admin_role = Role.find_by(name: "admin", provider: @user_domain)
current_user_role = current_user.role
# Users aren't allowed to update the priority of the admin or user roles
return false if role_to_update.include?(user_role.id.to_s) || role_to_update.include?(admin_role.id.to_s)
# Restrict users to only updating the priority for roles in their domain with a higher
# priority
role_to_update.each do |id|
role = Role.find(id)
return false if role.priority <= current_user_role.priority || role.provider != @user_domain
end
# Get the priority of the current user's role and start with 1 higher
new_priority = [current_user_role.priority, 0].max + 1
begin
# Save the old priorities incase something fails
old_priority = Role.where(id: role_to_update).select(:id, :priority).index_by(&:id)
# Set all the priorities to nil to avoid unique column issues
Role.where(id: role_to_update).update_all(priority: nil)
# Starting at the starting priority, increase by 1 every time
role_to_update.each_with_index do |id, index|
Role.find(id).update_attribute(:priority, new_priority + index)
end
true
rescue => e
# Reset to old prorities
role_to_update.each_with_index do |id, _index|
Role.find(id).update_attribute(:priority, old_priority[id.to_i].priority)
end
logger.error "#{current_user} failed to update role priorities: #{e}"
false
end
end
# Update Permissions
def update_permissions(role)
current_user_role = current_user.role
# Checks that it is valid for the provider to update the role
return false if role.priority <= current_user_role.priority || role.provider != @user_domain
role_params = params.require(:role).permit(:name)
permission_params = params.require(:role).permit(:can_create_rooms, :send_promoted_email,
:send_demoted_email, :can_edit_site_settings, :can_edit_roles, :can_manage_users,
:can_launch_recording, :can_manage_rooms_recordings, :can_appear_in_share_list, :colour)
permission_params.transform_values! do |v|
case v
when "0"
"false"
when "1"
"true"
else
v
end
end
# Role is a default role so users can't change the name
role_params[:name] = role.name if Role::RESERVED_ROLE_NAMES.include?(role.name)
# Make sure if the user is updating the role name that the role name is valid
if role.name != role_params[:name] && !Role.duplicate_name(role_params[:name], @user_domain) &&
!role_params[:name].strip.empty?
role.name = role_params[:name]
elsif role.name != role_params[:name]
return false
end
role.update(colour: permission_params[:colour])
role.update_all_role_permissions(permission_params)
# Create home rooms for all users with this role if users with this role are now able to create rooms
create_home_rooms(role.name) if !role.get_permission("can_create_rooms") && permission_params["can_create_rooms"] == "true"
role.save!
end
private
# Create home rooms for users since they are now able to create rooms
def create_home_rooms(role_name)
User.with_role(role_name).each do |user|
user.create_home_room if user.main_room.nil?
end
end
end

View File

@@ -0,0 +1,45 @@
# 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 Themer
extend ActiveSupport::Concern
# Lightens a color by 40%
def color_lighten(color)
# Uses the built in Sass Engine to lighten the color
generate_sass("lighten", color, "40%")
end
# Darkens a color by 10%
def color_darken(color)
# Uses the built in Sass Engine to darken the color
generate_sass("darken", color, "10%")
end
private
def generate_sass(action, color, percentage)
dummy_scss = "h1 { color: $#{action}; }"
compiled = SassC::Engine.new("$#{action}:#{action}(#{color}, #{percentage});" + dummy_scss, syntax: :scss).render
string_locater = 'color: '
color_start = compiled.index(string_locater) + string_locater.length
compiled[color_start..color_start + 6]
end
end

View File

@@ -0,0 +1,39 @@
# 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 ErrorsController < ApplicationController
def not_found
render "greenlight_error", status: 404, formats: :html
end
def internal_error
render "errors/greenlight_error", status: 500, formats: :html,
locals: {
status_code: 500,
message: I18n.t("errors.internal.message"),
help: I18n.t("errors.internal.help"),
display_back: true,
report_issue: true
}
end
def unauthorized
render "errors/greenlight_error", status: 401, formats: :html, locals: { status_code: 401,
message: I18n.t("errors.unauthorized.message"), help: I18n.t("errors.unauthorized.help"), display_back: true }
end
end

View File

@@ -0,0 +1,74 @@
# 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 HealthCheckController < ApplicationController
skip_before_action :redirect_to_https, :set_user_domain, :set_user_settings, :maintenance_mode?, :migration_error?,
:user_locale, :check_admin_password, :check_user_role
# GET /health_check
def all
response = "success"
@cache_expire = 10.seconds
begin
cache_check
database_check
email_check
rescue => e
response = "Health Check Failure: #{e}"
end
render plain: response
end
private
def cache_check
if Rails.cache.write("__health_check_cache_write__", "true", expires_in: @cache_expire)
raise "Unable to read from cache" unless Rails.cache.read("__health_check_cache_write__") == "true"
else
raise "Unable to write to cache"
end
end
def database_check
raise "Database not responding" if defined?(ActiveRecord) && !ActiveRecord::Migrator.current_version
raise "Pending migrations" unless ActiveRecord::Migration.check_pending!.nil?
end
def email_check
test_smtp if Rails.configuration.action_mailer.delivery_method == :smtp
end
def test_smtp
settings = ActionMailer::Base.smtp_settings
smtp = Net::SMTP.new(settings[:address], settings[:port])
smtp.enable_starttls_auto if settings[:enable_starttls_auto] == ("true") && smtp.respond_to?(:enable_starttls_auto)
if settings[:authentication].present? && settings[:authentication] != "none"
smtp.start(settings[:domain]) do |s|
s.authenticate(settings[:user_name], settings[:password], settings[:authentication])
end
else
smtp.start(settings[:domain])
end
rescue => e
raise "Unable to connect to SMTP Server - #{e}"
end
end

View File

@@ -0,0 +1,28 @@
# 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 MainController < ApplicationController
include Registrar
# GET /
def index
# Store invite token
session[:invite_token] = params[:invite_token] if params[:invite_token] && invite_registration
redirect_to home_page if current_user
end
end

View File

@@ -0,0 +1,95 @@
# 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 PasswordResetsController < ApplicationController
include Emailer
before_action :find_user, only: [:edit, :update]
before_action :check_expiration, only: [:edit, :update]
# GET /password_resets/new
def new
end
# POST /password_resets
def create
return redirect_to new_password_reset_path, flash: { alert: I18n.t("reset_password.captcha") } unless valid_captcha
# Check if user exists and throw an error if he doesn't
@user = User.find_by!(email: params[:password_reset][:email].downcase, provider: @user_domain)
send_password_reset_email(@user, @user.create_reset_digest)
redirect_to root_path
rescue
# User doesn't exist
redirect_to root_path, flash: { success: I18n.t("email_sent", email_type: t("reset_password.subtitle")) }
end
# GET /password_resets/:id/edit
def edit
end
# PATCH /password_resets/:id
def update
# Check if password is valid
if params[:user][:password].empty?
flash.now[:alert] = I18n.t("password_empty_notice")
elsif params[:user][:password] != params[:user][:password_confirmation]
# Password does not match password confirmation
flash.now[:alert] = I18n.t("password_different_notice")
elsif @user.without_terms_acceptance { @user.update_attributes(user_params) }
@user.without_terms_acceptance {
# Clear the user's social uid if they are switching from a social to a local account
@user.update_attribute(:social_uid, nil) if @user.social_uid.present?
# Deactivate the reset digest in use disabling the reset link.
@user.update(reset_digest: nil, reset_sent_at: nil, last_pwd_update: Time.zone.now)
# For password resets the last_pwd_update has to match the resetting event timestamp.
# And the activated_at session metadata has to match it only if the authenticated user
# is the user with the account having its password reset.
# This keeps that user session only alive while invalidating all others for the same account.
session[:activated_at] = @user.last_pwd_update.to_i if current_user&.id == @user.id
}
# Successfully reset password
return redirect_to root_path, flash: { success: I18n.t("password_reset_success") }
end
render 'edit'
end
private
def find_user
@user = User.find_by(reset_digest: User.hash_token(params[:id]), provider: @user_domain)
return redirect_to new_password_reset_url, alert: I18n.t("reset_password.invalid_token") unless @user
end
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
# Checks expiration of reset token.
def check_expiration
redirect_to new_password_reset_url, alert: I18n.t("expired_reset_token") if @user.password_reset_expired?
end
# Checks that the captcha passed is valid
def valid_captcha
return true unless Rails.configuration.recaptcha_enabled
verify_recaptcha
end
end

View File

@@ -0,0 +1,74 @@
# 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 RecordingsController < ApplicationController
before_action :find_room
before_action :verify_room_ownership
META_LISTED = "gl-listed"
# POST /:meetingID/:record_id
def update
meta = {
"meta_#{META_LISTED}" => (params[:state] == "public"),
}
if params[:state] == "protected"
protect_recording(params[:record_id])
else
unprotect_recording(params[:record_id])
end
if params[:state] == "inaccessible"
unpublish_recording(params[:record_id])
else
publish_recording(params[:record_id])
end
res = update_recording(params[:record_id], meta)
# Redirects to the page that made the initial request
redirect_back fallback_location: root_path if res[:updated]
end
# PATCH /:meetingID/:record_id
def rename
update_recording(params[:record_id], "meta_name" => params[:record_name])
redirect_back fallback_location: room_path(@room)
end
# DELETE /:meetingID/:record_id
def delete
delete_recording(params[:record_id])
# Redirects to the page that made the initial request
redirect_back fallback_location: root_path
end
private
def find_room
@room = Room.find_by!(bbb_id: params[:meetingID])
end
# Ensure the user is logged into the room they are accessing.
def verify_room_ownership
redirect_to root_path if !@room.owned_by?(current_user) && !current_user&.role&.get_permission("can_manage_rooms_recordings")
end
end

View File

@@ -0,0 +1,478 @@
# 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 RoomsController < ApplicationController
include Pagy::Backend
include Recorder
include Joiner
include Populator
before_action :validate_accepted_terms, unless: -> { !Rails.configuration.terms }
before_action :validate_verified_email, except: [:show, :join],
unless: -> { !Rails.configuration.enable_email_verification }
before_action :find_room, except: [:create, :join_specific_room, :cant_create_rooms]
before_action :verify_room_ownership_or_admin_or_shared, only: [:start, :shared_access]
before_action :verify_room_ownership_or_admin,
only: [:room_settings, :update_settings, :destroy, :preupload_presentation, :remove_presentation]
before_action :verify_room_ownership_or_shared, only: [:remove_shared_access]
before_action :verify_room_owner_verified, only: [:show, :join],
unless: -> { !Rails.configuration.enable_email_verification }
before_action :verify_room_owner_valid, only: [:show, :join]
before_action :verify_user_not_admin, only: [:show]
skip_before_action :verify_authenticity_token, only: [:join]
# POST /
def create
# Return to root if user is not signed in
return redirect_to root_path unless current_user
# Check if the user has not exceeded the room limit
return redirect_to current_user.main_room, flash: { alert: I18n.t("room.room_limit") } if room_limit_exceeded
# Create room
@room = Room.new(name: room_params[:name],
access_code: room_params[:access_code],
moderator_access_code: room_params[:moderator_access_code])
@room.owner = current_user
@room.room_settings = create_room_settings_string(room_params)
# Save the room and redirect if it fails
return redirect_to current_user.main_room, flash: { alert: I18n.t("room.create_room_error") } unless @room.save
logger.info "Support: #{current_user.email} has created a new room #{@room.uid}."
# Redirect to room is auto join was not turned on
return redirect_to @room,
flash: { success: I18n.t("room.create_room_success") } unless room_params[:auto_join] == "1"
# Start the room if auto join was turned on
start
end
# GET /:room_uid
def show
@room_settings = JSON.parse(@room[:room_settings])
@anyone_can_start = room_setting_with_config("anyoneCanStart")
@room_running = room_running?(@room.bbb_id)
@shared_room = room_shared_with_user
# If its the current user's room
if current_user && (@room.owned_by?(current_user) || @shared_room)
# If the user is trying to access their own room but is not allowed to
if @room.owned_by?(current_user) && !current_user.role.get_permission("can_create_rooms")
return redirect_to cant_create_rooms_path
end
# User is allowed to have rooms
@search, @order_column, @order_direction, recs =
recordings(@room.bbb_id, params.permit(:search, :column, :direction), true)
@pagy, @recordings = pagy_array(recs)
else
return redirect_to root_path, flash: { alert: I18n.t("room.invalid_provider") } if incorrect_user_domain
show_user_join
end
end
# GET /rooms
def cant_create_rooms
return redirect_to root_path unless current_user
shared_rooms = current_user.shared_rooms
if current_user.shared_rooms.empty?
# Render view for users that cant create rooms
@recent_rooms = Room.where(id: cookies.encrypted["#{current_user.uid}_recently_joined_rooms"])
render :cant_create_rooms
else
redirect_to shared_rooms[0]
end
end
# POST /:room_uid
def join
return redirect_to root_path,
flash: { alert: I18n.t("administrator.site_settings.authentication.user-info") } if auth_required
@shared_room = room_shared_with_user
unless @room.owned_by?(current_user) || @shared_room
# Don't allow users to join unless they have a valid access code or the room doesn't have an access codes
valid_access_code = !@room.access_code.present? || @room.access_code == session[:access_code]
if !valid_access_code && !valid_moderator_access_code(session[:moderator_access_code])
return redirect_to room_path(room_uid: params[:room_uid]), flash: { alert: I18n.t("room.access_code_required") }
end
# Assign join name if passed.
if params[@room.invite_path]
@join_name = params[@room.invite_path][:join_name]
elsif !params[:join_name]
# Join name not passed.
return redirect_to root_path
end
end
# create or update cookie with join name
cookies.encrypted[:greenlight_name] = @join_name unless cookies.encrypted[:greenlight_name] == @join_name
save_recent_rooms
logger.info "Support: #{current_user.present? ? current_user.email : @join_name} is joining room #{@room.uid}"
join_room(default_meeting_options)
end
# DELETE /:room_uid
def destroy
begin
# Don't delete the users home room.
raise I18n.t("room.delete.home_room") if @room == @room.owner.main_room
# Destroy all recordings then permanently delete the room
delete_all_recordings(@room.bbb_id)
@room.destroy(true)
rescue => e
flash[:alert] = I18n.t("room.delete.fail", error: e)
else
flash[:success] = I18n.t("room.delete.success")
end
# Redirect to home room if the redirect_back location is the deleted room
return redirect_to @current_user.main_room if request.referer == room_url(@room)
# Redirect to the location that the user deleted the room from
redirect_back fallback_location: current_user.main_room
end
# POST /room/join
def join_specific_room
room_uid = params[:join_room][:url].split('/').last
@room = Room.find_by(uid: room_uid)
return redirect_to cant_create_rooms_path, alert: I18n.t("room.no_room.invalid_room_uid") unless @room
redirect_to room_path(@room)
end
# POST /:room_uid/start
def start
logger.info "Support: #{current_user.email} is starting room #{@room.uid}"
# Join the user in and start the meeting.
opts = default_meeting_options
opts[:user_is_moderator] = true
# Include the user's choices for the room settings
@room_settings = JSON.parse(@room[:room_settings])
opts[:mute_on_start] = room_setting_with_config("muteOnStart")
opts[:require_moderator_approval] = room_setting_with_config("requireModeratorApproval")
opts[:record] = record_meeting
begin
redirect_to join_path(@room, current_user.name, opts, current_user.uid)
rescue BigBlueButton::BigBlueButtonException => e
logger.error("Support: #{@room.uid} start failed: #{e}")
redirect_to room_path, alert: I18n.t(e.key.to_s.underscore, default: I18n.t("bigbluebutton_exception"))
end
# Notify users that the room has started.
# Delay 5 seconds to allow for server start, although the request will retry until it succeeds.
NotifyUserWaitingJob.set(wait: 5.seconds).perform_later(@room)
end
# POST /:room_uid/update_settings
def update_settings
begin
options = params[:room].nil? ? params : params[:room]
raise "Room name can't be blank" if options[:name].blank?
# Update the rooms values
room_settings_string = create_room_settings_string(options)
attributes = {
name: options[:name],
}
unless params[:setting] == "rename_header"
attributes[:room_settings] = room_settings_string
attributes[:access_code] = options[:access_code]
attributes[:moderator_access_code] = options[:moderator_access_code]
end
@room.update(attributes)
flash[:success] = I18n.t("room.update_settings_success")
rescue => e
logger.error "Support: Error in updating room settings: #{e}"
flash[:alert] = I18n.t("room.update_settings_error")
end
redirect_back fallback_location: room_path(@room)
end
# GET /:room_uid/current_presentation
def current_presentation
attached = @room.presentation.attached?
# Respond with JSON object of presentation name
respond_to do |format|
format.json { render body: { attached: attached, name: attached ? @room.presentation.filename.to_s : "" }.to_json }
end
end
# POST /:room_uid/preupload_presenstation
def preupload_presentation
begin
raise "Invalid file type" unless valid_file_type
@room.presentation.attach(room_params[:presentation])
flash[:success] = I18n.t("room.preupload_success")
rescue => e
logger.error "Support: Error in updating room presentation: #{e}"
flash[:alert] = I18n.t("room.preupload_error")
end
redirect_back fallback_location: room_path(@room)
end
# POST /:room_uid/remove_presenstation
def remove_presentation
begin
@room.presentation.purge
flash[:success] = I18n.t("room.preupload_remove_success")
rescue => e
logger.error "Support: Error in removing room presentation: #{e}"
flash[:alert] = I18n.t("room.preupload_remove_error")
end
redirect_back fallback_location: room_path(@room)
end
# POST /:room_uid/update_shared_access
def shared_access
begin
current_list = @room.shared_users.pluck(:id)
new_list = User.where(uid: params[:add]).pluck(:id)
# Get the list of users that used to be in the list but were removed
users_to_remove = current_list - new_list
# Get the list of users that are in the new list but not in the current list
users_to_add = new_list - current_list
# Remove users that are removed
SharedAccess.where(room_id: @room.id, user_id: users_to_remove).delete_all unless users_to_remove.empty?
# Add users that are added
users_to_add.each do |id|
SharedAccess.create(room_id: @room.id, user_id: id)
end
flash[:success] = I18n.t("room.shared_access_success")
rescue => e
logger.error "Support: Error in updating room shared access: #{e}"
flash[:alert] = I18n.t("room.shared_access_error")
end
redirect_back fallback_location: room_path
end
# POST /:room_uid/remove_shared_access
def remove_shared_access
begin
SharedAccess.find_by!(room_id: @room.id, user_id: current_user).destroy
flash[:success] = I18n.t("room.remove_shared_access_success")
rescue => e
logger.error "Support: Error in removing room shared access: #{e}"
flash[:alert] = I18n.t("room.remove_shared_access_error")
end
redirect_to current_user.main_room
end
# GET /:room_uid/shared_users
def shared_users
# Respond with JSON object of users that have access to the room
respond_to do |format|
format.json { render body: @room.shared_users.pluck_to_hash(:uid, :name, :image).to_json }
end
end
# GET /:room_uid/room_settings
def room_settings
# Respond with JSON object of the room_settings
respond_to do |format|
format.json { render body: @room.room_settings }
end
end
# GET /:room_uid/logout
def logout
logger.info "Support: #{current_user.present? ? current_user.email : 'Guest'} has left room #{@room.uid}"
# Redirect the correct page.
redirect_to @room
end
# POST /:room_uid/login
def login
# use same form for access_code and moderator_access_code
if valid_moderator_access_code(room_params[:access_code])
session[:moderator_access_code] = room_params[:access_code]
else
session[:access_code] = room_params[:access_code]
end
if session[:access_code] != @room.access_code && !valid_moderator_access_code(session[:moderator_access_code])
flash[:alert] = I18n.t("room.access_code_required")
end
redirect_to room_path(@room.uid)
end
private
def create_room_settings_string(options)
room_settings = {
muteOnStart: options[:mute_on_join] == "1",
requireModeratorApproval: options[:require_moderator_approval] == "1",
anyoneCanStart: options[:anyone_can_start] == "1",
joinModerator: options[:all_join_moderator] == "1",
recording: options[:recording] == "1",
}
room_settings.to_json
end
def room_params
params.require(:room).permit(:name, :auto_join, :mute_on_join, :access_code,
:require_moderator_approval, :anyone_can_start, :all_join_moderator,
:recording, :presentation, :moderator_access_code)
end
# Find the room from the uid.
def find_room
@room = Room.includes(:owner).find_by!(uid: params[:room_uid])
end
# Ensure the user either owns the room or is an admin of the room owner or the room is shared with him
def verify_room_ownership_or_admin_or_shared
return redirect_to root_path unless @room.owned_by?(current_user) ||
room_shared_with_user ||
current_user&.admin_of?(@room.owner, "can_manage_rooms_recordings")
end
# Ensure the user either owns the room or is an admin of the room owner
def verify_room_ownership_or_admin
return redirect_to root_path if !@room.owned_by?(current_user) &&
!current_user&.admin_of?(@room.owner, "can_manage_rooms_recordings")
end
# Ensure the user owns the room or is allowed to start it
def verify_room_ownership_or_shared
return redirect_to root_path unless @room.owned_by?(current_user) || room_shared_with_user
end
def validate_accepted_terms
redirect_to terms_path if current_user && !current_user&.accepted_terms
end
def validate_verified_email
redirect_to account_activation_path(digest: current_user.activation_digest) if current_user && !current_user&.activated?
end
def verify_room_owner_verified
redirect_to root_path, alert: t("room.unavailable") unless @room.owner.activated?
end
# Check to make sure the room owner is not pending or banned
def verify_room_owner_valid
redirect_to root_path, alert: t("room.owner_banned") if @room.owner.has_role?(:pending) || @room.owner.has_role?(:denied)
end
def verify_user_not_admin
redirect_to admins_path if current_user&.has_role?(:super_admin)
end
def auth_required
@settings.get_value("Room Authentication") == "true" && current_user.nil?
end
# Checks if the room is shared with the user and room sharing is enabled
def room_shared_with_user
shared_access_allowed ? @room.shared_with?(current_user) : false
end
def room_limit_exceeded
limit = @settings.get_value("Room Limit").to_i
# Does not apply to admin or users that aren't signed in
# 15+ option is used as unlimited
return false if current_user&.has_role?(:admin) || limit == 15
current_user.rooms.length >= limit
end
helper_method :room_limit_exceeded
def valid_moderator_access_code(code)
code == @room.moderator_access_code && !@room.moderator_access_code.blank? && moderator_code_allowed?
end
helper_method :valid_moderator_access_code
def record_meeting
# If the require consent setting is checked, then check the room setting, else, set to true
user = current_user || @room.owner
if recording_consent_required?
room_setting_with_config("recording") && user&.role&.get_permission("can_launch_recording")
else
user&.role&.get_permission("can_launch_recording")
end
end
# Checks if the file extension is allowed
def valid_file_type
Rails.configuration.allowed_file_types.split(",")
.include?(File.extname(room_params[:presentation].original_filename.downcase))
end
# Gets the room setting based on the option set in the room configuration
def room_setting_with_config(name)
config = case name
when "muteOnStart"
"Room Configuration Mute On Join"
when "requireModeratorApproval"
"Room Configuration Require Moderator"
when "joinModerator"
"Room Configuration All Join Moderator"
when "anyoneCanStart"
"Room Configuration Allow Any Start"
when "recording"
"Room Configuration Recording"
end
case @settings.get_value(config)
when "enabled"
true
when "optional"
@room_settings[name]
when "disabled"
false
end
end
helper_method :room_setting_with_config
end

View File

@@ -0,0 +1,293 @@
# 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 SessionsController < ApplicationController
include Authenticator
include Registrar
include Emailer
include LdapAuthenticator
skip_before_action :verify_authenticity_token, only: [:omniauth, :fail]
before_action :check_user_signup_allowed, only: [:new]
before_action :ensure_unauthenticated_except_twitter, only: [:new, :signin, :ldap_signin]
# GET /signin
def signin
check_if_twitter_account
@providers = configured_providers
if one_provider
provider_path = if Rails.configuration.omniauth_ldap
ldap_signin_path
else
"#{Rails.configuration.relative_url_root}/auth/#{@providers.first}"
end
redirect_post(provider_path, options: { authenticity_token: :auto })
end
end
# GET /ldap_signin
def ldap_signin
end
# GET /signup
def new
# Check if the user needs to be invited
if invite_registration
redirect_to root_path, flash: { alert: I18n.t("registration.invite.no_invite") } unless params[:invite_token]
session[:invite_token] = params[:invite_token]
end
check_if_twitter_account(true)
@user = User.new
end
# POST /users/login
def create
logger.info "Support: #{session_params[:email]} is attempting to login."
user = User.include_deleted.find_by(email: session_params[:email].downcase)
is_super_admin = user&.has_role? :super_admin
# Scope user to domain if the user is not a super admin
user = User.include_deleted.find_by(email: session_params[:email].downcase, provider: @user_domain) unless is_super_admin
# Check user with that email exists
return redirect_to(signin_path, alert: I18n.t("invalid_credentials")) unless user
# Check if authenticators have switched
return switch_account_to_local(user) if !is_super_admin && auth_changed_to_local?(user)
# Check correct password was entered
unless user.try(:authenticate, session_params[:password])
logger.info "Support: #{session_params[:email]} login failed."
return redirect_to(signin_path, alert: I18n.t("invalid_credentials"))
end
# Check that the user is not deleted
return redirect_to root_path, flash: { alert: I18n.t("registration.banned.fail") } if user.deleted?
unless is_super_admin
# Check that the user is a Greenlight account
return redirect_to(root_path, alert: I18n.t("invalid_login_method")) unless user.greenlight_account?
# Check that the user has verified their account
unless user.activated?
user.create_activation_token if user.activation_digest.nil?
return redirect_to(account_activation_path(digest: user.activation_digest))
end
end
return redirect_to edit_password_reset_path(user.create_reset_digest),
flash: { alert: I18n.t("registration.insecure_password") } unless User.secure_password?(session_params[:password])
login(user)
end
# POST /users/logout
def destroy
logout
redirect_to root_path
end
# GET/POST /auth/:provider/callback
def omniauth
@auth = request.env['omniauth.auth']
begin
process_signin
rescue => e
logger.error "Error authenticating via omniauth: #{e}"
omniauth_fail
end
end
# POST /auth/failure
def omniauth_fail
if params[:message].nil?
redirect_to root_path, alert: I18n.t("omniauth_error")
else
redirect_to root_path, alert: I18n.t("omniauth_specific_error", error: params["message"])
end
end
# GET /auth/ldap
def ldap
ldap_config = {}
ldap_config[:host] = ENV['LDAP_SERVER']
ldap_config[:port] = ENV['LDAP_PORT'].to_i.zero? ? 389 : ENV['LDAP_PORT'].to_i
ldap_config[:bind_dn] = ENV['LDAP_BIND_DN']
ldap_config[:password] = ENV['LDAP_PASSWORD']
ldap_config[:auth_method] = ENV['LDAP_AUTH']
ldap_config[:encryption] = case ENV['LDAP_METHOD']
when 'ssl'
'simple_tls'
when 'tls'
'start_tls'
end
ldap_config[:base] = ENV['LDAP_BASE']
ldap_config[:filter] = ENV['LDAP_FILTER']
ldap_config[:uid] = ENV['LDAP_UID']
if params[:session][:username].blank? || session_params[:password].blank?
return redirect_to(ldap_signin_path, alert: I18n.t("invalid_credentials_external"))
end
result = send_ldap_request(params[:session], ldap_config)
return redirect_to(ldap_signin_path, alert: I18n.t("invalid_credentials_external")) unless result
@auth = parse_auth(result.first, ENV['LDAP_ROLE_FIELD'], ENV['LDAP_ATTRIBUTE_MAPPING'])
begin
process_signin
rescue => e
logger.error "Support: Error authenticating via omniauth: #{e}"
omniauth_fail
end
end
private
# Verify that GreenLight is configured to allow user signup.
def check_user_signup_allowed
redirect_to root_path unless Rails.configuration.allow_user_signup
end
def session_params
params.require(:session).permit(:email, :password)
end
def one_provider
(!allow_user_signup? || !allow_greenlight_accounts?) && @providers.count == 1 &&
!Rails.configuration.loadbalanced_configuration
end
def check_user_exists
User.exists?(social_uid: @auth['uid'], provider: current_provider)
end
def check_user_deleted(email)
User.deleted.exists?(email: email, provider: @user_domain)
end
def check_auth_deleted
User.deleted.exists?(social_uid: @auth['uid'], provider: current_provider)
end
def current_provider
@auth['provider'] == "bn_launcher" ? @auth['info']['customer'] : @auth['provider']
end
# Check if the user already exists, if not then check for invitation
def passes_invite_reqs
return true if @user_exists
invitation = check_user_invited("", session[:invite_token], @user_domain)
invitation[:present]
end
def process_signin
@user_exists = check_user_exists
if !@user_exists && @auth['provider'] == "twitter"
return redirect_to root_path, flash: { alert: I18n.t("registration.deprecated.twitter_signup") }
end
# Check if user is deleted
return redirect_to root_path, flash: { alert: I18n.t("registration.banned.fail") } if check_auth_deleted
# If using invitation registration method, make sure user is invited
return redirect_to root_path, flash: { alert: I18n.t("registration.invite.no_invite") } unless passes_invite_reqs
# Switch the user to a social account if they exist under the same email with no social uid
switch_account_to_social if !@user_exists && auth_changed_to_social?(@auth['info']['email'])
user = User.from_omniauth(@auth)
logger.info "Support: Auth user #{user.email} is attempting to login."
# Add pending role if approval method and is a new user
if approval_registration && !@user_exists
user.set_role :pending
# Inform admins that a user signed up if emails are turned on
send_approval_user_signup_email(user)
return redirect_to root_path, flash: { success: I18n.t("registration.approval.signup") }
end
send_invite_user_signup_email(user) if invite_registration && !@user_exists
user.set_role(initial_user_role(user.email)) if !@user_exists && user.role.nil?
login(user)
if @auth['provider'] == "twitter"
flash[:alert] = if allow_user_signup? && allow_greenlight_accounts?
I18n.t("registration.deprecated.twitter_signin", link: signup_path(old_twitter_user_id: user.id))
else
I18n.t("registration.deprecated.twitter_signin", link: signin_path(old_twitter_user_id: user.id))
end
end
end
# Send the user a password reset email to allow them to set their password
def switch_account_to_local(user)
logger.info "Switching social account to local account for #{user.uid}"
# Send the user a reset password email
send_password_reset_email(user, user.create_reset_digest)
# Overwrite the flash with a more descriptive message if successful
flash[:success] = I18n.t("reset_password.auth_change") if flash[:success].present?
redirect_to signin_path
end
# Set the user's social id to the new id being passed
def switch_account_to_social
user = User.find_by({
email: @auth['info']['email'],
provider: Rails.configuration.loadbalanced_configuration ? @user_domain : nil
}.compact)
logger.info "Switching social account for #{user.uid}"
# Set the user's social id to the one being returned from auth
user.update_attribute(:social_uid, @auth['uid'])
end
def ldap_encryption
encryption_method = case ENV['LDAP_METHOD']
when 'ssl'
'simple_tls'
when 'tls'
'start_tls'
end
{
method: encryption_method,
tls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
}
end
end

View File

@@ -0,0 +1,42 @@
# 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 ThemesController < ApplicationController
skip_before_action :block_unknown_hosts, :redirect_to_https, :maintenance_mode?, :migration_error?, :user_locale,
:check_admin_password, :check_user_role
# GET /primary
def index
color = @settings.get_value("Primary Color") || Rails.configuration.primary_color_default
lighten_color = @settings.get_value("Primary Color Lighten") || Rails.configuration.primary_color_lighten_default
darken_color = @settings.get_value("Primary Color Darken") || Rails.configuration.primary_color_darken_default
file_name = Rails.root.join('lib', 'assets', '_primary_themes.scss')
@file_contents = File.read(file_name)
# Include the variables and covert scss file to css
@compiled = SassC::Engine.new("$primary-color:#{color};" \
"$primary-color-lighten:#{lighten_color};" \
"$primary-color-darken:#{darken_color};" +
@file_contents, syntax: :scss).render
respond_to do |format|
format.css { render body: @compiled }
end
end
end

View File

@@ -0,0 +1,257 @@
# 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 UsersController < ApplicationController
include Pagy::Backend
include Authenticator
include Emailer
include Registrar
include Recorder
include Rolify
before_action :find_user, only: [:edit, :change_password, :delete_account, :update, :update_password]
before_action :ensure_unauthenticated_except_twitter, only: [:create]
before_action :check_user_signup_allowed, only: [:create]
before_action :check_admin_of, only: [:edit, :change_password, :delete_account]
# POST /u
def create
@user = User.new(user_params)
@user.provider = @user_domain
# User or recpatcha is not valid
render("sessions/new") && return unless valid_user_or_captcha
# Redirect to root if user token is either invalid or expired
return redirect_to root_path, flash: { alert: I18n.t("registration.invite.fail") } unless passes_invite_reqs
# User has passed all validations required
@user.save
logger.info "Support: #{@user.email} user has been created."
# Set user to pending and redirect if Approval Registration is set
if approval_registration
@user.set_role :pending
return redirect_to root_path,
flash: { success: I18n.t("registration.approval.signup") } unless Rails.configuration.enable_email_verification
end
send_registration_email
# Sign in automatically if email verification is disabled or if user is already verified.
if !Rails.configuration.enable_email_verification || @user.email_verified
@user.set_role(initial_user_role(@user.email))
login(@user) && return
end
send_activation_email(@user, @user.create_activation_token)
redirect_to root_path
end
# GET /u/:user_uid/edit
def edit
redirect_to root_path unless current_user
end
# GET /u/:user_uid/change_password
def change_password
redirect_to edit_user_path unless current_user.greenlight_account?
end
# GET /u/:user_uid/delete_account
def delete_account
redirect_to signin_path unless current_user
end
# POST /u/:user_uid/edit
def update
if session[:prev_url].present?
path = session[:prev_url]
session.delete(:prev_url)
else
path = admins_path
end
redirect_path = current_user.admin_of?(@user, "can_manage_users") ? path : edit_user_path(@user)
unless can_edit_user?(@user, current_user)
params[:user][:name] = @user.name
params[:user][:email] = @user.email
end
old_user_email = @user.email
if @user.without_terms_acceptance { @user.update_attributes(user_params) }
@user.without_terms_acceptance {
@user.update_attributes(email_verified: false) if user_params[:email] != old_user_email
user_locale(@user)
}
if @user.without_terms_acceptance { update_roles(params[:user][:role_id]) }
return redirect_to redirect_path, flash: { success: I18n.t("info_update_success") }
else
flash[:alert] = I18n.t("administrator.roles.invalid_assignment")
end
end
render :edit
end
# POST /u/:user_uid/change_password
def update_password
# Update the users password.
if @user.authenticate(user_params[:old_password])
# Bad UX on client side [FIXED].
@user.assign_attributes user_params.slice(:password, :password_confirmation)
else
# Original password is incorrect, can't update.
@user.errors.add(:old_password, I18n.t("old_password_incorrect"))
end
# Notify the user that their account has been updated.
if @user.errors.empty? && @user.without_terms_acceptance {
@user.save && @user.update(last_pwd_update: Time.zone.now)
}
# Changing the password has to update the last_pwd_update to match the event timestamp.
# The activated_at session metadata has to match it too to reset the state of the active session
# making this change password request.
# This keeps only this session alive.
session[:activated_at] = @user.last_pwd_update.to_i if current_user.id == @user.id
return redirect_to change_password_path, flash: { success: I18n.t("info_update_success") }
end
# redirect_to change_password_path
render :change_password
end
# DELETE /u/:user_uid
def destroy
# Include deleted users in the check
admin_path = request.referer.present? ? request.referer : admins_path
@user = User.include_deleted.find_by(uid: params[:user_uid])
logger.info "Support: #{current_user.email} is deleting #{@user.email}."
self_delete = current_user == @user
redirect_url = self_delete ? root_path : admin_path
begin
if current_user && (self_delete || current_user.admin_of?(@user, "can_manage_users"))
# Permanently delete if the user is deleting themself
perm_delete = self_delete || (params[:permanent].present? && params[:permanent] == "true")
# Permanently delete the rooms under the user if they have not been reassigned
if perm_delete
@user.rooms.include_deleted.each do |room|
# Destroy all recordings then permanently delete the room
delete_all_recordings(room.bbb_id)
room.destroy(true)
end
end
@user.destroy(perm_delete)
# Log the user out if they are deleting themself
session.delete(:user_id) if self_delete
return redirect_to redirect_url, flash: { success: I18n.t("administrator.flash.delete") } unless self_delete
else
flash[:alert] = I18n.t("administrator.flash.delete_fail")
end
rescue => e
logger.error "Support: Error in user deletion: #{e}"
flash[:alert] = I18n.t(params[:message], default: I18n.t("administrator.flash.delete_fail"))
end
redirect_to redirect_url
end
# GET /u/:user_uid/recordings
def recordings
if current_user && current_user.uid == params[:user_uid]
@search, @order_column, @order_direction, recs =
all_recordings(current_user.rooms.pluck(:bbb_id), params.permit(:search, :column, :direction), true)
@pagy, @recordings = pagy_array(recs)
else
redirect_to root_path
end
end
# GET | POST /terms
def terms
redirect_to '/404' unless Rails.configuration.terms
if params[:accept] == "true"
current_user.update_attributes(accepted_terms: true)
login(current_user)
end
end
# GET /shared_access_list
def shared_access_list
# Don't allow searchs unless atleast 3 characters are passed
return redirect_to '/404' unless params[:search].strip.length >= 3
roles_can_appear = []
Role.where(provider: @user_domain).each do |role|
roles_can_appear << role.name if role.get_permission("can_appear_in_share_list") && role.priority >= 0
end
initial_list = User.where.not(uid: params[:owner_uid])
.with_role(roles_can_appear)
.shared_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).to_json }
end
end
private
def find_user
@user = User.find_by(uid: params[:user_uid])
end
# Verify that GreenLight is configured to allow user signup.
def check_user_signup_allowed
redirect_to root_path unless Rails.configuration.allow_user_signup
end
def user_params
params.require(:user).permit(:name, :email, :image, :password, :password_confirmation,
:old_password, :accepted_terms, :language)
end
def send_registration_email
if invite_registration
send_invite_user_signup_email(@user)
elsif approval_registration
send_approval_user_signup_email(@user)
end
end
# Checks that the user is allowed to edit this user
def check_admin_of
redirect_to root_path if current_user &&
@user != current_user &&
!current_user.admin_of?(@user, "can_manage_users")
end
end

Some files were not shown because too many files have changed in this diff Show More