Compare commits

..

No commits in common. "master" and "v1" have entirely different histories.
master ... v1

454 changed files with 7177 additions and 48320 deletions

View File

@ -2,11 +2,10 @@
.git
.gitignore
README.md
SECURITY.md
.github
# Rails
.env
.env.prod
*.rbc
capybara-*.html
.rspec
@ -14,16 +13,14 @@ log
tmp
/db/**/*.sqlite3
/db/**/*.sqlite3-journal
/db/production
/db/production-postgres
public/assets
public/b
public/system
coverage/
spec/tmp
.rvmrc
vendor/bundle
.bundle
Dockerfile
.gitlab-ci.yml
.rubocop.yml
spec
test
.byebug_history
# docker
docker-compose.yml
production-compose.yml
.atom

View File

@ -1,16 +0,0 @@
<!---
IMPORTANT
This template is mandatory for all Pull Requests.
Please follow the template to ensure your Pull Request is reviewed.
-->
<!--- Provide a general summary of your changes in the Title above -->
## Description
<!--- Describe your changes in detail -->
## Testing Steps
<!--- Please describe in detail how to test your changes. -->
## Screenshots (if appropriate):
<!--- Please include screenshots that may help to visualize your changes. -->

View File

@ -1,95 +0,0 @@
env:
RUBY_VERSION: 2.7
name: CI Build Pre-Release
on:
release:
types: [prereleased]
jobs:
main:
name: Build Docker Image
env:
DOCKER_REPOSITORY: ${{ secrets.DOCKER_REPOSITORY }}
DOCKER_BUILD_ENABLED: ${{ secrets.DOCKER_BUILD_ENABLED }}
DOCKER_BUILD_ALTERNATE_ENABLED: ${{ secrets.DOCKER_BUILD_ALTERNATE_ENABLED }}
runs-on: ubuntu-20.04
steps:
- name: Checkout
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: actions/checkout@v2
- name: Set up Docker Buildx
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: docker/setup-buildx-action@v1
- name: Cache Docker layers
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to DockerHub
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract Docker Repository
id: ci_docker_repository
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
shell: bash
run: echo "##[set-output name=repository;]$(echo ${DOCKER_REPOSITORY:-$GITHUB_REPOSITORY})"
- name: Extract Tag Release
id: ci_tag_release_version
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
shell: bash
run: echo "##[set-output name=tag;]$(echo ${GITHUB_REF#refs/tags/} | cut -c 9-)"
- name: Build and Push
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: docker/build-push-action@v2
with:
push: true
tags: |
"${{ steps.ci_docker_repository.outputs.repository }}:v${{ steps.ci_tag_release_version.outputs.tag }}"
build-args: "version_code=release-${{ steps.ci_tag_release_version.outputs.tag }}"
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
# Alternate Images with alpine
- name: Alternate Alpine Build and Push
if: contains(env.DOCKER_BUILD_ENABLED, 'true') && contains(env.DOCKER_BUILD_ALTERNATE_ENABLED, 'true')
uses: docker/build-push-action@v2
with:
file: dockerfiles/v2/alpine
push: true
tags: |
"${{ steps.ci_docker_repository.outputs.repository }}:v${{ steps.ci_tag_release_version.outputs.tag }}-alpine"
build-args: "version_code=release-${{ steps.ci_tag_release_version.outputs.tag }}"
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
# Alternate Images with amazonlinux
- name: Alternate Amazon Linux Build and Push
if: contains(env.DOCKER_BUILD_ENABLED, 'true') && contains(env.DOCKER_BUILD_ALTERNATE_ENABLED, 'true')
uses: docker/build-push-action@v2
with:
file: dockerfiles/v2/amazonlinux
push: true
tags: |
"${{ steps.ci_docker_repository.outputs.repository }}:v${{ steps.ci_tag_release_version.outputs.tag }}-amazonlinux"
build-args: "version_code=release-${{ steps.ci_tag_release_version.outputs.tag }}"
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
- name: Move cache
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache

View File

@ -1,101 +0,0 @@
env:
RUBY_VERSION: 2.7
name: CI Build Push
on:
push:
branches: "*"
jobs:
main:
name: Build Docker Image
env:
DOCKER_REPOSITORY: ${{ secrets.DOCKER_REPOSITORY }}
DOCKER_BUILD_ENABLED: ${{ secrets.DOCKER_BUILD_ENABLED }}
DOCKER_BUILD_ALTERNATE_ENABLED: ${{ secrets.DOCKER_BUILD_ALTERNATE_ENABLED }}
runs-on: ubuntu-20.04
steps:
- name: Checkout
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: actions/checkout@v2
- name: Set up Docker Buildx
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: docker/setup-buildx-action@v1
- name: Cache Docker layers
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to DockerHub
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract Docker Repository
id: ci_docker_repository
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
shell: bash
run: echo "##[set-output name=repository;]$(echo ${DOCKER_REPOSITORY:-$GITHUB_REPOSITORY})"
- name: Extract Branch Name
id: ci_branch_name
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
shell: bash
run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
- name: Extract Commit Short SHA
id: ci_commit_short_sha
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
shell: bash
run: echo "##[set-output name=short_sha;]$(echo ${GITHUB_SHA} | cut -c1-7)"
- name: Build and Push
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: docker/build-push-action@v2
with:
push: true
tags: |
"${{ steps.ci_docker_repository.outputs.repository }}:${{ steps.ci_branch_name.outputs.branch }}"
build-args: "version_code=${{ steps.ci_branch_name.outputs.branch }}-${{ steps.ci_commit_short_sha.outputs.short_sha }}"
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
# Alternate Image with alpine
- name: Alternate Alpine Build and Push
if: contains(env.DOCKER_BUILD_ENABLED, 'true') && contains(env.DOCKER_BUILD_ALTERNATE_ENABLED, 'true')
uses: docker/build-push-action@v2
with:
file: dockerfiles/v2/alpine
push: true
tags: |
"${{ steps.ci_docker_repository.outputs.repository }}:${{ steps.ci_branch_name.outputs.branch }}-alpine"
build-args: "version_code=${{ steps.ci_branch_name.outputs.branch }}-${{ steps.ci_commit_short_sha.outputs.short_sha }}"
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
# Alternate Images for bbb-bionic-230 with amazonlinux
- name: Alternate Amazon Linux Build and Push
if: contains(env.DOCKER_BUILD_ENABLED, 'true') && contains(env.DOCKER_BUILD_ALTERNATE_ENABLED, 'true')
uses: docker/build-push-action@v2
with:
file: dockerfiles/v2/amazonlinux
push: true
tags: |
"${{ steps.ci_docker_repository.outputs.repository }}:${{ steps.ci_branch_name.outputs.branch }}-amazonlinux"
build-args: "version_code=${{ steps.ci_branch_name.outputs.branch }}-${{ steps.ci_commit_short_sha.outputs.short_sha }}"
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
- name: Move cache
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache

View File

@ -1,110 +0,0 @@
env:
RUBY_VERSION: 2.7
name: CI Build Release
on:
release:
types: [released]
jobs:
main:
name: Build Docker Image
env:
DOCKER_REPOSITORY: ${{ secrets.DOCKER_REPOSITORY }}
DOCKER_BUILD_ENABLED: ${{ secrets.DOCKER_BUILD_ENABLED }}
DOCKER_BUILD_ALTERNATE_ENABLED: ${{ secrets.DOCKER_BUILD_ALTERNATE_ENABLED }}
runs-on: ubuntu-20.04
steps:
- name: Checkout
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: actions/checkout@v2
- name: Set up Docker Buildx
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: docker/setup-buildx-action@v1
- name: Cache Docker layers
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to DockerHub
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract Docker Repository
id: ci_docker_repository
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
shell: bash
run: echo "##[set-output name=repository;]$(echo ${DOCKER_REPOSITORY:-$GITHUB_REPOSITORY})"
- name: Extract Tag Release
id: ci_tag_release
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
shell: bash
run: echo "##[set-output name=tag;]$(echo ${GITHUB_REF#refs/tags/})"
- name: Extract Tag Release Major
id: ci_tag_release_major
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
shell: bash
run: echo "##[set-output name=tag;]$(echo ${{steps.ci_tag_release.outputs.tag}} | cut -c 9- | cut -f1-1 -d'.')"
- name: Extract Tag Release Minor
id: ci_tag_release_minor
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
shell: bash
run: echo "##[set-output name=tag;]$(echo ${{steps.ci_tag_release.outputs.tag}} | cut -c 9- | cut -f1-2 -d'.')"
- name: Build and Push
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
uses: docker/build-push-action@v2
with:
push: true
tags: |
"${{ steps.ci_docker_repository.outputs.repository }}:${{ steps.ci_tag_release.outputs.tag }}"
"${{ steps.ci_docker_repository.outputs.repository }}:v${{ steps.ci_tag_release_major.outputs.tag }}"
"${{ steps.ci_docker_repository.outputs.repository }}:v${{ steps.ci_tag_release_minor.outputs.tag }}"
"${{ steps.ci_docker_repository.outputs.repository }}:latest"
build-args: "version_code=${{ steps.ci_tag_release_revision.outputs.tag }}"
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
# Alternate Image with alpine
- name: Alternate Alpine Build and Push
if: contains(env.DOCKER_BUILD_ENABLED, 'true') && contains(env.DOCKER_BUILD_ALTERNATE_ENABLED, 'true')
uses: docker/build-push-action@v2
with:
file: dockerfiles/v2/alpine
push: true
tags: |
"${{ steps.ci_docker_repository.outputs.repository }}:${{ steps.ci_tag_release.outputs.tag }}-alpine"
build-args: "version_code=${{ steps.ci_tag_release_revision.outputs.tag }}"
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
# Alternate Image with amazonlinux
- name: Alternate Amazon Linux Build and Push
if: contains(env.DOCKER_BUILD_ENABLED, 'true') && contains(env.DOCKER_BUILD_ALTERNATE_ENABLED, 'true')
uses: docker/build-push-action@v2
with:
file: dockerfiles/v2/amazonlinux
push: true
tags: |
"${{ steps.ci_docker_repository.outputs.repository }}:${{ steps.ci_tag_release.outputs.tag }}-amazonlinux"
build-args: "version_code=${{ steps.ci_tag_release_revision.outputs.tag }}"
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new
- name: Move cache
if: contains(env.DOCKER_BUILD_ENABLED, 'true')
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache

View File

@ -1,68 +0,0 @@
env:
RUBY_VERSION: 2.7
DB_ADAPTER: postgresql
DB_HOST: localhost
DB_NAME: postgres
DB_USERNAME: postgres
DB_PASSWORD: postgres
DB_PORT: 5432
name: CI
on:
push:
branches-ignore: "master"
pull_request:
branches: "*"
jobs:
test:
name: Rubocop + RSpec
runs-on: ubuntu-18.04
services:
postgres:
image: postgres:13.2-alpine
env:
POSTGRES_DB: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
ports:
- 5432:5432
# Health checks to wait until postgres is ready
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install Ruby ${{ env.RUBY_VERSION }}
uses: actions/setup-ruby@v1
with:
ruby-version: ${{ env.RUBY_VERSION }}
- name: Bundle cache
uses: actions/cache@v2
with:
path: vendor/bundle
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gems-
- name: Bundle install
run: |
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- name: Setup database
run: |
bundler exec rails db:create RAILS_ENV=test
bundler exec rails db:migrate RAILS_ENV=test
- name: Run Rubocop
run: bundle exec rubocop --parallel --fail-level F
- name: Run RSpec
run: bundle exec rspec

38
.gitignore vendored
View File

@ -6,45 +6,21 @@
# Ignore bundler config.
/.bundle
vendor/.bundle
# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal
# Ignore static assets.
/db/**/*.sqlite3
/db/**/*.sqlite3-journal
/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
/tmp/*
!/log/.keep
!/tmp/.keep
.env
coverage
# 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

View File

@ -1,71 +0,0 @@
stages:
- test
- build
- deploy
cache:
paths:
- /cache
before_script:
test:
stage: test
image: ruby:2.5
script:
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
- bundle install --path /cache
- bundle exec rake db:create RAILS_ENV=test
- bundle exec rake test & bundle exec rspec & bundle exec rubocop
except:
variables:
- $CD_TEST_IGNORE
build:
stage: build
image: docker:stable
services:
- docker:dind
script:
# Install bash, curl, git for deployment script
- apk update && apk add --no-cache bash curl git
# Install CA certs, openssl to https downloads, python for gcloud sdk
- apk add --update make ca-certificates openssl python
- update-ca-certificates
# Build.
- ./scripts/image_build.sh $CI_PROJECT_PATH $CI_COMMIT_REF_NAME $CI_COMMIT_SHA
only:
refs:
- branches
- tags
variables:
- $CD_DOCKER_USERNAME
- $CD_DOCKER_PASSWORD
except:
variables:
- $CD_BUILD_IGNORE
deploy:
stage: deploy
image: docker:stable
services:
- docker:dind
script:
# Install bash, curl, git for deployment script
- apk update && apk add --no-cache bash curl git
# Install CA certs, openssl to https downloads, python for gcloud sdk
- apk add --update make ca-certificates openssl python
- update-ca-certificates
# Deploy.
- ./scripts/image_deploy.sh $CI_PROJECT_PATH $CI_COMMIT_REF_NAME $CI_COMMIT_SHA $CI_COMMIT_BEFORE_SHA
only:
refs:
- branches
- tags
variables:
- $CD_DOCKER_USERNAME
- $CD_DOCKER_PASSWORD
- $CD_DEPLOY_SCRIPT
except:
variables:
- $CD_DEPLOY_IGNORE

View File

@ -1,42 +0,0 @@
about
app:template
app:update
assets:clean[keep]
assets:clobber
assets:environment
assets:precompile
autoprefixer:info
cache_digests:dependencies
cache_digests:nested_dependencies
conf:check
db:create
db:drop
db:environment:set
db:fixtures:load
db:migrate
db:migrate:status
db:rollback
db:schema:cache:clear
db:schema:cache:dump
db:schema:dump
db:schema:load
db:seed
db:setup
db:structure:dump
db:structure:load
db:version
dev:cache
initializers
log:clear
middleware
notes
notes:custom
restart
routes
secret
stats
test
test:db
time:zones[country_or_offset]
tmp:clear
tmp:create

3
.rspec
View File

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

View File

@ -1,191 +0,0 @@
AllCops:
Exclude:
- 'db/schema.rb'
- 'vendor/**/*'
DisabledByDefault: false
TargetRubyVersion: 2.7
NewCops: enable
Style/BlockDelimiters:
Enabled: false
# Checks if uses of quotes match the configured preference.
Style/StringLiterals:
Enabled: false
# Document classes and non-namespace modules.
Style/Documentation:
Enabled: false
# Check for conditionals that can be replaced with guard clauses
Style/GuardClause:
Enabled: false
# Checks the formatting of empty method definitions.
Style/EmptyMethod:
Enabled: false
# Checks for trailing comma in hash literals.
Style/TrailingCommaInHashLiteral:
Enabled: false
# Checks for trailing comma in argument lists.
Style/TrailingCommaInArguments:
Enabled: false
# Checks that `include`, `extend` and `prepend` exists at the top level.
Style/MixinUsage:
Enabled: false
# Use %i or %I for arrays of symbols.
Style/SymbolArray:
Enabled: false
# Don't use begin blocks when they are not needed.
Style/RedundantBegin:
Enabled: false
# Use `%`-literal delimiters consistently
Style/PercentLiteralDelimiters:
Enabled: false
# Only use if/unless modifiers on single line statements.
Style/MultilineIfModifier:
Enabled: false
# Checks for trailing comma in array literals.
Style/TrailingCommaInArrayLiteral:
Enabled: false
# Use `expand_path(__dir__)` instead of `expand_path('..', __FILE__)`.
Style/ExpandPathArguments:
Enabled: false
# Do not assign mutable objects to constants.
Style/MutableConstant:
Enabled: false
# Avoid rescuing without specifying an error class.
Style/RescueStandardError:
Enabled: false
# Align the elements of a hash literal if they span more than one line.
Layout/HashAlignment:
Enabled: false
# Align the parameters of a method definition if they span more than one line.
Layout/ParameterAlignment:
Enabled: false
# Align ends corresponding to defs correctly.
Layout/EndAlignment:
Enabled: false
# Align elses and elsifs correctly.
Layout/ElseAlignment:
Enabled: false
# Add empty line after guard clause.
Layout/EmptyLineAfterGuardClause:
Enabled: false
# Align the arguments of a method call if they span more than one line.
Layout/ArgumentAlignment:
Enabled: false
#
Layout/IndentationWidth:
Enabled: false
Layout/CaseIndentation:
Enabled: false
# Checks for ambiguous block association with method when param passed without parentheses.
Lint/AmbiguousBlockAssociation:
Enabled: false
# Avoid long blocks with many lines.
Metrics/BlockLength:
Enabled: false
# A complexity metric geared towards measuring complexity for a human reader.
Metrics/PerceivedComplexity:
Max: 17
# Avoid classes longer than 100 lines of code.
Metrics/ClassLength:
Enabled: false
# Limit lines to 80 characters.
Layout/LineLength:
Max: 130
# Avoid methods longer than 10 lines of code.
Metrics/MethodLength:
Enabled: false
Metrics/ModuleLength:
Enabled: false
# A calculated magnitude based on number of assignments,
# branches, and conditions.
Metrics/AbcSize:
Max: 60
# A complexity metric that is strongly correlated to the number
# of test cases needed to validate a method.
Metrics/CyclomaticComplexity:
Max: 20
# Checks for method parameter names that contain capital letters, end in numbers, or do not meet a minimal length.
Naming/MethodParameterName:
Enabled: false
Lint/LiteralInInterpolation:
Enabled: false
Layout/EmptyLinesAroundAttributeAccessor:
Enabled: true
Layout/SpaceAroundMethodCallOperator:
Enabled: true
Lint/DeprecatedOpenSSLConstant:
Enabled: true
Lint/RaiseException:
Enabled: true
Lint/StructNewOverride:
Enabled: true
Style/ExponentialNotation:
Enabled: true
Style/HashEachMethods:
Enabled: true
Style/HashTransformKeys:
Enabled: true
Style/HashTransformValues:
Enabled: true
Style/SlicingWithRange:
Enabled: true
Style/OptionalBooleanParameter:
Enabled: false
Lint/DuplicateBranch:
Enabled: false
Lint/ConstantDefinitionInBlock:
Enabled: false
Lint/EmptyBlock:
Enabled: false
Style/HashLikeCase:
Enabled: false

View File

@ -1 +0,0 @@
2.7.2

View File

@ -1,74 +1,20 @@
FROM ruby:2.7.2-alpine AS base
FROM ruby:2.4.0
# 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"
# app dependencies
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
# 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 \
&& rm -rf vendor/bundle/ruby/2.7.0/cache/*.gem \
&& find vendor/bundle/ruby/2.7.0/gems/ -name "*.c" -delete \
&& find vendor/bundle/ruby/2.7.0/gems/ -name "*.o" -delete
# Adding project files.
COPY . .
# Remove folders not needed in resulting image
RUN rm -rf tmp/cache spec
############### Build step done ###############
FROM ruby:2.7.2-alpine
# Set a variable for the install location.
ARG RAILS_ROOT=/usr/src/app
ARG PACKAGES="tzdata curl postgresql-client sqlite-libs yarn nodejs bash"
ENV RAILS_ENV=production
ENV BUNDLE_APP_CONFIG="$RAILS_ROOT/.bundle"
WORKDIR $RAILS_ROOT
RUN apk update \
&& apk upgrade \
&& apk add --update --no-cache $PACKAGES
ENV RAILS_ENV=production \
APP_HOME=/usr/src/app
COPY --from=base $RAILS_ROOT $RAILS_ROOT
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
# Expose port 80.
EXPOSE 80
# Add the greenlight app
ADD . $APP_HOME
# Sets the footer of greenlight application with current build version
ARG version_code
ENV VERSION_CODE=$version_code
# Install app dependencies
RUN bundle install --without development test doc --deployment --clean
RUN bundle exec rake assets:precompile --trace
# Set executable permission to start file
RUN chmod +x bin/start
# FIXME / to remove / https://github.com/nahi/httpclient/issues/445
RUN cat /etc/ssl/certs/ca-certificates.crt \
>/usr/src/app/vendor/bundle/ruby/2.7.0/gems/httpclient-2.8.3/lib/httpclient/cacert.pem
# Start the application.
CMD ["bin/start"]
CMD ["scripts/default_start.sh"]

10
Dockerfile.dev Normal file
View File

@ -0,0 +1,10 @@
FROM ruby:2.4.0
# app dependencies
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
ENV RAILS_ENV=development \
APP_HOME=/usr/src/app
RUN mkdir $APP_HOME
WORKDIR $APP_HOME

155
Gemfile
View File

@ -1,79 +1,98 @@
# 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
ruby '2.4.0'
# 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', git: 'https://github.com/mconf/bigbluebutton-api-ruby.git', branch: 'master'
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.2'
gem 'jquery-rails', '~> 4.4.0'
gem 'jquery-ui-rails', '~> 6.0.1'
gem 'local_time', '~> 2.1.0'
gem 'net-ldap', '~> 0.17.0'
gem 'omniauth', '~> 1.9.1'
gem 'omniauth-bn-launcher', '~> 0.1.3'
gem 'omniauth-bn-office365', '~> 0.1.1'
gem 'omniauth-google-oauth2', '~> 0.7.0'
gem 'omniauth_openid_connect', '~> 0.3.5'
gem 'omniauth-twitter', '~> 1.4.0'
gem 'pagy', '~> 3.11.0'
gem 'pluck_to_hash', '~> 1.0.2'
gem 'puma', '~> 4.3.8'
gem 'rails', '~> 5.2.6'
gem 'random_password', '~> 0.1.1'
gem "recaptcha", '~> 5.7.0'
gem 'redcarpet', '~> 3.5.1'
gem 'remote_syslog_logger', '~> 1.0.4'
gem 'rubocop', '~> 1.10.0'
gem 'sassc-rails', '~> 2.1.2'
gem 'sprockets', '~> 3.7.2'
gem 'sqlite3', '~> 1.3.6'
gem 'tabler-rubygem', git: 'https://github.com/blindsidenetworks/tabler-rubygem.git', tag: '0.1.4.1'
gem 'turbolinks', '~> 5.2.1'
gem 'tzinfo-data', '~> 1.2021.1'
gem 'uglifier', '~> 4.2.0'
gem 'rails', '~> 5.0.0', '>= 5.0.0.1'
# Use sqlite3 as the database for Active Record
gem 'sqlite3'
# Use postgres as the database for Active Record
gem 'pg'
# Use Puma as the app server
gem 'puma', '~> 3.0'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# Use CoffeeScript for .coffee assets and views
gem 'coffee-rails', '~> 4.2'
# See https://github.com/rails/execjs#readme for more supported runtimes
gem 'therubyracer', platforms: :ruby
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
# Use jquery as the JavaScript library
gem 'jquery-rails'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 3.0'
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'
# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development
group :development, :test do
gem 'byebug', '~> 11.1', platform: :mri
gem 'dotenv-rails', '~> 2.7'
end
group :test do
gem 'action-cable-testing', '~> 0.6'
gem "factory_bot_rails", '~> 6.1'
gem 'faker', '~> 2.16'
gem 'rails-controller-testing', '~> 1.0'
gem 'rspec-rails', '~> 3.7'
gem 'shoulda-matchers', '~> 3.1'
gem 'webmock', '~> 3.11'
# For environment configuration
gem 'dotenv-rails'
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platform: :mri
end
group :development do
gem 'listen', '~> 3.0'
gem 'spring', '~> 2.1'
gem 'spring-watcher-listen', '~> 2.0'
gem 'web-console', '~> 3.7'
# Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
gem 'web-console'
gem 'listen', '~> 3.0.5'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
end
group :test do
gem 'mocha'
gem 'simplecov', :require => false
end
group :production do
# For more condensed logging
gem "lograge"
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
gem 'omniauth', '1.6.1'
gem 'omniauth-twitter', '1.2.1'
gem 'omniauth-google-oauth2', '0.4.1'
gem 'omniauth-ldap'
gem 'bigbluebutton-api-ruby'
gem 'bootstrap-sass', '3.3.0.0'
gem 'bootstrap-social-rails', '~> 4.12'
gem 'font-awesome-sass', '4.7.0'
gem 'jquery-ui-rails'
gem 'jquery-datatables-rails', '~> 3.4.0'
gem 'rails-timeago', '~> 2.0'
gem 'http_accept_language'
# For sending slack notifications.
gem 'slack-notifier'
# For landing background image uploading.
gem 'paperclip', '~> 5.1'
# For uploading recordings to Youtube.
gem 'yt', '~> 0.28.0'
# Simple HTTP client.
gem 'faraday'
# For SAML authentication
gem 'omniauth-saml'
# For device detection to determine BigBlueButton client.
gem 'browser'

View File

@ -1,521 +1,339 @@
GIT
remote: https://github.com/blindsidenetworks/tabler-rubygem.git
revision: 4c16e6dc930f6b92dec093abaf9652d768b46d97
tag: 0.1.4.1
specs:
tabler-rubygem (0.1.4.1)
autoprefixer-rails (>= 6.0.3)
GIT
remote: https://github.com/mconf/bigbluebutton-api-ruby.git
revision: 91dc495324a6b7e162773227ec3650f8a5b39c50
branch: master
specs:
bigbluebutton-api-ruby (1.7.0)
childprocess (>= 1.0.1)
ffi (>= 1.9.24)
json (>= 1.8.6)
nokogiri (>= 1.10.4)
rack (>= 1.6.11)
rubyzip (>= 1.3.0)
xml-simple (~> 1.1)
GEM
remote: https://rubygems.org/
specs:
action-cable-testing (0.6.1)
actioncable (>= 5.0)
actioncable (5.2.6)
actionpack (= 5.2.6)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailer (5.2.6)
actionpack (= 5.2.6)
actionview (= 5.2.6)
activejob (= 5.2.6)
actioncable (5.0.7)
actionpack (= 5.0.7)
nio4r (>= 1.2, < 3.0)
websocket-driver (~> 0.6.1)
actionmailer (5.0.7)
actionpack (= 5.0.7)
actionview (= 5.0.7)
activejob (= 5.0.7)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (5.2.6)
actionview (= 5.2.6)
activesupport (= 5.2.6)
rack (~> 2.0, >= 2.0.8)
rack-test (>= 0.6.3)
actionpack (5.0.7)
actionview (= 5.0.7)
activesupport (= 5.0.7)
rack (~> 2.0)
rack-test (~> 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.2.6)
activesupport (= 5.2.6)
actionview (5.0.7)
activesupport (= 5.0.7)
builder (~> 3.1)
erubi (~> 1.4)
erubis (~> 2.7.0)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (5.2.6)
activesupport (= 5.2.6)
activejob (5.0.7)
activesupport (= 5.0.7)
globalid (>= 0.3.6)
activemodel (5.2.6)
activesupport (= 5.2.6)
activerecord (5.2.6)
activemodel (= 5.2.6)
activesupport (= 5.2.6)
arel (>= 9.0)
activestorage (5.2.6)
actionpack (= 5.2.6)
activerecord (= 5.2.6)
marcel (~> 1.0.0)
activesupport (5.2.6)
activemodel (5.0.7)
activesupport (= 5.0.7)
activerecord (5.0.7)
activemodel (= 5.0.7)
activesupport (= 5.0.7)
arel (~> 7.0)
activesupport (5.0.7)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
aes_key_wrap (1.1.0)
arel (9.0.0)
ast (2.4.2)
attr_required (1.0.1)
autoprefixer-rails (10.2.4.0)
arel (7.1.4)
bigbluebutton-api-ruby (1.6.0)
xml-simple (~> 1.1)
bindex (0.5.0)
bootstrap-sass (3.3.0.0)
sass (~> 3.2)
bootstrap-social-rails (4.12.0)
railties (>= 3.1)
browser (2.5.2)
builder (3.2.3)
byebug (9.1.0)
climate_control (0.2.0)
cocaine (0.5.8)
climate_control (>= 0.0.3, < 1.0)
coffee-rails (4.2.2)
coffee-script (>= 2.2.0)
railties (>= 4.0.0)
coffee-script (2.4.1)
coffee-script-source
execjs
aws-eventstream (1.1.1)
aws-partitions (1.435.0)
aws-sdk-core (3.113.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.43.0)
aws-sdk-core (~> 3, >= 3.112.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.2.3)
aws-eventstream (~> 1, >= 1.0.2)
bcrypt (3.1.16)
bindata (2.4.10)
bindex (0.8.1)
bn-ldap-authentication (0.1.4)
net-ldap (~> 0)
bootsnap (1.7.3)
msgpack (~> 1.0)
bootstrap (4.3.1)
autoprefixer-rails (>= 9.1.0)
popper_js (>= 1.14.3, < 2)
sassc-rails (>= 2.0.0)
builder (3.2.4)
byebug (11.1.3)
cancancan (2.3.0)
childprocess (4.0.0)
concurrent-ruby (1.1.8)
coveralls (0.8.23)
json (>= 1.8, < 3)
simplecov (~> 0.16.1)
term-ansicolor (~> 1.3)
thor (>= 0.19.4, < 2.0)
tins (~> 1.6)
crack (0.4.5)
rexml
crass (1.0.6)
declarative (0.0.20)
declarative-option (0.1.0)
diff-lcs (1.4.4)
digest-crc (0.6.3)
rake (>= 12.0.0, < 14.0.0)
docile (1.3.5)
dotenv (2.7.6)
dotenv-rails (2.7.6)
dotenv (= 2.7.6)
railties (>= 3.2)
erubi (1.10.0)
coffee-script-source (1.12.2)
concurrent-ruby (1.0.5)
concurrent-ruby (1.0.5-java)
crass (1.0.4)
docile (1.1.5)
dotenv (2.2.1)
dotenv-rails (2.2.1)
dotenv (= 2.2.1)
railties (>= 3.2, < 5.2)
erubis (2.7.0)
execjs (2.7.0)
factory_bot (6.1.0)
activesupport (>= 5.0.0)
factory_bot_rails (6.1.0)
factory_bot (~> 6.1.0)
railties (>= 5.0.0)
faker (2.17.0)
i18n (>= 1.6, < 2)
faraday (1.3.0)
faraday-net_http (~> 1.0)
faraday (0.12.2)
multipart-post (>= 1.2, < 3)
ruby2_keywords
faraday-net_http (1.0.1)
ffi (1.15.0)
font-awesome-sass (5.9.0)
sassc (>= 1.11)
globalid (0.4.2)
ffi (1.9.18)
ffi (1.9.18-java)
ffi (1.9.18-x64-mingw32)
ffi (1.9.18-x86-mingw32)
font-awesome-sass (4.7.0)
sass (>= 3.2)
globalid (0.4.1)
activesupport (>= 4.2.0)
google-apis-core (0.3.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.14)
httpclient (>= 2.8.1, < 3.0)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
rexml
signet (~> 0.14)
webrick
google-apis-iamcredentials_v1 (0.2.0)
google-apis-core (~> 0.1)
google-apis-storage_v1 (0.3.0)
google-apis-core (~> 0.1)
google-cloud-core (1.6.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.5.0)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.1.0)
google-cloud-storage (1.30.0)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1)
google-cloud-core (~> 1.2)
googleauth (~> 0.9)
mini_mime (~> 1.0)
googleauth (0.16.0)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.14)
hashdiff (1.0.1)
hashie (4.1.0)
hiredis (0.6.3)
hashie (3.5.7)
http_accept_language (2.1.1)
httpclient (2.8.3)
i18n (1.8.10)
i18n (1.0.1)
concurrent-ruby (~> 1.0)
i18n-language-mapping (0.1.3.1)
jbuilder (2.11.2)
activesupport (>= 5.0.0)
jmespath (1.4.0)
jquery-rails (4.4.0)
jbuilder (2.7.0)
activesupport (>= 4.2.0)
multi_json (>= 1.2)
jquery-datatables-rails (3.4.0)
actionpack (>= 3.1)
jquery-rails
railties (>= 3.1)
sass-rails
jquery-rails (4.3.1)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
jquery-ui-rails (6.0.1)
railties (>= 3.2.16)
json (2.5.1)
json-jwt (1.13.0)
activesupport (>= 4.2)
aes_key_wrap
bindata
jwt (2.2.2)
listen (3.5.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
local_time (2.1.0)
lograge (0.11.2)
json (1.8.6)
json (1.8.6-java)
jwt (1.5.6)
libv8 (3.16.14.19)
listen (3.0.8)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
lograge (0.9.0)
actionpack (>= 4)
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
loofah (2.9.1)
loofah (2.2.2)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
mail (2.7.0)
mini_mime (>= 0.1.1)
marcel (1.0.1)
memoist (0.16.2)
method_source (1.0.0)
mini_mime (1.1.0)
mini_portile2 (2.6.1)
minitest (5.14.4)
msgpack (1.4.2)
multi_json (1.15.0)
metaclass (0.0.4)
method_source (0.9.0)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mimemagic (0.3.2)
mini_mime (1.0.0)
mini_portile2 (2.3.0)
minitest (5.11.3)
mocha (1.3.0)
metaclass (~> 0.0.1)
multi_json (1.13.1)
multi_xml (0.6.0)
multipart-post (2.1.1)
net-ldap (0.17.0)
nio4r (2.5.7)
nokogiri (1.12.5)
mini_portile2 (~> 2.6.1)
racc (~> 1.4)
oauth (0.5.5)
oauth2 (1.4.7)
faraday (>= 0.8, < 2.0)
jwt (>= 1.0, < 3.0)
multipart-post (2.0.0)
net-ldap (0.16.1)
nio4r (2.3.1)
nio4r (2.3.1-java)
nokogiri (1.8.2)
mini_portile2 (~> 2.3.0)
nokogiri (1.8.2-java)
nokogiri (1.8.2-x64-mingw32)
mini_portile2 (~> 2.3.0)
nokogiri (1.8.2-x86-mingw32)
mini_portile2 (~> 2.3.0)
oauth (0.5.4)
oauth2 (1.4.0)
faraday (>= 0.8, < 0.13)
jwt (~> 1.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
omniauth (1.9.1)
hashie (>= 3.4.6)
omniauth (1.6.1)
hashie (>= 3.4.6, < 3.6.0)
rack (>= 1.6.2, < 3)
omniauth-bn-launcher (0.1.3)
omniauth (~> 1.3, >= 1.3.2)
omniauth-oauth2 (= 1.5.0)
omniauth-bn-office365 (0.1.1)
omniauth (>= 1.3.2)
omniauth-oauth2 (>= 1.5.0)
omniauth-google-oauth2 (0.7.0)
jwt (>= 2.0)
omniauth-google-oauth2 (0.4.1)
jwt (~> 1.5.2)
multi_json (~> 1.3)
omniauth (>= 1.1.1)
omniauth-oauth2 (>= 1.5)
omniauth-oauth (1.2.0)
omniauth-oauth2 (>= 1.3.1)
omniauth-ldap (1.0.5)
net-ldap (~> 0.12)
omniauth (~> 1.0)
pyu-ruby-sasl (~> 0.0.3.2)
rubyntlm (~> 0.3.4)
omniauth-oauth (1.1.0)
oauth
omniauth (>= 1.0, < 3)
omniauth (~> 1.0)
omniauth-oauth2 (1.5.0)
oauth2 (~> 1.1)
omniauth (~> 1.2)
omniauth-twitter (1.4.0)
omniauth-saml (1.8.1)
omniauth (~> 1.3)
ruby-saml (~> 1.4, >= 1.4.3)
omniauth-twitter (1.2.1)
json (~> 1.3)
omniauth-oauth (~> 1.1)
rack
omniauth_openid_connect (0.3.5)
addressable (~> 2.5)
omniauth (~> 1.9)
openid_connect (~> 1.1)
openid_connect (1.2.0)
activemodel
attr_required (>= 1.0.0)
json-jwt (>= 1.5.0)
rack-oauth2 (>= 1.6.1)
swd (>= 1.0.0)
tzinfo
validate_email
validate_url
webfinger (>= 1.0.1)
os (1.1.1)
pagy (3.11.0)
parallel (1.20.1)
parser (3.0.0.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.0)
public_suffix (4.0.6)
puma (4.3.8)
nio4r (~> 2.0)
racc (1.5.2)
rack (2.2.3)
rack-oauth2 (1.16.0)
activesupport
attr_required
httpclient
json-jwt (>= 1.11.0)
rack (>= 2.1.0)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (5.2.6)
actioncable (= 5.2.6)
actionmailer (= 5.2.6)
actionpack (= 5.2.6)
actionview (= 5.2.6)
activejob (= 5.2.6)
activemodel (= 5.2.6)
activerecord (= 5.2.6)
activestorage (= 5.2.6)
activesupport (= 5.2.6)
paperclip (5.2.1)
activemodel (>= 4.2.0)
activesupport (>= 4.2.0)
cocaine (~> 0.5.5)
mime-types
mimemagic (~> 0.3.0)
pg (1.0.0)
pg (1.0.0-x64-mingw32)
pg (1.0.0-x86-mingw32)
puma (3.11.2)
puma (3.11.2-java)
pyu-ruby-sasl (0.0.3.3)
rack (2.0.5)
rack-test (0.6.3)
rack (>= 1.0)
rails (5.0.7)
actioncable (= 5.0.7)
actionmailer (= 5.0.7)
actionpack (= 5.0.7)
actionview (= 5.0.7)
activejob (= 5.0.7)
activemodel (= 5.0.7)
activerecord (= 5.0.7)
activesupport (= 5.0.7)
bundler (>= 1.3.0)
railties (= 5.2.6)
railties (= 5.0.7)
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.3.0)
loofah (~> 2.3)
railties (5.2.6)
actionpack (= 5.2.6)
activesupport (= 5.2.6)
rails-html-sanitizer (1.0.4)
loofah (~> 2.2, >= 2.2.2)
rails-timeago (2.16.0)
actionpack (>= 3.1)
activesupport (>= 3.1)
railties (5.0.7)
actionpack (= 5.0.7)
activesupport (= 5.0.7)
method_source
rake (>= 0.8.7)
thor (>= 0.19.0, < 2.0)
rainbow (3.0.0)
rake (13.0.3)
random_password (0.1.1)
rb-fsevent (0.10.4)
rb-inotify (0.10.1)
ffi (~> 1.0)
recaptcha (5.7.0)
json
redcarpet (3.5.1)
redis (4.2.5)
regexp_parser (2.1.1)
remote_syslog_logger (1.0.4)
syslog_protocol
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
request_store (1.5.0)
thor (>= 0.18.1, < 2.0)
rake (12.3.1)
rb-fsevent (0.10.2)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
ref (2.0.0)
request_store (1.4.0)
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.4.1)
parser (>= 2.7.1.5)
ruby-progressbar (1.11.0)
ruby2_keywords (0.0.4)
rubyzip (2.3.0)
sassc (2.4.0)
ffi (~> 1.9)
sassc-rails (2.1.2)
railties (>= 4.0.0)
sassc (>= 2.0)
sprockets (> 3.0)
sprockets-rails
tilt
sequel (5.41.0)
shoulda-matchers (3.1.3)
activesupport (>= 4.0.0)
signet (0.15.0)
addressable (~> 2.3)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simplecov (0.16.1)
docile (~> 1.1)
ruby-saml (1.4.3)
nokogiri (>= 1.5.10)
rubyntlm (0.3.4)
sass (3.5.5)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sass-rails (5.0.7)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
simplecov (0.15.1)
docile (~> 1.1.0)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.2)
spring (2.1.1)
slack-notifier (2.3.2)
spring (2.0.2)
activesupport (>= 4.2)
spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0)
sprockets (3.7.2)
sprockets (3.7.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.2)
sprockets-rails (3.2.1)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.3.13)
swd (1.2.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.1.0)
sqlite3 (1.3.13-x64-mingw32)
sqlite3 (1.3.13-x86-mingw32)
sqlite3 (1.3.13-x86-mswin32-60)
therubyracer (0.12.3)
libv8 (~> 3.16.14.15)
ref
thor (0.20.0)
thread_safe (0.3.6)
tilt (2.0.10)
tins (1.28.0)
sync
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
tzinfo (1.2.9)
thread_safe (0.3.6-java)
tilt (2.0.8)
turbolinks (5.1.0)
turbolinks-source (~> 5.1)
turbolinks-source (5.1.0)
tzinfo (1.2.5)
thread_safe (~> 0.1)
tzinfo-data (1.2021.1)
tzinfo-data (1.2017.3)
tzinfo (>= 1.0.0)
uber (0.1.0)
uglifier (4.2.0)
uglifier (4.1.3)
execjs (>= 0.3.0, < 3)
unicode-display_width (2.0.0)
validate_email (0.1.6)
activemodel (>= 3.0)
mail (>= 2.2.5)
validate_url (1.0.13)
activemodel (>= 3.0.0)
public_suffix
web-console (3.7.0)
web-console (3.5.1)
actionview (>= 5.0)
activemodel (>= 5.0)
bindex (>= 0.4.0)
railties (>= 5.0)
webfinger (1.1.0)
activesupport
httpclient (>= 2.4)
webmock (3.12.1)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webrick (1.7.0)
websocket-driver (0.7.3)
websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
xml-simple (1.1.8)
websocket-driver (0.6.5-java)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3)
xml-simple (1.1.5)
yt (0.28.5)
activesupport
PLATFORMS
java
ruby
x64-mingw32
x86-mingw32
x86-mswin32
DEPENDENCIES
action-cable-testing (~> 0.6)
aws-sdk-s3 (~> 1.88.1)
bcrypt (~> 3.1.7)
bigbluebutton-api-ruby!
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.7)
factory_bot_rails (~> 6.1)
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.2)
jquery-rails (~> 4.4.0)
jquery-ui-rails (~> 6.0.1)
listen (~> 3.0)
local_time (~> 2.1.0)
lograge (~> 0.11.2)
net-ldap (~> 0.17.0)
omniauth (~> 1.9.1)
omniauth-bn-launcher (~> 0.1.3)
omniauth-bn-office365 (~> 0.1.1)
omniauth-google-oauth2 (~> 0.7.0)
omniauth-twitter (~> 1.4.0)
omniauth_openid_connect (~> 0.3.5)
pagy (~> 3.11.0)
pg (~> 0.18)
pluck_to_hash (~> 1.0.2)
puma (~> 4.3.8)
rails (~> 5.2.6)
rails-controller-testing (~> 1.0)
random_password (~> 0.1.1)
recaptcha (~> 5.7.0)
redcarpet (~> 3.5.1)
redis (~> 4.2.5)
remote_syslog_logger (~> 1.0.4)
rspec-rails (~> 3.7)
rubocop (~> 1.10.0)
sassc-rails (~> 2.1.2)
sequel (~> 5.41.0)
shoulda-matchers (~> 3.1)
spring (~> 2.1)
spring-watcher-listen (~> 2.0)
sprockets (~> 3.7.2)
sqlite3 (~> 1.3.6)
tabler-rubygem!
turbolinks (~> 5.2.1)
tzinfo-data (~> 1.2021.1)
uglifier (~> 4.2.0)
web-console (~> 3.7)
webmock (~> 3.11)
bigbluebutton-api-ruby
bootstrap-sass (= 3.3.0.0)
bootstrap-social-rails (~> 4.12)
browser
byebug
coffee-rails (~> 4.2)
dotenv-rails
faraday
font-awesome-sass (= 4.7.0)
http_accept_language
jbuilder (~> 2.5)
jquery-datatables-rails (~> 3.4.0)
jquery-rails
jquery-ui-rails
listen (~> 3.0.5)
lograge
mocha
omniauth (= 1.6.1)
omniauth-google-oauth2 (= 0.4.1)
omniauth-ldap
omniauth-saml
omniauth-twitter (= 1.2.1)
paperclip (~> 5.1)
pg
puma (~> 3.0)
rails (~> 5.0.0, >= 5.0.0.1)
rails-timeago (~> 2.0)
sass-rails (~> 5.0)
simplecov
slack-notifier
spring
spring-watcher-listen (~> 2.0.0)
sqlite3
therubyracer
turbolinks (~> 5)
tzinfo-data
uglifier (>= 1.3.0)
web-console
yt (~> 0.28.0)
RUBY VERSION
ruby 2.4.0p0
BUNDLED WITH
1.16.1

165
LICENSE
View File

@ -1,165 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@ -1,37 +1,31 @@
# 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 (but powerful) front-end interface for your BigBlueButton server. At its core, GreenLight provides a minimalistic web-based application that lets users
Greenlight is a simple front-end interface for your BigBlueButton server. At its heart, Greenlight provides a minimalistic web-based application that allows users to:
* Create a meeting
* Invite others to the meeting
* Join a meeting
* Signup/Login with Google, Office365, OpenID Connect, or through the application itself.
* 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.
Furthermore, if you configure GreenLight to use either Google, Twitter (both via OAuth2) or LDAP for authentication, users can login to record meetings and manage recordings.
Interested? Try Greenlight out on our [demo server](https://demo.bigbluebutton.org/gl)!
## Overview video
Greenlight is also completely configurable. This means you can turn on/off features to make Greenlight fit your specific use case. For more information on Greenlight and its features, see our [documentation](http://docs.bigbluebutton.org/greenlight/gl-install.html).
For a overview of how GreenLight works, see the following video
For a overview of how Greenlight works, checkout our Introduction to Greenlight Video:
[![GreenLight Overview](https://img.youtube.com/vi/yGX3JCv7OVM/0.jpg)](https://youtu.be/yGX3JCv7OVM)
[![GreenLight Overview](https://img.youtube.com/vi/Hso8yLzkqj8/0.jpg)](https://youtu.be/Hso8yLzkqj8)
## Installation on a BigBlueButton Server
## Installation on the BigBlueButton server
We designed GreenLight to install on a [BigBlueButton 1.1-beta](http://docs.bigbluebutton.org/greenlight-v1.html) (or later) server. This means you don't need a separate server to run GreenLight.
Greenlight is designed to work on a [BigBlueButton 2.0](https://github.com/bigbluebutton/bigbluebutton) (or later) server.
For more informaiton see [Installing GreenLight](http://docs.bigbluebutton.org/greenlight-v1.html#installing-greenlight).
For information on installing Greenlight, checkout our [Installing Greenlight on a BigBlueButton Server](http://docs.bigbluebutton.org/greenlight/gl-install.html#installing-on-a-bigbluebutton-server) documentation.
# Source Code
## Source Code & Contributing
GreenLight is a rails 5 application.
Greenlight is built using Ruby on Rails. Many developers already know Rails well, and we wanted to create both a full front-end to BigBlueButton but also a reference implementation of how to fully leverage the [BigBlueButton API](http://docs.bigbluebutton.org/dev/api.html).
Many developers already know Rails well, and we wanted to create both a full front-end to BigBlueButton but also a reference implementation of how to fully leverage the [BigBlueButton API](http://docs.bigbluebutton.org/dev/api.html).
We invite you to build upon Greenlight and help make it better. See [Contributing to BigBlueButton](http://docs.bigbluebutton.org/support/faq.html#contributing-to-bigbluebutton).
We invite you to build upon GreenLight and help make it better. See [Contributing to BigBlueButton](http://docs.bigbluebutton.org/support/faq.html#contributing-to-bigbluebutton).
We invite your feedback, questions, and suggests about Greenlight too. Please post them to the [Greenlight mailing list](https://groups.google.com/forum/#!forum/bigbluebutton-greenlight).
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.
We invite your feedback, questions, and suggests about GreenLight too. Please post them to the [developer mailing list](https://groups.google.com/forum/#!forum/bigbluebutton-dev).

View File

@ -1,15 +1,6 @@
# 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

View File

@ -1,11 +0,0 @@
# Security Policy
## Supported Versions
We support only the latest version of this project.
## Reporting a Vulnerability
If you think youve found a security issue with BigBlueButton or Greenlight, we ask that you do a [responsible disclosure](https://en.wikipedia.org/wiki/Responsible_disclosure) and e-mail us directly at security@bigbluebutton.org.
We will respond to you quickly, work with you to examine the scope of the issue, and give priority to fixing it as soon as possible.

View File

@ -1,6 +1,6 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software

View File

@ -1,93 +0,0 @@
Copyright 2010, 2012, 2014 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name Source.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,236 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
// Handles live updating and initial population of the previous meetings and active meetings lists on
// the landing page using custom Actioncable events.
var MEETINGS = {}
var WAITING = {}
var LOADING_DELAY = 1750 // milliseconds.
// Updates the previous meetings section.
var updatePreviousMeetings = function(){
$("ul.previously-joined li").each(function(idx, li) {
var previous_meeting = $(li);
if(Object.keys(MEETINGS).indexOf(previous_meeting.text()) > -1){
previous_meeting.remove()
}
});
}
// Ignore excess on either side of user_id.
var trimUserId = function(user_id){
components = user_id.split('_')
return components.sort(function (a, b) {return b.length - a.length;})[0]
}
// Finds a user by their user_id.
var findByUserId = function(users, user_id){
for(i = 0; i < users.length; i++){
if(trimUserId(users[i]['user_id']) == trimUserId(user_id)){
return i
}
}
return undefined
}
// Adds a user to a meeting.
var addUser = function(data){
if(data['role'] == 'MODERATOR'){
MEETINGS[data['meeting']]['moderators'].push({'name': data['user'], 'user_id': data['user_id']})
} else {
MEETINGS[data['meeting']]['participants'].push({'name':data['user'], 'user_id': data['user_id']})
}
updateMeetingText(MEETINGS[data['meeting']])
}
// Removes a user from a meeting.
var removeUser = function(data){
user = findByUserId(MEETINGS[data['meeting']]['moderators'], data['user_id'])
if(user == undefined){
user = findByUserId(MEETINGS[data['meeting']]['participants'], data['user_id']);
if(user == undefined){ return; }
MEETINGS[data['meeting']]['participants'].splice(user, 1);
} else {
MEETINGS[data['meeting']]['moderators'].splice(user, 1);
}
updateMeetingText(MEETINGS[data['meeting']])
}
// Updates the display text for an active meeting.
var updateMeetingText = function(m){
// If a meeting has a moderators property, it is running.
var body;
if(m.hasOwnProperty('moderators')){
var list;
if(m['moderators'].length + m['participants'].length == 0){
list = '(empty)'
} else {
list = m['moderators'].map(function(x){ return x['name']; }).join('(mod), ') +
(m['moderators'].length > 0 ? '(mod)' : '') +
(m['participants'].length > 0 && m['moderators'].length != 0 ? ', ' : '') +
(m['participants'].map(function(x){ return x['name']; }).join(', '))
}
body = '<a>' + m['name'] + '</a><i>: ' + list + '</i>'
// Otherwise it hasn't started (users waiting the join).
} else {
body = '<a>' + m['name'] + '</a><i> (not yet started): ' + m['users'].join(', ') + '</i>'
}
// If the item doesn't exist, add it and set up join meeting event.
if($("li[id='" + m['name'].replace(' ', '_') + "']").length == 0){
var meeting_item = $("<li>" + body + "</li>")
meeting_item.attr('id', m['name'].replace(' ', '_'))
$('.actives').append(meeting_item);
// Set up join on click.
meeting_item.click(function(){
joinMeeting(m['name']);
});
// Otherwise, just change the body.
} else {
$("li[id='" + m['name'].replace(' ', '_') + "']").html(body)
}
}
// Initially populates the active meetings when the page loads using the API.
var initialPopulate = function(){
// Only populate on room resources.
var chopped = window.location.href.split('/')
if (!window.location.href.includes('rooms') || chopped[chopped.length - 2] == $('body').data('current-user')) { return; }
$.post((window.location.href + '/statuses').replace('#', ''), {previously_joined: getPreviouslyJoined()})
.done(function(data) {
// Populate waiting meetings.
Object.keys(data['waiting']).forEach(function(key) {
WAITING[key] = {'name': key, 'users': data['waiting'][key]}
updateMeetingText(WAITING[key])
})
// Add the meetings to the active meetings list.
for(var i = 0; i < data['active'].length; i++){
var meeting = data['active'][i]
var name = meeting['name']
var participants = meeting['participants']
var moderators = meeting['moderators']
// Create meeting.
MEETINGS[name] = {'name': name, 'participants': participants, 'moderators': moderators}
updateMeetingText(MEETINGS[name])
}
// Remove from previous meetings if they are active.
updatePreviousMeetings();
$('.hidden-list').show();
$('.active-spinner').hide();
});
}
// Gets a list of known previously joined meetings.
var getPreviouslyJoined = function(){
var joinedMeetings = localStorage.getItem('joinedRooms-' + $('body').data('current-user'));
if (joinedMeetings == '' || joinedMeetings == null){ return []; }
return joinedMeetings.split(',')
}
// Removes an active meeting.
var removeActiveMeeting = function(meeting){
if(meeting){ $("li[id='" + meeting['name'].replace(' ', '_') + "']").remove() }
}
// Directly join a meeting from active meetings.
var joinMeeting = function(meeting_name){
if (meeting_name == undefined || meeting_name == null) { return; }
Meeting.getInstance().setUserName(localStorage.getItem('lastJoinedName'));
Meeting.getInstance().setMeetingId(meeting_name);
var jqxhr = Meeting.getInstance().getJoinMeetingResponse();
if (jqxhr) {
jqxhr.done(function(data) {
if (data.messageKey === 'wait_for_moderator') {
waitForModerator(Meeting.getInstance().getURL());
} else {
$(location).attr("href", data.response.join_url);
}
});
jqxhr.fail(function() {
console.info("meeting join failed");
});
} else {
$('.meeting-user-name').parent().addClass('has-error');
}
}
// Only need to register for logged in users.
$(document).on('turbolinks:load', function(){
if($('body').data('current-user')){
MEETINGS = {}
// Ensure actives is empty.
$('.actives').empty();
if(!App.messages){
// Setup actioncable.
App.messages = App.cable.subscriptions.create('RefreshMeetingsChannel', {
received: function(data) {
console.log('Recieved ' + data['method'] + ' action for ' + data['meeting'] + ' with room id ' + data['room'] + '.')
if(data['room'] == $('body').data('current-user')){
// Handle webhook event.
if(data['method'] == 'create'){
// Create an empty meeting.
MEETINGS[data['meeting']] = {'name': data['meeting'],
'participants': [],
'moderators': []}
updateMeetingText(MEETINGS[data['meeting']])
updatePreviousMeetings();
if (WAITING.hasOwnProperty(data['meeting'])){ delete WAITING[data['meeting']]; }
} else if(data['method'] == 'destroy'){
removeActiveMeeting(MEETINGS[data['meeting']])
PreviousMeetings.uniqueAdd([data['meeting']])
delete MEETINGS[data['meeting']]
} else if(data['method'] == 'join'){
addUser(data)
} else if(data['method'] == 'leave'){
removeUser(data)
} else if(data['method'] == 'waiting'){
// Handle waiting meeting.
if(WAITING.hasOwnProperty(data['meeting'])){
WAITING[data['meeting']]['users'].push(data['user'])
updateMeetingText(WAITING[data['meeting']])
} else {
WAITING[data['meeting']] = {'name': data['meeting'],
'users': [data['user']]}
updateMeetingText(WAITING[data['meeting']])
}
} else if((data['method'] == 'no_longer_waiting') && (WAITING.hasOwnProperty(data['meeting']))){
WAITING[data['meeting']]['users'].splice(WAITING[data['meeting']]['users'].indexOf(data['user']), 1)
updateMeetingText(WAITING[data['meeting']])
if(WAITING[data['meeting']]['users'].length == 0){
removeActiveMeeting(WAITING[data['meeting']])
delete WAITING[data['meeting']]
}
}
}
}
});
}
console.log('Populating active meetings.');
// Short delay to hide the previous meetings population.
setTimeout(initialPopulate, LOADING_DELAY);
}
});

View File

@ -1,340 +0,0 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
$(document).on('turbolinks:load', function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
// Only run on the admins page.
if (controller == "admins") {
if(action == "index") {
//clear the role filter if user clicks on the x
$(".clear-role").click(function() {
var search = new URL(location.href).searchParams.get('search')
var url = window.location.pathname + "?page=1"
if (search) {
url += "&search=" + search
}
window.location.replace(url);
})
// Handle selected user tags
$(".manage-users-tab").click(function() {
$(".manage-users-tab").removeClass("selected")
$(this).addClass("selected")
updateTabParams(this.id)
})
$('.selectpicker').selectpicker({
liveSearchPlaceholder: getLocalizedString('javascript.search.start')
});
// Fixes turbolinks issue with bootstrap select
$(window).trigger('load.bs.select.data-api');
// Display merge accounts modal with correct info
$(".merge-user").click(function() {
// Update the path of save button
$("#merge-save-access").attr("data-path", $(this).data("path"))
let userInfo = $(this).data("info")
$("#merge-to").html("") // Clear current inputs
let spanName = document.createElement("span"),
spanEmail = document.createElement("span"),
spanUid = document.createElement("span");
spanName.innerText = userInfo.name
spanEmail.setAttribute('class', 'text-muted d-block')
spanEmail.innerText = userInfo.email
spanUid.setAttribute('class', 'text-muted d-block')
spanUid.innerText = userInfo.uid
$("#merge-to").append(spanName, spanEmail, spanUid)
})
$("#mergeUserModal").on("show.bs.modal", function() {
$(".selectpicker").selectpicker('val','')
})
$(".bootstrap-select").on("click", function() {
$(".bs-searchbox").siblings().hide()
})
$("#merge-user-select ~ button").on("click", function() {
$(".bs-searchbox").siblings().hide()
})
$(".bs-searchbox input").on("input", function() {
if ($(".bs-searchbox input").val() == '' || $(".bs-searchbox input").val().length < 3) {
$(".select-options").remove()
$(".bs-searchbox").siblings().hide()
} else {
// Manually populate the dropdown
$.get($("#merge-user-select").data("path"), { search: $(".bs-searchbox input").val() }, function(users) {
$(".select-options").remove()
if (users.length > 0) {
users.forEach(function(user) {
let opt = document.createElement("option")
$(opt).val(JSON.stringify({uid: user.uid, email: user.email, name: user.name}))
$(opt).text(user.name)
$(opt).addClass("select-options")
$(opt).attr("data-subtext", user.email)
$("#merge-user-select").append(opt)
})
// Only refresh the select dropdown if there are results to show
$('#merge-user-select').selectpicker('refresh');
}
$(".bs-searchbox").siblings().show()
})
}
})
// User selects an option from the Room Access dropdown
$(".bootstrap-select").on("changed.bs.select", function(){
// Get the uid of the selected user
let user = $(".selectpicker").selectpicker('val')
if (user != "") {
let userInfo = JSON.parse(user)
$("#merge-from").html("") // Clear current input
let spanName = document.createElement("span"),
spanEmail = document.createElement("span"),
spanUid = document.createElement("span");
spanName.innerText = userInfo.name
spanEmail.setAttribute('class', 'text-muted d-block')
spanEmail.innerText = userInfo.email
spanUid.setAttribute('class', 'text-muted d-block')
spanUid.id = 'from-uid'
spanUid.innerText = userInfo.uid
$("#merge-from").append(spanName, spanEmail, spanUid)
}
})
}
else if(action == "site_settings"){
var urlParams = new URLSearchParams(window.location.search);
// Only load the colour selectors if on the appearance tab
if (urlParams.get("tab") == null || urlParams.get("tab") == "appearance") {
loadColourSelectors()
}
}
else if (action == "roles"){
// Refreshes the new role modal
$("#newRoleButton").click(function(){
$("#createRoleName").val("")
})
// Updates the colour picker to the correct colour
let role_colour = $("#role-colorinput-regular").data("colour")
$("#role-colorinput-regular").css("background-color", role_colour);
$("#role-colorinput-regular").css("border-color", role_colour);
loadRoleColourSelector(role_colour, $("#role-colorinput-regular").data("disabled"));
// Loads the jquery sortable so users can manually sort roles
$("#rolesSelect").sortable({
items: "a:not(.sort-disabled)",
update: function() {
$.ajax({
url: $(this).data("url"),
type: 'PATCH',
data: $(this).sortable('serialize')
});
}
});
}
}
});
// Change the branding image to the image provided
function changeBrandingImage(path) {
var url = $("#branding-url").val()
$.post(path, {value: url, tab: "appearance"})
}
// Change the Legal URL to the one provided
function changeLegalURL(path) {
var url = $("#legal-url").val()
$.post(path, {value: url, tab: "administration"})
}
// Change the Privacy Policy URL to the one provided
function changePrivacyPolicyURL(path) {
var url = $("#privpolicy-url").val()
$.post(path, {value: url, tab: "administration"})
}
// Display the maintenance Banner
function displayMaintenanceBanner(path) {
var message = $("#maintenance-banner").val()
$.post(path, {value: message, tab: "administration"})
}
// Clear the maintenance Banner
function clearMaintenanceBanner(path) {
$.post(path, {value: "", tab: "administration"})
}
// Change the email mapping to the string provided
function changeEmailMapping(path) {
var url = $("#email-mapping").val()
$.post(path, {value: url, tab: "registration"})
}
function mergeUsers() {
let userToMerge = $("#from-uid").text()
$.post($("#merge-save-access").data("path"), {merge: userToMerge})
}
// Filters by role
function filterRole(role) {
var search = new URL(location.href).searchParams.get('search')
var url = window.location.pathname + "?page=1" + "&role=" + role
if (search) {
url += "&search=" + search
}
window.location.replace(url);
}
function updateTabParams(tab) {
var search_params = new URLSearchParams(window.location.search)
if (window.location.href.includes("tab=")) {
search_params.set("tab", tab)
} else {
search_params.append("tab", tab)
}
search_params.delete("page")
window.location.search = search_params.toString()
}
function loadColourSelectors() {
const pickrRegular = new Pickr({
el: '#colorinput-regular',
theme: 'monolith',
useAsButton: true,
lockOpacity: true,
defaultRepresentation: 'HEX',
closeWithKey: 'Enter',
default: $("#colorinput-regular").css("background-color"),
components: {
palette: true,
preview: true,
hue: true,
interaction: {
input: true,
save: true,
},
},
});
const pickrLighten = new Pickr({
el: '#colorinput-lighten',
theme: 'monolith',
useAsButton: true,
lockOpacity: true,
defaultRepresentation: 'HEX',
closeWithKey: 'Enter',
default: $("#colorinput-lighten").css("background-color"),
components: {
palette: true,
preview: true,
hue: true,
interaction: {
input: true,
save: true,
},
},
});
const pickrDarken = new Pickr({
el: '#colorinput-darken',
theme: 'monolith',
useAsButton: true,
lockOpacity: true,
defaultRepresentation: 'HEX',
closeWithKey: 'Enter',
default: $("#colorinput-darken").css("background-color"),
components: {
palette: true,
preview: true,
hue: true,
interaction: {
input: true,
save: true,
},
},
});
pickrRegular.on("save", (color, instance) => {
$.post($("#coloring-path-regular").val(), {value: color.toHEXA().toString()}).done(function() {
location.reload()
});
})
pickrLighten.on("save", (color, instance) => {
$.post($("#coloring-path-lighten").val(), {value: color.toHEXA().toString(), tab: "appearance"}).done(function() {
location.reload()
});
})
pickrDarken.on("save", (color, instance) => {
$.post($("#coloring-path-darken").val(), {value: color.toHEXA().toString(), tab: "appearance"}).done(function() {
location.reload()
});
})
}
function loadRoleColourSelector(role_colour, disabled) {
if (!disabled) {
const pickrRoleRegular = new Pickr({
el: '#role-colorinput-regular',
theme: 'monolith',
useAsButton: true,
lockOpacity: true,
defaultRepresentation: 'HEX',
closeWithKey: 'Enter',
default: role_colour,
components: {
palette: true,
preview: true,
hue: true,
interaction: {
input: true,
save: true,
},
},
});
// On save update the colour input's background colour and update the role colour input
pickrRoleRegular.on("save", (color, instance) => {
$("#role-colorinput-regular").css("background-color", color.toHEXA().toString());
$("#role-colorinput-regular").css("border-color", color.toHEXA().toString());
$("#role-colour").val(color.toHEXA().toString());
});
}
}

View File

@ -1,6 +1,6 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
@ -14,26 +14,13 @@
// You should have received a copy of the GNU Lesser General Public License along
// 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 jquery2
//= require jquery-ui
//= require dataTables/jquery.dataTables
//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
//= require rails-timeago-all
//= require bootstrap-sprockets
//= require qrcode/qrcode.js
//= require turbolinks
//= require jquery3
//= require tabler
//= require tabler.plugins
//= require jquery_ujs
//= require jquery-ui/widget
//= require jquery-ui/widgets/sortable
//= require pickr.min.js
//= require bootstrap-select.min.js
//= require local-time
//= require_self
//= require_tree .

View File

@ -1,7 +1,6 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
@ -14,14 +13,7 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<%= t('mailer.user.invite.subject') %>
<%= t('mailer.user.invite.info', name: @name) %>
<%= t('mailer.user.invite.username', email: @email) %>
<%= t('mailer.user.invite.signup_info') %>
<%= @url %>
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/

View File

@ -1,6 +1,6 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
@ -22,11 +22,13 @@
//= require_tree ./channels
(function() {
var protocol = (window.location.protocol === "https:" ? "wss://" : "ws://");
var protocol = window.location.protocol === "https:" ? "wss://" : "ws://";
var host = window.GreenLight.WEBSOCKET_HOST || window.location.host + window.GreenLight.RELATIVE_ROOT;
var url = protocol + host + '/cable';
var path = window.GreenLight.WEBSOCKET_PATH || '/cable';
var url = protocol + host + path;
this.App || (this.App = {});
App.cable = ActionCable.createConsumer(url);
}).call(this);

View File

@ -0,0 +1,81 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
(function() {
var sessionStatusRefresh = function(url) {
$.get(url + "/session_status_refresh", function(html) {
$(".center-panel-wrapper").html(html);
displayRoomURL();
});
};
var enableMeetingUpdates = function() {
var meetingId = ''
if (!$(".page-wrapper.rooms").data('main-room')) {
meetingId = $(".page-wrapper.rooms").data('id');
}
App.meeting_update = App.cable.subscriptions.create({
channel: 'MeetingUpdatesChannel',
admin_id: $(".page-wrapper.rooms").data('admin-id'),
meeting_id: meetingId
},
{
received: function(data) {
if (data.action === 'moderator_joined') {
if (!Meeting.getInstance().getModJoined()) {
Meeting.getInstance().setModJoined(true);
if (Meeting.getInstance().getWaitingForMod()) {
loopJoin();
} else {
sessionStatusRefresh($('.meeting-url').val());
showAlert(I18n.meeting_started, 4000);
}
}
} else if (data.action === 'meeting_ended') {
sessionStatusRefresh($('.meeting-url').val());
showAlert(I18n.meeting_ended, 4000);
} else if (data.action === 'user_waiting') {
// show a browser notification only to the owner
if (isRoomOwner()) {
showNotification(I18n.user_waiting_title, {
body: I18n.user_waiting_body.replace(/%{user}/, data.user).replace(/%{meeting}/, '"'+data.meeting_name+'"')
});
}
} else if(data.action === 'unable_to_join') {
showAlert(I18n.unable_to_join_mobile, 6000)
}
}
});
};
var disableMeetingUpdates = function() {
App.meeting_update.unsubscribe();
delete App.meeting_update
};
$(document).on("turbolinks:load", function() {
// disable meeting updates if enabled from a previous page
if (App.meeting_update) {
disableMeetingUpdates();
}
if ($("body[data-controller=landing]").get(0)) {
if ($("body[data-action=rooms]").get(0)) {
enableMeetingUpdates();
}
}
});
}).call(this);

View File

@ -0,0 +1,75 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
(function() {
var enableRecordingUpdates = function() {
App.recording_update = App.cable.subscriptions.create({
channel: 'RecordingUpdatesChannel',
admin_id: $(".page-wrapper.rooms").data('admin-id'),
meeting_id: $(".page-wrapper.rooms").data('id')
},
{
received: function(data) {
var recordings = Recordings.getInstance();
var table = recordings.table.api();
var row = table.row("#"+data.id);
if (data.action === 'update') {
var rowData = row.data();
rowData.published = data.published;
rowData.listed = data.listed;
table.row("#"+data.id).data(rowData);
recordings.draw();
var status = data.published ? (data.listed ? 'published' : 'unlisted') : 'unpublished';
showAlert(I18n['recording_'+status], 4000);
} else if (data.action === 'delete') {
row.remove();
recordings.draw();
showAlert(I18n.recording_deleted, 4000);
} else if (data.action === 'create') {
if (isRoomOwner() && row.length == 0) {
recordings.refresh();
showAlert(I18n.recording_created, 4000);
}
}
}
});
};
var disableRecordingUpdates = function() {
App.recording_update.unsubscribe();
delete App.recording_update
};
$(document).on("turbolinks:load", function() {
// disable recording updates if enabled from a previous page
if (App.recording_update) {
disableRecordingUpdates();
}
if ($("body[data-controller=landing]").get(0)) {
if ($("body[data-action=rooms]").get(0)) {
enableRecordingUpdates();
}
}
});
}).call(this);

View File

@ -1,37 +0,0 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
$(document).on('turbolinks:load', function(){
$("#cookies-agree-button").click(function() {
//create a cookie that lasts 1 year
var cookieDate = new Date();
cookieDate.setFullYear(cookieDate.getFullYear() + 1); //1 year from now
document.cookie = "cookie_consented=true; path=/; expires=" + cookieDate.toUTCString() + ";"
//hide the banner at the bottom
$(".cookies-banner").attr("style","display:none !important")
})
$("#maintenance-close").click(function(event) {
//create a cookie that lasts 1 day
var cookieDate = new Date()
cookieDate.setDate(cookieDate.getDate() + 1) //1 day from now
console.log("maintenance_window=" + $(event.target).data("date") + "; path=/; expires=" + cookieDate.toUTCString() + ";")
document.cookie = "maintenance_window=" + $(event.target).data("date") + "; path=/; expires=" + cookieDate.toUTCString() + ";"
})
})

View File

@ -1,57 +0,0 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
$(document).on('turbolinks:load', function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
// Only run on the admins page.
if (controller == "admins" && action == "index") {
// show the modal with the correct form action url
$(".delete-user").click(function(){
$("#delete-confirm").parent().attr("action", $(this).data("path"))
if ($(this).data("delete") == "temp-delete") {
$("#perm-delete").hide()
$("#delete-warning").show()
} else {
$("#perm-delete").show()
$("#delete-warning").hide()
}
})
}
$(".delete-user").click(function(data){
document.getElementById("delete-checkbox").checked = false
$("#delete-confirm").prop("disabled", "disabled")
if ($(data.target).data("delete") == "temp-delete") {
$("#perm-delete").hide()
$("#delete-warning").show()
} else {
$("#perm-delete").show()
$("#delete-warning").hide()
}
})
$("#delete-checkbox").click(function(data){
if (document.getElementById("delete-checkbox").checked) {
$("#delete-confirm").removeAttr("disabled")
} else {
$("#delete-confirm").prop("disabled", "disabled")
}
})
})

View File

@ -0,0 +1,297 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
/** global: Meeting */
/** global: PreviousMeetings */
/** global: QRCode */
/** global: Recordings */
/** global: Turbolinks */
(function() {
var qrcode;
var waitForModerator = function(url) {
window.localStorage.setItem("waitingName", $('.meeting-user-name').val());
$.post(url + "/wait", {name: $('.meeting-user-name').val()}, function(html) {
$(".center-panel-wrapper").html(html);
});
if (!Meeting.getInstance().getWaitingForMod()) {
Meeting.getInstance().setWaitingForMod(true);
if (Meeting.getInstance().getModJoined()) {
loopJoin();
}
}
};
var init = function() {
Meeting.clear();
var nameInput = $('.meeting-user-name');
if (!nameInput.val()) {
var lastName = window.localStorage.getItem('lastJoinedName');
if (lastName !== 'undefined') {
nameInput.val(lastName);
}
}
// setup event handlers
$('.center-panel-wrapper').on ('click', '.meeting-join', function () {
var name = $('.meeting-user-name').val();
Meeting.getInstance().setUserName(name);
Meeting.getInstance().setMeetingId($(".page-wrapper").data('id'));
// a user name is set, join the user into the session
if (name !== undefined && name !== null) {
var jqxhr = Meeting.getInstance().getJoinMeetingResponse();
if (jqxhr) {
jqxhr.done(function(data) {
if (data.messageKey === 'wait_for_moderator') {
waitForModerator(Meeting.getInstance().getURL());
} else if (data.messageKey === 'ok') {
$(location).attr("href", data.response.join_url);
}
});
jqxhr.fail(function() {
console.info("meeting join failed");
});
} else {
$('.meeting-user-name').parent().addClass('has-error');
}
// if not user name was set it means we must ask for a name
} else {
$(location).attr("href", Meeting.getInstance().getURL());
}
});
$('.center-panel-wrapper').on ('click', '.meeting-start', function () {
Turbolinks.visit($('.meeting-url').val());
});
$('.center-panel-wrapper').on ('input', '.meeting-user-name', function () {
if ($(this).val() === '') {
$(this).parent().addClass('has-error')
} else {
$(this).parent().removeClass('has-error')
}
});
$('.center-panel-wrapper').on ('keypress', '.meeting-user-name', function (event) {
if (event.keyCode === 13) {
event.preventDefault();
$('.meeting-join').click();
}
});
$('.center-panel-wrapper').on ('keypress', '.meeting-name', function (event) {
if (event.keyCode === 13) {
event.preventDefault();
$('.meeting-start').click();
}
});
$('.center-panel-wrapper').on ('click', '.meeting-end', function () {
var jqxhr = Meeting.getInstance().endMeeting();
var btn = $(this);
btn.prop("disabled", true);
jqxhr.fail(function() {
console.info("meeting end failed");
});
});
$('.center-panel-wrapper').on ('click', '.meeting-url-copy', function () {
var meetingURLInput = $('.meeting-url');
// copy URL
meetingURLInput.select();
try {
var success = document.execCommand("copy");
if (success) {
meetingURLInput.blur();
$(this).trigger('hint', [$(this).data('copied-hint')]);
} else {
$(this).trigger('hint', [$(this).data('copy-error')]);
}
} catch (err) {
$(this).trigger('hint', [$(this).data('copy-error')]);
}
});
$('.center-panel-wrapper').on('hint', '.meeting-url-copy', function (event, msg) {
$(this).focus();
$(this).attr('title', msg)
.tooltip('fixTitle')
.tooltip('show')
.attr('title', $(this).data('copy-hint'))
.tooltip('fixTitle');
});
$('.center-panel-wrapper').on('mouseleave', '.meeting-url-copy', function () {
$(this).blur();
});
// button used to send invitations to the meeting (i.e. "mailto:" link)
$('.center-panel-wrapper').on('click', '.meeting-invite', function () {
var meetingURL = Meeting.getInstance().getURL();
var subject = $(this).data("invite-subject");
var body = $(this).data("invite-body").replace("&&URL&&", meetingURL);
var mailto = "mailto:?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body);
window.open(mailto);
});
$('.center-panel-wrapper').on ('click', '.meeting-url-qrcode', function () {
var meetingURL;
try {
meetingURL = $('.meeting-url').val();
if ($('.meeting-url-qrcode-group').is(':empty')) {
// generate code
qrcode = new QRCode($(".meeting-url-qrcode-group")[0], {
text: meetingURL,
width: 128,
height: 128,
colorDark : "#000000",
colorLight : "#ffffff",
correctLevel : QRCode.CorrectLevel.H
});
} else {
// clear the code.
qrcode.clear();
// make another code.
qrcode.makeCode(meetingURL);
}
$(this).trigger('hint', [$(this).data('qrcode-generated-hint')]);
} catch (err) {
$(this).trigger('hint', [$(this).data('qrcode-generate-error')]);
}
});
$('.center-panel-wrapper').on('hint', '.meeting-url-qrcode', function (event, msg) {
$(this).focus();
$(this).attr('title', msg)
.tooltip('fixTitle')
.tooltip('show')
.attr('title', $(this).data('qrcode-generate-hint'))
.tooltip('fixTitle');
});
$('.center-panel-wrapper').on('mouseleave', '.meeting-url-qrcode', function () {
$(this).blur();
});
$('.center-panel-wrapper').on('focus', '.meeting-url', function () {
$(this).select();
});
// only allow ctrl commands
$('.center-panel-wrapper').on('keydown', '.meeting-url', function (event) {
if(!event.ctrlKey) {
event.preventDefault();
}
});
// enable tooltips
var options = {
selector: '.has-tooltip',
container: 'body'
};
$(document).tooltip(options);
options = {
selector: '.bottom-tooltip',
container: 'body',
placement: 'bottom'
};
$(document).tooltip(options);
// focus name input or join button
if ($('.meeting-name').is(':visible')) {
$('.meeting-name').focus();
} else if ($('.meeting-user-name').is(':visible')) {
$('.meeting-user-name').focus();
} else {
$('.meeting-join').focus();
}
};
var initIndex = function() {
$('.center-panel-wrapper').on('input', '.meeting-name', function () {
var newId = $(this).val();
Meeting.getInstance().setMeetingId(newId);
$(".page-wrapper.meetings").data('id', newId);
$('.meeting-url').val(Meeting.getInstance().getURL());
$('.join-meeting-title').text('"'+newId+'"');
if (newId === '') {
$('.invite-join-wrapper').addClass('hidden');
} else {
$('.invite-join-wrapper').removeClass('hidden');
}
if (!$('.meeting-url-qrcode-group').is(':empty')) {
$('.meeting-url-qrcode-group').empty();
}
});
PreviousMeetings.init('joinedMeetings');
};
var initMeetings = function() {
$('.meeting-url').val(Meeting.getInstance().getURL());
};
var initRooms = function() {
displayRoomURL();
var roomAdmin = $('.page-wrapper.rooms').data('admin-id');
$('.center-panel-wrapper').on('input', '.meeting-name', function () {
var newId = $(this).val();
Meeting.getInstance().setMeetingId(newId);
$('.meeting-url').val(Meeting.getInstance().getURL());
$('.join-meeting-title').text('"'+newId+'"');
if (newId === '') {
$('.invite-join-wrapper').addClass('hidden');
} else {
$('.invite-join-wrapper').removeClass('hidden');
}
if (!$('.meeting-url-qrcode-group').is(':empty')) {
$('.meeting-url-qrcode-group').empty();
}
});
if ($(".page-wrapper.rooms").data('main-room')) {
PreviousMeetings.init('joinedRooms-'+roomAdmin);
if ($('input.meeting-name').val() !== '') {
$('input.meeting-name').trigger('input');
}
}
Recordings.getInstance().refresh();
Recordings.getInstance().setupActionHandlers();
};
$(document).on("turbolinks:load", function() {
if ($("body[data-controller=landing]").get(0)) {
init();
if ($("body[data-action=index]").get(0)) {
initIndex();
} else if ($("body[data-action=meetings]").get(0)) {
initMeetings();
} else if ($("body[data-action=rooms]").get(0)) {
initRooms();
}
}
});
}).call(this);

View File

@ -1,51 +0,0 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
$(document).on('turbolinks:load', function(){
$.rails.refreshCSRFTokens();
})
document.addEventListener("turbolinks:before-cache", function() {
$(".alert").remove()
})
// Gets the localized string
function getLocalizedString(key) {
var keyArr = key.split(".")
var translated = I18n
// Search current language for the key
try {
keyArr.forEach(function(k) {
translated = translated[k]
})
} catch (e) {
// Key is missing in selected language so default to english
translated = undefined;
}
// If key is not found, search the fallback language for the key
if (translated === null || translated === undefined) {
translated = I18nFallback
keyArr.forEach(function(k) {
translated = translated[k]
})
}
return translated
}

View File

@ -0,0 +1,135 @@
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
# Meeting class
_meetingInstance = null
class @Meeting
constructor: (@meetingId, @type, @userName, @adminId) ->
# Gets the current instance or creates a new one
@getInstance: ->
if _meetingInstance
return _meetingInstance
meetingId = $(".page-wrapper").data('id')
type = $("body").data('resource')
name = $('.meeting-user-name').val()
adminId = $(".page-wrapper").data('admin-id')
_meetingInstance = new Meeting(meetingId, type, name, adminId)
return _meetingInstance
@clear: ->
_meetingInstance = null
@buildMeetingURL: (meetingId, type, adminId) ->
fullId = ''
if adminId
fullId = encodeURIComponent(adminId) + '/' + encodeURIComponent(meetingId)
else
fullId = encodeURIComponent(meetingId)
return @buildRootURL() + '/' + type + '/' + fullId
@buildRootURL: ->
url = location.protocol + '//' + location.hostname
if location.port
url += ':' + location.port
if GreenLight.RELATIVE_ROOT
url += GreenLight.RELATIVE_ROOT
return url
# Sends the end meeting request
# Returns a response object
endMeeting: ->
return $.ajax({
url: @getURL() + "/end",
type: 'DELETE'
})
# Makes a call to get the join meeting url
# Returns a response object
# The response object contains the URL to join the meeting
getJoinMeetingResponse: ->
if !@userName
showAlert(I18n.enter_name, 4000, 'danger')
return false
return $.get @getURL() + "/join?name=" + @userName, (data) =>
# update name used to join meeting
localStorage.setItem('lastJoinedName', @getUserName())
if data.messageKey == 'ok'
key = ''
if @type == 'meetings'
key = 'joinedMeetings'
else if @type == 'rooms'
key = 'joinedRooms-'+@adminId
# update previously joined meetings/rooms on client
try
joinedMeetings = localStorage.getItem(key) || ''
joinedMeetings = joinedMeetings.split(',')
joinedMeetings = joinedMeetings.filter (item) => item != @meetingId.toString()
if joinedMeetings.length >= 5
joinedMeetings.splice(0, 1)
joinedMeetings.push(@meetingId)
localStorage.setItem(key, joinedMeetings.join(','))
catch err
localStorage.setItem(key, @meetingId)
getMeetingId: ->
return @meetingId
setMeetingId: (id) ->
@meetingId = id
return this
getAdminId: ->
return @adminId
setAdminId: (id) ->
@adminId = id
return this
getType: ->
return @type
setType: (type) ->
@type = type
return this
getURL: ->
return Meeting.buildMeetingURL(@meetingId, @type, @adminId)
getUserName: ->
return @userName
setUserName: (name) ->
@userName = name
return this
getModJoined: ->
return @modJoined
setModJoined: (modJoined) ->
@modJoined = modJoined
return this
getWaitingForMod: ->
return @waitingForMod
setWaitingForMod: (wMod) ->
@waitingForMod = wMod
return this

View File

@ -0,0 +1,55 @@
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
# Previous Meetings class
class @PreviousMeetings
MAX_MEETINGS = 5
# initializes and populates previous meetings list with entries from localStorage
@init: (type) ->
$('.center-panel-wrapper').off 'click', '.fill-meeting-name'
$('.center-panel-wrapper').on 'click', '.fill-meeting-name', (event, msg) ->
name = $(this).text()
$('input.meeting-name').val(name).trigger('input')
$('ul.previously-joined').empty()
joinedMeetings = localStorage.getItem(type)
if joinedMeetings && joinedMeetings.length > 0
joinedMeetings = joinedMeetings.split(',')
PreviousMeetings.append(joinedMeetings.reverse())
# adds to previous meetings list if its unique
@uniqueAdd: (names) ->
meetings = $('ul.previously-joined > li').toArray().map( (li) ->
return li.innerText
)
index = meetings.indexOf('')
if index > 1
meetings.splice(index, 1)
if Array.isArray(names)
names = names.filter( (value) ->
return $.inArray(value, meetings) == -1
)
PreviousMeetings.append(names)
@append: (meeting_names) ->
for m in meeting_names
if $('ul.previously-joined > li').length > MAX_MEETINGS
break
$('ul.previously-joined').append('<li><a class="fill-meeting-name">'+m+'</a></li>')
$('.center-panel-wrapper .previously-joined-wrapper').removeClass('hidden')

View File

@ -1,48 +0,0 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
// Handle changing of settings tabs.
$(document).on('turbolinks:load', function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
if (controller == "rooms" && action == "show"
|| controller == "rooms" && action == "update"
|| controller == "users" && action == "recordings"
|| controller == "admins" && action == "server_recordings"){
// Handle recording emails.
$('.email-link').each(function(){
$(this).click(function(){
var subject = $(".username").text() + " " + getLocalizedString('javascript.room.mailer.subject');
var body = getLocalizedString('javascript.room.mailer.body') + "\n\n" + $(this).attr("data-pres-link");
var autogenerated = getLocalizedString('javascript.room.mailer.autogenerated') + "\n";
var footer = getLocalizedString('javascript.room.mailer.footer');
var url = "mailto:?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body) + encodeURIComponent(autogenerated) + encodeURIComponent(footer);
var win = window.open(url, '_blank');
win.focus();
});
});
// Handle recording delete modal
$(".delete-rec").click(function() {
$("#delete-rec-confirm").parent().attr("action", $(this).data("path"))
})
}
});

View File

@ -0,0 +1,430 @@
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
# Recordings class
_recordingsInstance = null
class @Recordings
# adding or removing a column will require updates to all subsequent column positions
COLUMNS = [
'DATE',
'NAME',
'PREVIEW',
'DURATION',
'PARTICIPANTS',
'PLAYBACK',
'VISIBILITY',
'LISTED',
'ACTION'
]
COLUMN = {}
i = 0
for c in COLUMNS
COLUMN[c] = i++
constructor: ->
recordingsObject = this
# configure the datatable for recordings
this.table = $('#recordings').dataTable({
data: [],
rowId: 'id',
paging: false,
dom: 't',
info: false,
order: [[ COLUMN.DATE, "desc" ]],
language: {
emptyTable: '<h3>'+I18n.no_recordings_yet+'</h3>',
zeroRecords: '<h3>'+I18n.no_recordings+'</h3>'
},
columns: [
{ data: "start_time" },
{ data: "name", visible: $(".page-wrapper.rooms").data('main-room') },
{ data: "previews", orderable: false },
{ data: "duration" },
{ data: "participants" },
{ data: "playbacks", orderable: false },
{ data: "published" },
{ data: "listed", visible: false },
{ data: "id", orderable: false }
],
columnDefs: [
{
targets: COLUMN.DATE,
render: (data, type, row) ->
if type == 'display'
date = new Date(data)
title = date
.toLocaleString($('html').attr('lang'),
{month: 'long', day: 'numeric', year: 'numeric',
hour12: 'true', hour: '2-digit', minute: '2-digit'})
timeago = '<time datetime="'+date.toISOString()+'" data-time-ago="'+date.toISOString()+'">'+date.toISOString()+'</time>'
return title+'<span class="timeago">('+timeago+')</span>'
return data
},
{
targets: COLUMN.PREVIEW,
render: (data, type, row) ->
if type == 'display'
str = ''
if row.published
for d in data
str += '<img height="50" width="50" class="img-thumbnail" src="'+d.url+'" alt="'+d.alt+'"></img><img class="img-thumbnail large" src="'+d.url+'"></img>'
return str
return data
},
{
targets: COLUMN.PLAYBACK,
render: (data, type, row) ->
if type == 'display'
str = ''
if row.published
if data.length == 1
str = '<a class="btn btn-default play-tooltip" href="'+data[0].url+'" target="_blank"><i class="fa fa-play-circle"></i></a>'
else
for d in data
str += '<a href="'+d.url+'" target="_blank">'+d.type_i18n+'</a> '
return str
return data
},
{
targets: COLUMN.VISIBILITY,
render: (data, type, row) ->
visibility = ['unpublished', 'unlisted', 'published']
if row.published
if row.listed
state = visibility[2]
else
state = visibility[1]
else
state = visibility[0]
if type == 'display'
return I18n[state]
return state
},
{
targets: COLUMN.ACTION,
render: (data, type, row) ->
if type == 'display'
roomName = Meeting.getInstance().getMeetingId()
recordingActions = $('.hidden-elements').find('.recording-actions')
classes = ['recording-unpublished', 'recording-unlisted', 'recording-published']
if row.published
if row.listed
cls = classes[2]
else
cls = classes[1]
else
cls = classes[0]
trigger = recordingActions.find('.recording-update-trigger')
trigger.removeClass(classes.join(' '))
trigger.addClass(cls)
return recordingActions.html()
return data
}
]
})
options = {
selector: '.delete-tooltip',
placement: 'bottom',
title: I18n.delete_recording
};
$('#recordings').tooltip(options)
options.selector = '.visibility-tooltip'
options.title = I18n.change_visibility
$('#recordings').tooltip(options)
options.selector = '.play-tooltip'
options.title = I18n.play_recording
$('#recordings').tooltip(options)
options.selector = '.youtube-tooltip'
options.title = I18n.upload_youtube
$('#recordings').tooltip(options)
options.selector = '.upload-tooltip'
options.title = I18n.share
$('#recordings').tooltip(options)
options.selector = '.mail-tooltip'
options.title = I18n.mail_recording
$('#recordings').tooltip(options)
$(document).one "turbolinks:before-cache", =>
@getTable().api().clear().draw().destroy()
# enable popovers
# can't use trigger:'focus' because it doesn't work will with buttons inside
# the popover
options = {
selector: '.has-popover',
html: true,
trigger: 'click',
title: ->
return $(this).data("popover-title");
content: ->
bodySelector = $(this).data("popover-body")
return $(bodySelector).html()
}
$('#recordings').popover(options)
# close popovers manually when clicking outside of them or in buttons
# with [data-dismiss="popover"]
# careful to hide only the open popover and not all of them, otherwise they won't reopen
$('body').on 'click', (e) ->
$('.has-popover').each ->
if !$(this).is(e.target) and $(this).has(e.target).length == 0 and $('.popover.in').has(e.target).length == 0
if $(this).next(".popover.in").length > 0
$(this).popover('hide')
$(document).on 'click', '[data-dismiss="popover"]', (e) ->
$('.has-popover').each ->
if $(this).next(".popover.in").length > 0
$(this).popover('hide')
# Gets the current instance or creates a new one
@getInstance: ->
if _recordingsInstance && Recordings.initialized()
return _recordingsInstance
_recordingsInstance = new Recordings()
return _recordingsInstance
@initialize: ->
Recordings.getInstance()
@initialized: ->
return $.fn.DataTable.isDataTable('#recordings') && _recordingsInstance
draw: ->
if !@isOwner()
@table.api().columns(COLUMN.LISTED).search('true')
@table.api().columns.adjust().draw()
# refresh the recordings from the server
refresh: ->
table_api = this.table.api()
$.get @getRecordingsURL(), (data) =>
@setOwner(data.is_owner)
if !@owner
table_api.column(COLUMN.ACTION).visible(false)
table_api.column(COLUMN.VISIBILITY).visible(false)
for recording in data.recordings
# NOTE: Temporary fix for the minutes to milliseconds bug some recordings may have
# experienced when transitioning from BigBlueButton 1.1 to BigBlueButton 2.0.
recording.duration = if recording.duration < 1000 then recording.duration else parseInt(recording.length / 60000)
data.recordings.sort (a,b) ->
return new Date(b.start_time) - new Date(a.start_time)
table_api.clear()
table_api.rows.add(data.recordings)
@draw()
if $(".page-wrapper.rooms").data('main-room')
recording_names = data.recordings.map (r) ->
return r.name
output = {}
for key in [0...recording_names.length]
output[recording_names[key]] = recording_names[key]
PreviousMeetings.uniqueAdd(value for key, value of output)
# setup click handlers for the action buttons
setupActionHandlers: ->
table_api = this.table.api()
recordingsObject = this
selectedUpload = null
canUpload = false
@getTable().on 'click', '.recording-update', (event) ->
btn = $(this)
row = table_api.row($(this).closest('tr')).data()
url = recordingsObject.getRecordingsURL()
id = row.id
published = btn.data('visibility') == "unlisted" ||
btn.data('visibility') == "published"
listed = btn.data('visibility') == "published"
btn.prop('disabled', true)
data = { published: published.toString() }
data["meta_" + GreenLight.META_LISTED] = listed.toString();
$.ajax({
method: 'PATCH',
url: url+'/'+id,
data: data
}).done((data) ->
btn.prop('disabled', false)
).fail((xhr, text) ->
btn.prop('disabled', false)
)
@getTable().on 'click', '.recording-delete', (event) ->
btn = $(this)
row = table_api.row($(this).closest('tr'))
url = recordingsObject.getRecordingsURL()
id = row.data().id
btn.prop('disabled', true)
$.ajax({
method: 'DELETE',
url: url+'/'+id
}).done((data) ->
btn.prop('disabled', false)
).fail((xhr, text) ->
btn.prop('disabled', false)
if xhr.status == 404
row.remove();
recordingsObject.draw()
showAlert(I18n.recording_deleted, 4000);
)
@getTable().on 'click', '.upload-button', (event) ->
btn = $(this)
row = table_api.row($(this).closest('tr')).data()
url = recordingsObject.getRecordingsURL()
id = row.id
title = $('#video-title').val()
privacy_status = $('input[name=privacy_status]:checked').val()
if title == ''
title = row.name
$.ajax({
method: 'POST',
url: url+'/'+id
data: {video_title: title, privacy_status: privacy_status}
success: (data) ->
if data['url'] != null
window.location.href = data['url']
else
cloud = selectedUpload.find('.cloud-blue')
check = selectedUpload.find('.green-check')
spinner = selectedUpload.find('.load-spinner')
showAlert(I18n.successful_upload, 4000);
spinner.hide()
check.show()
setTimeout ( ->
cloud.show()
check.hide()
), 2500
})
selectedUpload.find('.cloud-blue').hide()
selectedUpload.find('.load-spinner').show()
@getTable().on 'click', '.mail-recording', (event) ->
btn = $(this)
row = table_api.row($(this).closest('tr')).data()
url = recordingsObject.getRecordingsURL()
id = row.id
# Take the username from the header.
username = $('#title-header').text().replace('Welcome ', '').trim()
recording_url = row.playbacks[0].url
webcams_url = getHostName(recording_url) + '/presentation/' + id + '/video/webcams.webm'
subject = username + I18n.recording_mail_subject
body = I18n.recording_mail_body + "\n\n" + recording_url + "\n\n" + I18n.email_footer_1 + "\n" + I18n.email_footer_2
mailto = "mailto:?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body);
window.open(mailto);
@getTable().on 'click', '.youtube-upload', (event) ->
row = table_api.row($(this).closest('tr')).data()
$('#video-title').attr('value', row.name)
@getTable().on 'click', '.cloud-upload', (event) ->
btn = $(this)
row = table_api.row($(this).closest('tr')).data()
id = row.id
selectedUpload = $(this)
# Determine if the recording can be uploaded to Youtube.
$.ajax({
method: 'POST',
data: {'rec_id': id},
async: false,
url: recordingsObject.getRecordingsURL() + '/can_upload'
}).success((res_data) ->
canUpload = res_data['uploadable']
)
youtube_button = $('.share-popover').find('.youtube-upload')
attr = $(this).attr('data-popover-body');
# Check if the cloud button has a popover body.
if (typeof attr == typeof undefined || attr == false)
switch canUpload
# We can upload the recording.
when 'true'
youtube_button.attr('title', I18n.upload_youtube)
youtube_button.removeClass('disabled-button')
youtube_button.addClass('has-popover')
youtube_button.show()
# Can't upload because uploading is disabled.
when 'uploading_disabled'
youtube_button.hide()
# Can't upload because account is not authenticated with Google.
when 'invalid_provider'
youtube_button.attr('title', I18n.invalid_provider)
youtube_button.addClass('disabled-button')
youtube_button.removeClass('has-popover')
youtube_button.show()
# Can't upload because recording does not contain video.
else
youtube_button.attr('title', I18n.no_video)
youtube_button.addClass('disabled-button')
youtube_button.removeClass('has-popover')
youtube_button.show()
$(this).attr('data-popover-body', '.share-popover')
$(this).popover('show')
else
$(this).popover('hide')
$(this).removeAttr('data-popover-body')
@getTable().on 'draw.dt', (event) ->
$('time[data-time-ago]').timeago();
getTable: ->
@table
getHostName = (url) ->
parser = document.createElement('a');
parser.href = url;
parser.origin;
getRecordingsURL: ->
if $(".page-wrapper.rooms").data('main-room')
base_url = Meeting.buildRootURL()+'/'+$('body').data('resource')+'/'+Meeting.getInstance().getAdminId()
else
base_url = $('.meeting-url').val()
base_url+'/recordings'
isOwner: ->
@owner
setOwner: (owner) ->
@owner = owner

View File

@ -1,162 +0,0 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
$(document).on('turbolinks:load', function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
if(controller == "rooms" && action == "show"
|| controller == "rooms" && action == "update"
|| controller == "users" && action == "recordings"
|| controller == "admins" && action == "server_recordings"){
// Set a room header rename event
var configure_room_header = function(room_title){
function register_room_title_event(e){
// Remove current window events
$(window).off('mousedown keydown');
if(e.type == 'focusout'){
submit_rename_request(room_title);
return;
}
room_title.addClass("dotted_underline");
room_title.find('#user-text').fadeTo('medium', 0.7);
room_title.find('#user-text').attr("contenteditable", true);
room_title.find('#user-text').focus();
// Stop automatic refresh
e.preventDefault();
register_window_event(room_title, 'user-text', '#edit-room', 'edit-room');
}
room_title.find('#user-text').on('dblclick focusout', function(e){
if(room_title.find('#edit-room').length){
register_room_title_event(e);
}
});
room_title.find('.fa-edit').on('click', function(e){
register_room_title_event(e);
});
}
// Set a recording row rename event
var configure_recording_row = function(recording_title){
function register_recording_title_event(e){
// Remove current window events
$(window).off('mousedown keydown');
if(e.type == 'focusout'){
submit_rename_request(recording_title);
return;
}
recording_title.addClass("dotted_underline");
recording_title.fadeTo('medium', 0.7);
recording_title.find('span').attr("contenteditable", true);
recording_title.find('span').focus();
// Stop automatic refresh
e.preventDefault();
register_window_event(recording_title, 'recording-text', '#edit-record', 'edit-recordid');
}
recording_title.find('a').on('click focusout', function(e){
register_recording_title_event(e);
});
recording_title.find('#recording-text').on('dblclick focusout', function(e){
register_recording_title_event(e);
});
}
// Register window event to submit new name
// upon click or upon pressing the enter key
var register_window_event = function(element, textfield_id, edit_button_id, edit_button_data){
$(window).on('mousedown keydown', function(clickEvent){
// Return if the text is clicked
if(clickEvent.type == "mousedown" && clickEvent.target.id == textfield_id){
return;
}
// Return if the edit icon is clicked
if(clickEvent.type == "mousedown" && $(clickEvent.target).is(edit_button_id) &&
$(clickEvent.target).data(edit_button_data) === element.find(edit_button_id).data(edit_button_data)){
return;
}
// Check if event is keydown and enter key is not pressed
if(clickEvent.type == "keydown" && clickEvent.which !== 13){
return;
}
clickEvent.preventDefault();
submit_rename_request(element);
// Remove window event when ajax call to update name is submitted
$(window).off('mousedown keydown');
});
}
// Apply ajax request depending on the element that triggered the event
var submit_rename_request = function(element){
if(element.is('#room-title')){
submit_update_request({
setting: "rename_header",
name: element.find('#user-text').text(),
}, element.data('path'), "POST");
}
else if(element.is('#recording-title')){
submit_update_request({
setting: "rename_recording",
record_id: element.data('recordid'),
record_name: element.find('span').text(),
room_uid: element.data('room-uid'),
}, element.data('path'), "PATCH");
}
}
// Helper for submitting ajax requests
var submit_update_request = function(data, path, action){
// Send ajax request for update
$.ajax({
url: path,
type: action,
data: data,
});
}
// Elements that can be renamed
var room_title = $('#room-title');
var recording_rows = $('#recording-table').find('tr');
// Configure renaming for room header
configure_room_header(room_title);
// Configure renaming for recording rows
recording_rows.each(function(){
var recording_title = $(this).find('#recording-title');
configure_recording_row(recording_title);
});
}
});

View File

@ -1,502 +0,0 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
// Room specific js for copy button and email link.
$(document).on('turbolinks:load', function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
// highlight current room
$('.room-block').removeClass('current');
$('a[href="' + window.location.pathname + '"] .room-block').addClass('current');
// Only run on room pages.
if (controller == "rooms" && action == "show"){
// Display and update all fields related to creating a room in the createRoomModal
$("#create-room-block").click(function(){
showCreateRoom(this)
})
checkIfAutoJoin()
}
// Autofocus on the Room Name label when creating a room only
$('#createRoomModal').on('shown.bs.modal', function (){
if ($(".create-only").css("display") == "block"){
$('#create-room-name').focus()
}
})
if (controller == "rooms" && action == "show" || controller == "admins" && action == "server_rooms"){
// Display and update all fields related to creating a room in the createRoomModal
$(".update-room").click(function(){
showUpdateRoom(this)
})
// share room pop up accessibility
manageAccessAccessibility();
$(".delete-room").click(function() {
showDeleteRoom(this)
})
// For keyboard users to be able to generate access code
generateAccessCodeAccessibility()
$('.selectpicker').selectpicker({
liveSearchPlaceholder: getLocalizedString('javascript.search.start')
});
// Fixes turbolinks issue with bootstrap select
$(window).trigger('load.bs.select.data-api');
$(".share-room").click(function() {
// Update the path of save button
$("#save-access").attr("data-path", $(this).data("path"))
$("#room-owner-uid").val($(this).data("owner"))
// Get list of users shared with and display them
displaySharedUsers($(this).data("users-path"))
})
$("#shareRoomModal").on("show.bs.modal", function() {
$(".selectpicker").selectpicker('val','')
})
$(".bootstrap-select").on("click", function() {
$(".bs-searchbox").siblings().hide()
})
$("#share-room-select ~ button").on("click", function() {
$(".bs-searchbox").siblings().hide()
})
$(".bs-searchbox input").on("input", function() {
if ($(".bs-searchbox input").val() == '' || $(".bs-searchbox input").val().length < 3) {
$(".select-options").remove()
$(".bs-searchbox").siblings().hide()
} else {
// Manually populate the dropdown
$.get($("#share-room-select").data("path"), { search: $(".bs-searchbox input").val(), owner_uid: $("#room-owner-uid").val() }, function(users) {
$(".select-options").remove()
if (users.length > 0) {
users.forEach(function(user) {
let opt = document.createElement("option")
$(opt).val(user.uid)
$(opt).text(user.name)
$(opt).addClass("select-options")
$(opt).attr("data-subtext", user.uid)
$("#share-room-select").append(opt)
})
// Only refresh the select dropdown if there are results to show
$('#share-room-select').selectpicker('refresh');
}
$(".bs-searchbox").siblings().show()
})
}
})
$(".remove-share-room").click(function() {
$("#remove-shared-confirm").parent().attr("action", $(this).data("path"))
})
// User selects an option from the Room Access dropdown
$(".bootstrap-select").on("changed.bs.select", function(){
// Get the uid of the selected user
let uid = $(".selectpicker").selectpicker('val')
// If the value was changed to blank, ignore it
if (uid == "") return
let currentListItems = $("#user-list li").toArray().map(user => $(user).data("uid"))
// Check to make sure that the user is not already there
if (!currentListItems.includes(uid)) {
// Create the faded list item and display it
let option = $("option[value='" + uid + "']")
let listItem = document.createElement("li")
listItem.setAttribute('class', 'list-group-item text-left not-saved add-access');
listItem.setAttribute("data-uid", uid)
let spanItemAvatar = document.createElement("span"),
spanItemName = document.createElement("span"),
spanItemUser = document.createElement("span");
spanItemAvatar.setAttribute('class', 'avatar float-left mr-2');
spanItemAvatar.innerText = option.text().charAt(0);
spanItemName.setAttribute('class', 'shared-user');
spanItemName.innerText = option.text();
spanItemUser.setAttribute('class', 'text-muted');
spanItemUser.innerText = option.data('subtext');
spanItemName.append(spanItemUser);
listItem.innerHTML = "<span class='text-primary float-right shared-user cursor-pointer' onclick='removeSharedUser(this)'><i class='fas fa-times'></i></span>"
listItem.prepend(spanItemName);
listItem.prepend(spanItemAvatar);
$("#user-list").append(listItem)
}
})
$("#presentation-upload").change(function(data) {
var file = data.target.files[0]
// Check file type and size to make sure they aren't over the limit
if (validFileUpload(file)) {
$("#presentation-upload-label").text(file.name)
} else {
$("#invalid-file-type").show()
$("#presentation-upload").val("")
$("#presentation-upload-label").text($("#presentation-upload-label").data("placeholder"))
}
})
$(".preupload-room").click(function() {
updatePreuploadPresentationModal(this)
})
$("#remove-presentation").click(function(data) {
removePreuploadPresentation($(this).data("remove"))
})
// trigger initial room filter
filterRooms();
}
});
function copyInvite() {
$('#invite-url').select()
if (document.execCommand("copy")) {
$('#invite-url').blur();
copy = $("#copy-invite")
copy.addClass('btn-success');
copy.html("<i class='fas fa-check mr-1'></i>" + getLocalizedString("copied"))
setTimeout(function(){
copy.removeClass('btn-success');
copy.html("<i class='fas fa-copy mr-1'></i>" + getLocalizedString("copy"))
}, 1000)
}
}
function copyAccess(target) {
input = target ? $("#copy-" + target + "-code") : $("#copy-code")
input.attr("type", "text")
input.select()
if (document.execCommand("copy")) {
input.attr("type", "hidden")
copy = target ? $("#copy-" + target + "-access") : $("#copy-access")
copy.addClass('btn-success');
copy.html("<i class='fas fa-check mr-1'></i>" + getLocalizedString("copied"))
setTimeout(function(){
copy.removeClass('btn-success');
originalString = target ? getLocalizedString("room.copy_" + target + "_access") : getLocalizedString("room.copy_access")
copy.html("<i class='fas fa-copy mr-1'></i>" + originalString)
}, 1000)
}
}
function showCreateRoom(target) {
$("#create-room-name").val("")
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code_placeholder"))
$("#create-room-moderator-access-code").text(getLocalizedString("modal.create_room.moderator_access_code_placeholder"))
$("#room_access_code").val(null)
$("#room_moderator_access_code").val(null)
$("#createRoomModal form").attr("action", $("body").data('relative-root'))
$("#room_mute_on_join").prop("checked", $("#room_mute_on_join").data("default"))
$("#room_require_moderator_approval").prop("checked", $("#room_require_moderator_approval").data("default"))
$("#room_anyone_can_start").prop("checked", $("#room_anyone_can_start").data("default"))
$("#room_all_join_moderator").prop("checked", $("#room_all_join_moderator").data("default"))
$("#room_recording").prop("checked", $("#room_recording").data("default"))
//show all elements & their children with a create-only class
$(".create-only").each(function() {
$(this).show()
if($(this).children().length > 0) { $(this).children().show() }
})
//hide all elements & their children with a update-only class
$(".update-only").each(function() {
$(this).attr('style',"display:none !important")
if($(this).children().length > 0) { $(this).children().attr('style',"display:none !important") }
})
}
function showUpdateRoom(target) {
var modal = $(target)
var update_path = modal.closest(".room-block").data("path")
var settings_path = modal.data("settings-path")
$("#create-room-name").val(modal.closest(".room-block").find(".room-name-text").text().trim())
$("#createRoomModal form").attr("action", update_path)
//show all elements & their children with a update-only class
$(".update-only").each(function() {
$(this).show()
if($(this).children().length > 0) { $(this).children().show() }
})
//hide all elements & their children with a create-only class
$(".create-only").each(function() {
$(this).attr('style',"display:none !important")
if($(this).children().length > 0) { $(this).children().attr('style',"display:none !important") }
})
updateCurrentSettings(settings_path)
var accessCode = modal.closest(".room-block").data("room-access-code")
if(accessCode){
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code") + ": " + accessCode)
$("#room_access_code").val(accessCode)
} else {
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code_placeholder"))
$("#room_access_code").val(null)
}
var moderatorAccessCode = modal.closest(".room-block").data("room-moderator-access-code")
if(moderatorAccessCode){
$("#create-room-moderator-access-code").text(getLocalizedString("modal.create_room.moderator_access_code") + ": " + moderatorAccessCode)
$("#room_moderator_access_code").val(moderatorAccessCode)
} else {
$("#create-room-moderator-access-code").text(getLocalizedString("modal.create_room.moderator_access_code_placeholder"))
$("#room_moderator_access_code").val(null)
}
}
function showDeleteRoom(target) {
$("#delete-header").text(getLocalizedString("modal.delete_room.confirm").replace("%{room}", $(target).data("name")))
$("#delete-confirm").parent().attr("action", $(target).data("path"))
}
//Update the createRoomModal to show the correct current settings
function updateCurrentSettings(settings_path){
// Get current room settings and set checkbox
$.get(settings_path, function(settings) {
$("#room_mute_on_join").prop("checked", $("#room_mute_on_join").data("default") || settings.muteOnStart)
$("#room_require_moderator_approval").prop("checked", $("#room_require_moderator_approval").data("default") || settings.requireModeratorApproval)
$("#room_anyone_can_start").prop("checked", $("#room_anyone_can_start").data("default") || settings.anyoneCanStart)
$("#room_all_join_moderator").prop("checked", $("#room_all_join_moderator").data("default") || settings.joinModerator)
$("#room_recording").prop("checked", $("#room_recording").data("default") || Boolean(settings.recording))
})
}
function generateAccessCode(){
const accessCodeLength = 6
var validCharacters = "0123456789"
var accessCode = ""
for( var i = 0; i < accessCodeLength; i++){
accessCode += validCharacters.charAt(Math.floor(Math.random() * validCharacters.length));
}
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code") + ": " + accessCode)
$("#room_access_code").val(accessCode)
}
function ResetAccessCode(){
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code_placeholder"))
$("#room_access_code").val(null)
}
function generateModeratorAccessCode(){
const accessCodeLength = 6
var validCharacters = "abcdefghijklmopqrstuvwxyz"
var accessCode = ""
for( var i = 0; i < accessCodeLength; i++){
accessCode += validCharacters.charAt(Math.floor(Math.random() * validCharacters.length));
}
$("#create-room-moderator-access-code").text(getLocalizedString("modal.create_room.moderator_access_code") + ": " + accessCode)
$("#room_moderator_access_code").val(accessCode)
}
function ResetModeratorAccessCode(){
$("#create-room-moderator-access-code").text(getLocalizedString("modal.create_room.moderator_access_code_placeholder"))
$("#room_moderator_access_code").val(null)
}
function saveAccessChanges() {
let listItemsToAdd = $("#user-list li:not(.remove-shared)").toArray().map(user => $(user).data("uid"))
$.post($("#save-access").data("path"), {add: listItemsToAdd})
}
// Get list of users shared with and display them
function displaySharedUsers(path) {
$.get(path, function(users) {
// Create list element and add to user list
var user_list_html = ""
users.forEach(function(user) {
user_list_html += "<li class='list-group-item text-left' data-uid='" + user.uid + "'>"
if (user.image) {
user_list_html += "<img id='user-image' class='avatar float-left mr-2' src='" + user.image + "'></img>"
} else {
user_list_html += "<span class='avatar float-left mr-2'>" + user.name.charAt(0) + "</span>"
}
user_list_html += "<span class='shared-user'>" + user.name + "<span class='text-muted ml-1'>" + user.uid + "</span></span>"
user_list_html += "<span class='text-primary float-right shared-user cursor-pointer' onclick='removeSharedUser(this)'><i class='fas fa-times'></i></span>"
user_list_html += "</li>"
})
$("#user-list").html(user_list_html)
});
}
// Removes the user from the list of shared users
function removeSharedUser(target) {
let parentLI = target.closest("li")
if (parentLI.classList.contains("not-saved")) {
parentLI.parentNode.removeChild(parentLI)
} else {
parentLI.removeChild(target)
parentLI.classList.add("remove-shared")
}
}
function updatePreuploadPresentationModal(target) {
$.get($(target).data("settings-path"), function(presentation) {
if(presentation.attached) {
$("#current-presentation").show()
$("#presentation-name").text(presentation.name)
$("#change-pres").show()
$("#use-pres").hide()
} else {
$("#current-presentation").hide()
$("#change-pres").hide()
$("#use-pres").show()
}
});
$("#preuploadPresentationModal form").attr("action", $(target).data("path"))
$("#remove-presentation").data("remove", $(target).data("remove"))
// Reset values to original to prevent confusion
$("#presentation-upload").val("")
$("#presentation-upload-label").text($("#presentation-upload-label").data("placeholder"))
$("#invalid-file-type").hide()
}
function removePreuploadPresentation(path) {
$.post(path, {})
}
function validFileUpload(file) {
return file.size/1024/1024 <= 30
}
// Automatically click the join button if this is an action cable reload
function checkIfAutoJoin() {
var url = new URL(window.location.href)
if (url.searchParams.get("reload") == "true") {
$("#joiner-consent").click()
$("#room-join").click()
}
}
function filterRooms() {
let search = $('#room-search').val()
if (search == undefined) { return }
let search_term = search.toLowerCase(),
rooms = $('#room_block_container > div:not(:last-child)');
clear_room_search = $('#clear-room-search');
if (search_term) {
clear_room_search.show();
} else {
clear_room_search.hide();
}
rooms.each(function(i, room) {
let text = $(this).find('h4').text();
room.style.display = (text.toLowerCase().indexOf(search_term) < 0) ? 'none' : 'block';
})
}
function clearRoomSearch() {
$('#room-search').val('');
filterRooms()
}
function manageAccessAccessibility() {
// share room pop up accessibility
var holdModal = false;
$("#shareRoomModal").on("show.bs.modal", function() {
// for screen reader to be able to read results
$("#shareRoomModal .form-control").attr("aria-atomic", true);
$("#shareRoomModal .dropdown-menu div.inner").attr("role", "alert");
$("#shareRoomModal ul.dropdown-menu").attr("role", "listbox");
$("#shareRoomModal div.dropdown-menu").find("*").keyup(function(event) {
$("#shareRoomModal ul.dropdown-menu li").attr("aria-selected", false);
$("#shareRoomModal ul.dropdown-menu li.active").attr("aria-selected", true);
$("#shareRoomModal ul.dropdown-menu li.active a").attr("aria-selected", true);
});
// for keyboard support
// so that it can escape / close search user without closing the modal
$("#shareRoomModal div.dropdown-menu input").keydown(function(event) {
if (event.keyCode === 27) {
holdModal = true;
}
});
});
// reset escape button if the search is closed / done
$("#shareRoomModal").on("hide.bs.modal", function(e) {
if (holdModal) {
holdModal = false;
e.stopPropagation();
return false;
}
});
}
function generateAccessCodeAccessibility() {
// For keyboard users to be able to generate access code
$("#generate-room-access-code").keyup(function(event) {
if (event.keyCode === 13 || event.keyCode === 32) {
generateAccessCode();
}
})
// For keyboard users to be able to reset access code
$("#reset-access-code").keyup(function(event) {
if (event.keyCode === 13 || event.keyCode === 32) {
ResetAccessCode();
}
})
// For keyboard users to be able to generate access code
// for moderator
$("#generate-moderator-room-access-code").keyup(function(event) {
if (event.keyCode === 13 || event.keyCode === 32) {
generateModeratorAccessCode();
}
})
// For keyboard users to be able to reset access code
// for moderator
$("#reset-moderator-access-code").keyup(function(event) {
if (event.keyCode === 13 || event.keyCode === 32) {
ResetModeratorAccessCode();
}
})
}

View File

@ -1,103 +0,0 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
$(document).on('turbolinks:load', function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
if ((controller == "admins" && action == "index") ||
(controller == "rooms" && action == "show") ||
(controller == "rooms" && action == "update") ||
(controller == "rooms" && action == "join") ||
(controller == "users" && action == "recordings") ||
(controller == "admins" && action == "server_recordings") ||
(controller == "admins" && action == "server_rooms")) {
// Submit search if the user hits enter
$("#search-input").keypress(function(key) {
if (key.which == 13) {
searchPage()
}
})
// Add listeners for sort
$("th[data-order]").click(function(data){
var header_elem = $(data.target)
if(header_elem.data('order') === 'asc'){ // asc
header_elem.data('order', 'desc');
}
else if(header_elem.data('order') === 'desc'){ // desc
header_elem.data('order', 'none');
}
else{ // none
header_elem.data('order', 'asc');
}
var search = $("#search-input").val();
var url = window.location.pathname + "?page=1&search=" + search + "&column=" + header_elem.data("header") +
"&direction=" + header_elem.data('order')
window.location.replace(addRecordingTable(url))
})
if(controller === "rooms" && action === "show"){
$(".page-item > a").each(function(){
if(!$(this).attr('href').endsWith("#")){
$(this).attr('href', $(this).attr('href') + "#recordings-table")
}
})
}
}
})
// Searches the user table for the given string
function searchPage() {
var search = $("#search-input").val();
// Check if the user filtered by role
var role = new URL(location.href).searchParams.get('role')
var tab = new URL(location.href).searchParams.get('tab')
var url = window.location.pathname + "?page=1&search=" + search
if (role) { url += "&role=" + role }
if (tab) { url += "&tab=" + tab }
window.location.replace(addRecordingTable(url));
}
// Clears the search bar
function clearSearch() {
var role = new URL(location.href).searchParams.get('role')
var tab = new URL(location.href).searchParams.get('tab')
var url = window.location.pathname + "?page=1"
if (role) { url += "&role=" + role }
if (tab) { url += "&tab=" + tab }
window.location.replace(addRecordingTable(url));
var search_params = new URLSearchParams(window.location.search)
}
function addRecordingTable(url) {
if($("body").data('controller') === "rooms" && $("body").data('action') === "show") {
url += "#recordings-table"
}
return url
}

View File

@ -1,42 +0,0 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
// Handle changing of settings tabs.
$(document).on('turbolinks:load', function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
// Only run on the settings page.
if ((controller == "users" && action == "edit") || (controller == "users" && action == "update")){
var settingsButtons = $('.setting-btn');
var settingsViews = $('.setting-view');
settingsButtons.each(function(i, btn) {
if(!$(btn).hasClass("active")){ $(settingsViews[i]).hide(); }
$(btn).click(function(){
$(btn).addClass("active");
settingsViews.each(function(i, view){
if($(view).attr("id") == $(btn).attr("id")){
$(view).show();
} else {
$(settingsButtons[i]).removeClass("active");
$(view).hide();
}
});
});
});
}
});

View File

@ -0,0 +1,93 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
$.ajaxSetup({
headers: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
}
});
var loopJoin = function() {
var jqxhr = Meeting.getInstance().getJoinMeetingResponse();
jqxhr.done(function(data) {
if (data.messageKey === 'wait_for_moderator') {
setTimeout(loopJoin, 5000);
} else {
$(location).attr("href", data.response.join_url);
}
});
jqxhr.fail(function(xhr, status, error) {
console.info("meeting join failed");
});
};
var alertTimeout = null;
var showAlert = function(html, timeout_delay, type) {
type = type || 'success'
if (!html) {
return;
}
$('.alert-template .alert-message').html(html);
$('.alert-template .alert').removeClass('alert-success alert-danger alert-info alert-warning').addClass('alert-'+type);
$('#alerts').html($('.alert-template').html());
if (timeout_delay) {
clearTimeout(alertTimeout);
alertTimeout = setTimeout(function() {
$('#alerts > .alert').alert('close');
}, timeout_delay);
}
};
// remove flash alerts after 5 seconds
var flashAlertDelayedDismiss = function() {
setTimeout(function(){
$('.flash-alerts').remove();
}, 5000);
};
var displayRoomURL = function() {
$('.meeting-url').val(Meeting.getInstance().getURL());
};
var showNotification = function(title, options) {
if (Notification.permission === "granted") {
var icon = '<%= asset_path("bbb-logo.png") %>';
options = $.extend(options, {
icon: icon,
tag: 'UserWaiting'
});
var notification = new Notification(title, options);
notification.onclick = function() {
window.focus();
};
}
};
var isRoomOwner = function() {
return $('body').data('current-user') === $('.page-wrapper.rooms').data('admin-id')
}
// For now there are notifications only for users signed in and when they
// are in their room's page
$(document).on("turbolinks:load", function() {
if (isRoomOwner()) {
Notification.requestPermission();
}
flashAlertDelayedDismiss();
});

View File

@ -1,105 +0,0 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
$(document).on('turbolinks:load', function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
if(controller == "rooms" && action == "show"
|| controller == "rooms" && action == "update"
|| controller == "users" && action == "recordings"
|| controller == "admins" && action == "server_recordings"){
// Choose active header
// (Name, Length or Users)
$('th').each(function(){
if($(this).data("header")){
$(this).on('click', function(){
set_active_header($(this).data("header"));
sort_by($(this).data("header"), $(this).data('order'));
});
}
});
// Based on the header (Name, Length or Users) clicked,
// Modify the ui for the tables
var set_active_header = function(active_header){
$('th').each(function(){
if($(this).data("header") == active_header){
configure_order($(this));
}
else{
$(this).text($(this).data("header"));
$(this).data('order', 'none');
}
});
}
// Based on the header (Name, Length or Users) clicked,
// Modify the ui for the tables
var configure_order = function(header_elem){
if(header_elem.data('order') === 'asc'){ // asc
header_elem.data('order', 'desc');
}
else if(header_elem.data('order') === 'desc'){ // desc
header_elem.data('order', 'none');
}
else{ // none
header_elem.data('order', 'asc');
}
}
// Given a label and an order, sort recordings by order
// under a given label
var sort_by = function(label, order){
var recording_list_tbody = $('.table-responsive').find('tbody');
if(label === "Name"){
sort_recordings(recording_list_tbody, order, "#recording-title");
}
else if(label === "Length"){
sort_recordings(recording_list_tbody, order, "#recording-length");
}
else if(label === "Users"){
sort_recordings(recording_list_tbody, order, "#recording-users");
}
}
// Generalized function for sorting recordings
var sort_recordings = function(recording_list_tbody, order, recording_id){
recording_list_tbody.find('tr').sort(function(a, b){
var a_val, b_val;
if (recording_id == "#recording-length") {
a_val = $.trim($(a).find(recording_id).data("full-length"));
b_val = $.trim($(b).find(recording_id).data("full-length"));
} else {
a_val = $.trim($(a).find(recording_id).text());
b_val = $.trim($(b).find(recording_id).text());
}
if(order === "asc"){
return a_val.localeCompare(b_val);
}
else if(order === "desc"){
return b_val.localeCompare(a_val);
} else {
return undefined;
}
}).appendTo(recording_list_tbody);
}
}
});

View File

@ -1,46 +0,0 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
$(document).on('turbolinks:load', function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
if ((controller == "admins" && action == "edit_user") || (controller == "users" && action == "edit")) {
// Hack to make it play nice with turbolinks
if ($("#role-dropdown:visible").length == 0){
$(window).trigger('load.bs.select.data-api')
}
// Check to see if the role dropdown was set up
if ($("#role-dropdown").length != 0){
$("#role-dropdown").selectpicker('val', $("#user_role_id").val())
}
// Update hidden field with new value
$("#role-dropdown").on("changed.bs.select", function(){
$("#user_role_id").val($("#role-dropdown").selectpicker('val'))
})
// Update hidden field with new value
// $("#language-dropdown").on("show.bs.select", function(){
// $("#language-dropdown").selectpicker('val', $("#user_language").val())
// })
// Update hidden field with new value
$("#language-dropdown").on("changed.bs.select", function(){
$("#user_language").val($("#language-dropdown").selectpicker('val'))
})
}
})

View File

@ -1,67 +0,0 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
// Handle client request to join when meeting starts.
$(document).on("turbolinks:load", function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
if(controller == "rooms" && action == "join"){
App.waiting = App.cable.subscriptions.create({
channel: "WaitingChannel",
roomuid: $(".background").attr("room"),
useruid: $(".background").attr("user")
}, {
connected: function() {
console.log("connected");
setTimeout(startRefreshTimeout, 120000);
},
disconnected: function(data) {
console.log("disconnected");
console.log(data);
},
rejected: function() {
console.log("rejected");
},
received: function(data){
console.log(data);
if(data.action == "started"){
request_to_join_meeting();
}
}
});
}
});
var join_attempts = 0;
function request_to_join_meeting() {
$.post(window.location.pathname, { join_name: $(".background").attr("join-name") }, function() {
//Successful post - set up retry incase
if(join_attempts < 4){ setTimeout(request_to_join_meeting, 10000); }
join_attempts++;
})
}
// Refresh the page after 2 mins and attempt to reconnect to ActionCable
function startRefreshTimeout() {
var url = new URL(window.location.href)
url.searchParams.set("reload","true")
window.location.href = url.href
}

View File

@ -1,81 +0,0 @@
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
*
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*/
@import "tabler/functions";
@import "tabler/core";
@import "tabler/type";
@import "tabler/grid";
@import "tabler/layout";
@import "tabler/aside";
@import "tabler/header";
@import "tabler/footer";
@import "tabler/colors";
@import "tabler/text";
@import "tabler/utilities";
@import "tabler/nav";
@import "tabler/button";
@import "tabler/alert";
//@import "tabler/close";
//@import "tabler/badge";
@import "tabler/tables";
//@import "tabler/breadcrumb";
//@import "tabler/pagination";
@import "tabler/cards";
//@import "tabler/popover";
@import "tabler/dropdown";
@import "tabler/list";
@import "tabler/list-group";
@import "tabler/avatar";
//@import "tabler/product";
@import "tabler/progress";
@import "tabler/icon";
//@import "tabler/image";
//@import "tabler/link";
//@import "tabler/media";
@import "tabler/form";
//@import "tabler/sparkline";
@import "tabler/social";
//@import "tabler/maps";
//@import "tabler/statuses";
//@import "tabler/charts";
//@import "tabler/chips";
@import "tabler/stamp";
//@import "tabler/chat";
//@import "tabler/example";
@import "tabler/tag";
//@import "tabler/syntax";
//@import "tabler/infobox";
//@import "tabler/carousel";
//@import "tabler/forms/custom-range";
//@import "tabler/forms/custom-selectgroup";
@import "tabler/forms/custom-switch";
//@import "tabler/forms/custom-imagecheck";
@import "tabler/forms/custom-colorinput";
//@import "tabler/timeline";
//@import "tabler/browser";
//@import "tabler/flag";
//@import "tabler/payments";
//@import "tabler/jvectormap";
//@import "tabler/selectize";
//@import "tabler/fonts/feather";

View File

@ -1,105 +0,0 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
#users-table {
.user-role {
color: white !important;
}
.user-email {
max-width: 250px;
}
}
#clear-search {
z-index: 9;
position: absolute;
right: 55px;
top: 8px;
&:hover {
cursor: pointer;
}
}
.tag i {
color: white !important;
}
#branding-image{
z-index: auto;
}
.authentication-required{
padding-top: 2px;
}
#site_settings {
.colorinput-color {
text-align: center;
padding-top: 4px;
height: 2rem;
width: 2rem;
}
}
.sort-disabled{
background: #e6e6e6 !important;
color: rgb(110, 118, 135) !important;
opacity: 0.75;
&:hover{
opacity: 0.9;
}
}
.form-disable{
background-color: #e6e6e6;
}
.role-colour-picker{
color: white !important;
}
.custom-role-tag{
color: white !important;
// Make it consistent with the manage users tab tags
padding-top: 1px;
padding-bottom: 1px;
}
.user-role-tag{
color: white !important;
}
#manage-users-nav.nav-tabs .nav-item {
margin-bottom: -1px;
}
#merge-account-arrow {
position: absolute;
top: 47%;
right: 47%;
z-index: 999;
background: white;
}
.admin-tabs {
justify-content: space-around;
}
#rooms-table-div {
overflow: visible;
}

View File

@ -0,0 +1,34 @@
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
*
* Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*= require jquery-ui
*= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
*= require_tree ./main
*= require_self
*/

View File

@ -1,193 +0,0 @@
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
*
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*= require_self
*/
@import "tabler/variables";
@import "bootstrap";
@import "jquery-ui/sortable";
@import "tabler-custom";
@import "font-awesome-sprockets";
@import "font-awesome";
@import "monolith.min.scss";
@import "bootstrap-select.min";
@import "utilities/variables";
@import "admins";
@import "main";
@import "rooms";
@import "sessions";
@import "utilities/fonts";
@import "users";
* {
outline: none !important;
}
html, body {
font-family: "Source Sans Pro" !important;
position: relative;
width: 100%;
height: 100%;
background-color: white;
}
a {
text-decoration: none !important;
cursor: pointer;
}
.dotted_underline {
border-bottom: 3px dashed #d3d3d3;
width: 100%;
}
.disable-click {
pointer-events: none;
}
.header {
height: $header-height;
}
.wrapper {
position: relative;
height: auto;
min-height: calc(100% - #{$header-height} - #{$footer-height});
}
.footer {
height: $footer-height;
width: 100%;
}
.background {
background-color: $background-color;
}
.error-section {
background-color: $error-background-color;
}
.font-weight-400 {
font-weight: 400;
}
.subtitle {
font-size: 25px;
}
.force-bottom {
display: flex;
justify-content: flex-end;
flex-direction: column;
}
.invite-link-input {
width: 100%;
}
.no-border-top {
td {
border-top: none;
}
}
.force-text-normal {
color: #495057;
}
.terms {
overflow: scroll;
height: 55vh;
}
[contenteditable]:focus {
outline: 0px solid transparent;
}
.cookies-banner {
color: white;
background-color: $button-color-blue;
#cookies-agree-button {
margin: 0;
}
}
input:focus {
border-color: $primary !important;
}
.input-group button:focus {
box-shadow: none !important;
}
.list-group-item-action.active {
color: $primary;
}
.header .header-nav {
color: $text-muted !important;
&:hover {
padding-bottom: 21px;
border-bottom: 1px solid $primary;
}
&.active {
color: $primary !important;
padding-bottom: 21px;
border-bottom: 1px solid $primary;
}
}
table {
thead {
th[data-order]:hover {
cursor: pointer;
}
}
}
.cursor-pointer{
cursor: pointer;
}
#delete-confirm:disabled {
cursor: not-allowed;
}
.btn i {
transition: all .15s;
}
.nav-icon i {
width: 35px;
}

View File

@ -1,20 +0,0 @@
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
*
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*/
// Place all the styles related to the Errors controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

View File

@ -1,6 +1,6 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
@ -14,23 +14,5 @@
// You should have received a copy of the GNU Lesser General Public License along
// 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()
})
})
@import "font-awesome-sprockets";
@import "font-awesome";

View File

@ -1,178 +0,0 @@
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
*
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*/
// Place all the styles related to the Main controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
.responsive-header {
font-size: 3.3vw;
}
.landing-section {
position: relative;
height: 40%;
}
.lead {
font-size:18px;
}
.feature-stamp {
.stamp {
padding:1em 1.5em;
height:auto;
}
}
.or-line {
div {
width: 100%;
height: 16px;
border-bottom: 1px solid #d3d3d3;
text-align: center;
span {
font-size: 120%;
background-color: #ffffff;
padding: 0 10px;
color: #d3d3d3
}
}
}
.display-4 {
font-weight: normal;
}
.customBtn {
display: block;
text-align: center;
background: #cccccc;
color: #ffffff;
box-shadow: 0 2px 4px 0 rgba(0,0,0,.25);
-webkit-transition: background-color .218s,border-color .218s,box-shadow .218s;
transition: background-color .218s,border-color .218s,box-shadow .218s;
white-space: nowrap;
border-radius: 2px;
border: 1px solid transparent;
margin-bottom:20px;
&:hover {
cursor: pointer;
color: #ffffff;
-webkit-box-shadow: 0 0 3px 3px rgba(66,133,244,.3);
box-shadow: 0 0 3px 3px rgba(66,133,244,.3);
}
.customBtn-icon {
display: inline-block;
vertical-align: middle;
padding:13px 15px 13px 15px;
float:left;
background: #ffffff;
}
.customBtn-text {
font-size: 19px;
line-height: 48px;
font-weight: 600;
letter-spacing: .21px;
padding:0 25px;
}
.customBtn-image {
background: #ffffff;
background-size: 18px 18px;
padding:10px 10px 10px 10px;
}
}
.customBtn-google {
@extend .customBtn;
background: #4688f1;
.customBtn-image {
background: #ffffff image-url("google-logo.png") no-repeat left top;
background-size: 18px 18px;
padding:10px 10px 10px 10px;
}
}
.customBtn-twitter {
@extend .customBtn;
background: #1da1f2;
.customBtn-image {
background: #ffffff image-url("twitter-logo.png") no-repeat left top;
background-size: 18px 18px;
padding:10px 10px 10px 10px;
}
}
.customBtn-office365 {
@extend .customBtn;
background: #f65314;
.customBtn-image {
background: #ffffff image-url("office365-logo.jpeg") no-repeat left top;
background-size: 18px 18px;
padding:10px 10px 10px 10px;
}
}
.customBtn-microsoft_windows {
@extend .customBtn;
background: #00a1f1;
.customBtn-image {
background: #ffffff image-url("windows-logo.png") no-repeat left top;
background-size: 18px 18px;
padding:10px 10px 10px 10px;
}
}
.customBtn-ldap {
@extend .customBtn;
background: #d61515;
.customBtn-image {
background: #ffffff image-url("ldap-logo.png") no-repeat left top;
background-size: 18px 18px;
padding:10px 10px 10px 10px;
}
}
.customBtn-openid_connect {
@extend .customBtn;
background: #ef8e1f;
.customBtn-image {
background: #ffffff image-url("openid-logo.png") no-repeat left top;
background-size: 18px 18px;
padding:10px 10px 10px 10px;
}
}
.signin-button {
font-size: 16px;
}
.table-responsive tbody td:first-child > *:first-child {
max-height: 3em;
overflow: hidden;
max-width: 200px;
display: flex;
}

View File

@ -0,0 +1,15 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.

View File

@ -0,0 +1,133 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
.meeting-url-button-group {
padding-top: 5px;
width: 150px;
text-align: center;
}
.meeting-url-qrcode-group {
padding-top: 5px;
width: 128px;
text-align: center;
}
.previously-joined, .actives {
list-style-type: none;
margin:auto;
width: 200px;
padding: 0;
cursor: pointer;
}
.meetings {
}
.rooms {
.table-wrapper {
padding: 40px 50px 10px 50px;
#recordings {
thead {
th:after {
content: none; //removes the sort icon in table header
}
}
.dataTables_empty {
text-align: center;
}
.timeago {
margin-left: 5px;
font-size: 13px;
cursor: pointer;
color: #999;
}
}
}
}
.img-thumbnail{
padding: 4px;
background-color: white;
border: 1px solid #dddddd;
border-radius: 4px;
&.large{
display: none;
}
&:hover + &.large{
display: inline-block;
position: absolute;
z-index: 1;
}
}
.meeting-url-group {
position: relative;
}
.recording-update-trigger {
&.recording-unpublished {
color: red;
}
&.recording-unlisted {
color: #e3a91e;
}
&.recording-published {
color: green;
}
}
.fa-spinner {
width: 100%;
text-align: center;
}
.disabled-button {
opacity: 0.5;
}
.youtube-red {
color: red;
}
.cloud-blue {
color: cornflowerblue;
}
.green-check {
color: limegreen;
}
.top-buffer {
margin-top: 8px;
}
.tooltip-wrapper {
display: inline-block;
}
#youtube-footer{
font-size: 10px;
text-align: center;
margin-top: 10px;
}

View File

@ -0,0 +1,23 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
.login {
.center-panel {
.center-panel-size {
max-width: 400px
}
}
}

View File

@ -0,0 +1,187 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
//
// This program is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free Software
// Foundation; either version 3.0 of the License, or (at your option) any later
// version.
//
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
@import "bootstrap-sprockets";
@import "bootstrap";
@import "bootstrap-social";
html, body {
width: 100%;
height: 100%;
background: #ffffff;
}
.container-fluid {
height: 100%;
}
.background {
height: 350px;
width: 100%;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
}
body[data-controller=landing].app-background {
@extend .background;
}
#alerts {
position: absolute;
top: 10px;
left: 50%;
margin-left: -250px;
width: 500px;
z-index: 999;
}
.header {
padding: 20px 40px;
margin-bottom: 160px;
.logo {
max-width: 150px;
max-height: 50px;
}
}
.footer {
padding: 20px;
}
.center-block {
float: none;
}
.center-panel-wrapper {
height: 100%;
}
.center-panel {
height: 100%;
.center-panel-size {
max-width: 1200px
}
.center-panel-content-size {
height: 100%;
max-width: 1100px;
}
.panel {
position: relative;
overflow: hidden;
}
.input-spacing {
margin-top: 15px;
}
.panel-footer {
background-color: white;
}
.panel-body {
padding-bottom: 40px;
}
.title-wrapper {
margin-bottom: 30px;
}
.join-form-wrapper {
.center-block {
max-width: 400px;
}
.join-form {
width: 100%;
}
.btn {
width: initial;
}
}
.meeting-url-wrapper {
.meeting-url {
cursor: default;
}
}
.loading-wrapper {
text-align: center;
}
}
.popover {
max-width: none;
}
a.signin-link {
&:hover, &:focus {
cursor: pointer;
text-decoration: none;
}
}
.signin-link {
.signin-icon {
vertical-align: middle;
width: 35px;
height: 35px;
}
.signin-button {
background: white;
width: 250px;
border: thin solid #888;
border-radius: 2px;
white-space: nowrap;
padding: 5px;
margin-bottom: 14px;
}
.signin-icon-wrapper {
display: inline-block;
width: 40px;
}
.signin-text-wrapper {
display: inline-block;
width: 200px;
}
.signin-text {
font-family: 'Roboto', sans-serif;
vertical-align: middle;
font-size: 14px;
font-weight: bold;
color: #444;
}
}
.verticle-line {
// parent must be position relative to work
width: 1px;
background-color: lightgray;
height: 100%;
position: absolute;
left: 50%;
}
.invite-join-wrapper {
position: relative;
}
.help-link {
position: absolute;
top: 0;
right: 0;
padding-right: 3px;
}

View File

@ -1,146 +0,0 @@
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
*
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*/
// Place all the styles related to the Rooms controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
.start-button {
font-size: 26px !important;
}
.thumbnail {
height: 60px !important;
width: auto;
}
.join-form {
font-size: 20px !important;
}
.moderator-code-label {
margin-top: 150px !important;
}
.home-indicator {
font-size: 22px !important;
}
.btn-del-room {
width: 70% !important;
}
.edit_hover_class a{
visibility: hidden;
}
.edit_hover_class:hover a {
visibility: visible;
}
#room-settings-dropdown-label {
vertical-align: middle;
padding-top: 12px;
}
.room-block {
&:not(.current) {
.stamp {
opacity: 0.5;
}
}
}
#create-room-block {
border: 1px dashed lightgray;
&:hover {
cursor: pointer;
background-color: rgba(0, 0, 0, 0.04);
}
}
.allow-icon-click{
pointer-events: auto;
}
.cant-create-rooms-title{
align-items: center;
justify-content: center;
}
.avatar-xxxl{
width: 8rem;
height: 8rem;
line-height: 8rem;
max-width: 8rem;
margin-top: -6rem;
font-size: 5rem;
}
.bootstrap-select .dropdown-menu li.active small.text-muted{
color: #9aa0ac !important
}
.not-saved {
color: grey;
background: rgba(0, 40, 100, 0.12);
}
.dropdown-menu.show {
min-height: 0px !important;
}
.remove-shared {
text-decoration: line-through;
}
.enabled-setting {
background: lightgray;
pointer-events: none;
}
#recording-table .edit_hover_class {
word-break: break-word;
white-space: normal;
}
#room-owner-name {
line-height: 12px;
}
.create-room-button {
width: 49%;
}
#presentation-upload-label {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding-right: 75px;
}
#clear-room-search {
z-index: 9;
position: absolute;
right: 15px;
top: 8px;
&:hover {
cursor: pointer;
}
}

View File

@ -1,66 +0,0 @@
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
*
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*/
// Place all the styles related to the Sessions controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
.login {
.center-panel {
.center-panel-size {
max-width: 400px;
}
}
}
a.signin-link {
&:hover, &:focus {
cursor: pointer;
text-decoration: none;
}
}
.signin-link {
.signin-icon {
vertical-align: middle;
width: 35px;
height: 35px;
}
.signin-button {
background: white;
width: 250px;
border: thin solid #888;
border-radius: 2px;
white-space: nowrap;
padding: 5px;
margin-bottom: 14px;
}
.signin-icon-wrapper {
display: inline-block;
width: 40px;
}
.signin-text-wrapper {
display: inline-block;
width: 200px;
}
.signin-text {
vertical-align: middle;
font-size: 14px;
font-weight: bold;
color: #444;
}
}

View File

@ -1,32 +0,0 @@
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
*
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*/
// Place all the styles related to the Users controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
.user-role-tag{
color: white !important;
}
.shared-user {
line-height: 30px;
}
.bootstrap-select {
border: 1px solid rgba(0, 40, 100, 0.12);
}

View File

@ -1,504 +0,0 @@
/* cyrillic-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Source Sans Pro';
font-style: italic;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
unicode-range: U+0370-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
font-display: swap;
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

View File

@ -1,24 +0,0 @@
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
*
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*/
// Declare all variables here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
$background-color: #F5F7FB;
$error-background-color: #EFE6E6;
$button-color-blue: #467FCF;
$header-height: 65px;
$footer-height: 65px;

View File

@ -1,8 +1,6 @@
# frozen_string_literal: true
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software

View File

@ -1,8 +1,6 @@
# frozen_string_literal: true
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software

View File

@ -0,0 +1,27 @@
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
class MeetingUpdatesChannel < ApplicationCable::Channel
def subscribed
full_id = if params[:meeting_id].present?
"#{params[:admin_id]}-#{params[:meeting_id]}"
else
params[:admin_id]
end
stream_from "#{full_id}_meeting_updates_channel"
end
end

View File

@ -0,0 +1,26 @@
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
class RecordingUpdatesChannel < ApplicationCable::Channel
def subscribed
full_id = if params[:meeting_id].present?
"#{params[:admin_id]}-#{params[:meeting_id]}"
else
params[:admin_id]
end
stream_from "#{full_id}_recording_updates_channel"
end
end

View File

@ -1,8 +1,6 @@
# frozen_string_literal: true
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
@ -16,8 +14,8 @@
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
class WaitingChannel < ApplicationCable::Channel
class RefreshMeetingsChannel < ApplicationCable::Channel
def subscribed
stream_from "#{params[:roomuid]}_waiting_channel"
stream_from "refresh_meetings"
end
end

View File

@ -1,78 +0,0 @@
# frozen_string_literal: true
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
class AccountActivationsController < ApplicationController
include Emailer
include Authenticator
before_action :ensure_unauthenticated
before_action :find_user_by_token, only: :edit
before_action :find_user_by_digest, only: :resend
# GET /account_activations
def show
end
# GET /account_activations/edit
def edit
# If the user exists and is not verified and provided the correct token
if @user && !@user.activated?
# Verify user
@user.set_role(initial_user_role(@user.email)) if @user.role.nil?
@user.activate
# Redirect user to root with account pending flash if account is still pending
return redirect_to root_path,
flash: { success: I18n.t("registration.approval.signup") } if @user.has_role?(:pending)
# Redirect user to sign in path with success flash
redirect_to signin_path, flash: { success: "#{I18n.t('verify.activated')} #{I18n.t('verify.signin')}" }
else
redirect_to root_path, flash: { alert: I18n.t("verify.invalid") }
end
end
# POST /account_activations/resend
def resend
if @user.activated?
# User is already verified
flash[:alert] = I18n.t("verify.already_verified")
else
# Resend
send_activation_email(@user, @user.create_activation_token)
end
redirect_to root_path
end
private
def find_user_by_token
return redirect_to root_path, flash: { alert: I18n.t("verify.invalid") } unless params[:token].present?
@user = User.find_by!(activation_digest: User.hash_token(params[:token]), provider: @user_domain)
end
def find_user_by_digest
@user = User.find_by!(activation_digest: params[:digest], provider: @user_domain)
end
def ensure_unauthenticated
redirect_to current_user.main_room || root_path if current_user
end
end

View File

@ -1,379 +0,0 @@
# frozen_string_literal: true
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
class AdminsController < ApplicationController
include Pagy::Backend
include Themer
include Emailer
include Recorder
include Rolify
include Populator
manage_users = [:edit_user, :promote, :demote, :ban_user, :unban_user, :approve, :reset, :merge_user]
manage_deleted_users = [:undelete]
authorize_resource class: false
before_action :find_user, only: manage_users
before_action :find_deleted_user, only: manage_deleted_users
before_action :verify_admin_of_user, only: [manage_users, manage_deleted_users]
# GET /admins
def index
# Initializa the data manipulation variables
@search = params[:search] || ""
@order_column = params[:column] && params[:direction] != "none" ? params[:column] : "created_at"
@order_direction = params[:direction] && params[:direction] != "none" ? params[:direction] : "DESC"
@tab = params[:tab] || "active"
@role = params[:role] ? Role.find_by(name: params[:role], provider: @user_domain) : nil
users = if @tab == "invited"
invited_users_list
else
manage_users_list
end
@pagy, @users = pagy(users)
end
# GET /admins/site_settings
def site_settings
@tab = params[:tab] || "appearance"
end
# GET /admins/server_recordings
def server_recordings
@search = params[:search] || ""
if @search.present?
if @search.include? "@"
user_email = @search
else
room_uid = @search
end
else
@latest = true
end
@pagy, @recordings = pagy_array(recordings_to_show(user_email, room_uid))
end
# GET /admins/rooms
def server_rooms
@search = params[:search] || ""
@order_column = params[:column] && params[:direction] != "none" ? params[:column] : "status"
@order_direction = params[:direction] && params[:direction] != "none" ? params[:direction] : "DESC"
begin
meetings = all_running_meetings[:meetings]
rescue BigBlueButton::BigBlueButtonException
flash[:alert] = I18n.t("administrator.rooms.timeout", server: I18n.t("bigbluebutton"))
meetings = []
end
@order_column = "created_at" if meetings.empty?
@running_room_bbb_ids = meetings.pluck(:meetingID)
@participants_count = {}
meetings.each do |meet|
@participants_count[meet[:meetingID]] = meet[:participantCount]
end
@pagy, @rooms = pagy_array(server_rooms_list)
end
# GET /admins/room_configuration
def room_configuration
end
# MANAGE USERS
# GET /admins/edit/:user_uid
def edit_user
session[:prev_url] = request.referer if request.referer.present?
end
# POST /admins/ban/:user_uid
def ban_user
@user.set_role :denied
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.banned") }
end
# POST /admins/unban/:user_uid
def unban_user
@user.set_role :user
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.unbanned") }
end
# POST /admins/approve/:user_uid
def approve
@user.set_role :user
send_user_approved_email(@user)
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.approved") }
end
# POST /admins/approve/:user_uid
def undelete
# Undelete the user and all of his rooms
@user.undelete!
@user.rooms.deleted.each(&:undelete!)
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.restored") }
end
# POST /admins/invite
def invite
emails = params[:invite_user][:email].split(",")
emails.each do |email|
invitation = create_or_update_invite(email)
send_invitation_email(current_user.name, email, invitation)
end
redirect_back fallback_location: admins_path,
flash: { success: I18n.t("administrator.flash.invite", email: emails.join(", ")) }
end
# GET /admins/reset
def reset
send_password_reset_email(@user, @user.create_reset_digest)
if session[:prev_url].present?
redirect_path = session[:prev_url]
session.delete(:prev_url)
else
redirect_path = admins_path
end
redirect_to redirect_path, flash: { success: I18n.t("administrator.flash.reset_password") }
end
# POST /admins/merge/:user_uid
def merge_user
begin
# Get uid of user that will be merged into the other account
uid_to_merge = params[:merge]
logger.info "#{current_user.uid} is attempting to merge #{uid_to_merge} into #{@user.uid}"
# Check to make sure the 2 users are unique
raise "Can not merge the user into themself" if uid_to_merge == @user.uid
# Find user to merge
user_to_merge = User.find_by(uid: uid_to_merge)
# Move over user's rooms
user_to_merge.rooms.each do |room|
room.owner = @user
room.name = "(#{I18n.t('merged')}) #{room.name}"
room.save!
end
# Reload user to update merge rooms
user_to_merge.reload
# Delete merged user
user_to_merge.destroy(true)
rescue => e
logger.info "Failed to merge #{uid_to_merge} into #{@user.uid}: #{e}"
flash[:alert] = I18n.t("administrator.flash.merge_fail")
else
logger.info "#{current_user.uid} successfully merged #{uid_to_merge} into #{@user.uid}"
flash[:success] = I18n.t("administrator.flash.merge_success")
end
redirect_back fallback_location: admins_path
end
# GET /admins/merge_list
def merge_list
# Returns a list of users that can merged into another user
initial_list = User.without_role(:super_admin)
.where.not(uid: current_user.uid)
.merge_list_search(params[:search])
initial_list = initial_list.where(provider: @user_domain) if Rails.configuration.loadbalanced_configuration
# Respond with JSON object of users
respond_to do |format|
format.json { render body: initial_list.pluck_to_hash(:uid, :name, :email).to_json }
end
end
# SITE SETTINGS
# POST /admins/update_settings
def update_settings
tab = params[:tab] || "settings"
@settings.update_value(params[:setting], params[:value])
flash_message = I18n.t("administrator.flash.settings")
if params[:value] == "Default Recording Visibility"
flash_message += ". #{I18n.t('administrator.site_settings.recording_visibility.warning')}"
end
redirect_to admin_site_settings_path(tab: tab), flash: { success: flash_message }
end
# POST /admins/color
def coloring
@settings.update_value("Primary Color", params[:value])
@settings.update_value("Primary Color Lighten", color_lighten(params[:value]))
@settings.update_value("Primary Color Darken", color_darken(params[:value]))
redirect_to admin_site_settings_path(tab: "appearance"), flash: { success: I18n.t("administrator.flash.settings") }
end
# POST /admins/registration_method/:method
def registration_method
new_method = Rails.configuration.registration_methods[params[:value].to_sym]
# Only allow change to Join by Invitation if user has emails enabled
if !Rails.configuration.enable_email_verification && new_method == Rails.configuration.registration_methods[:invite]
redirect_to admin_site_settings_path(tab: "settings"),
flash: { alert: I18n.t("administrator.flash.invite_email_verification") }
else
@settings.update_value("Registration Method", new_method)
redirect_to admin_site_settings_path(tab: "settings"),
flash: { success: I18n.t("administrator.flash.registration_method_updated") }
end
end
# POST /admins/clear_auth
def clear_auth
User.include_deleted.where(provider: @user_domain).update_all(social_uid: nil)
redirect_to admin_site_settings_path(tab: "settings"), flash: { success: I18n.t("administrator.flash.settings") }
end
# POST /admins/clear_cache
def clear_cache
Rails.cache.delete("#{@user_domain}/getUser")
Rails.cache.delete("#{@user_domain}/getUserGreenlightCredentials")
redirect_to admin_site_settings_path(tab: "settings"), flash: { success: I18n.t("administrator.flash.settings") }
end
# POST /admins/log_level
def log_level
Rails.logger.level = params[:value].to_i
redirect_to admin_site_settings_path(tab: "administration"), flash: { success: I18n.t("administrator.flash.settings") }
end
# ROOM CONFIGURATION
# POST /admins/update_room_configuration
def update_room_configuration
@settings.update_value(params[:setting], params[:value])
flash_message = I18n.t("administrator.flash.room_configuration")
redirect_to admin_room_configuration_path, flash: { success: flash_message }
end
# ROLES
# GET /admins/roles
def roles
@roles = all_roles(params[:selected_role])
end
# POST /admins/role
# This method creates a new role scoped to the users provider
def new_role
new_role = create_role(params[:role][:name])
return redirect_to admin_roles_path, flash: { alert: I18n.t("administrator.roles.invalid_create") } if new_role.nil?
redirect_to admin_roles_path(selected_role: new_role.id)
end
# PATCH /admin/roles/order
# This updates the priority of a site's roles
# Note: A lower priority role will always get used before a higher priority one
def change_role_order
unless update_priority(params[:role])
redirect_to admin_roles_path, flash: { alert: I18n.t("administrator.roles.invalid_order") }
end
end
# POST /admin/role/:role_id
# This method updates the permissions assigned to a role
def update_role
role = Role.find(params[:role_id])
flash[:alert] = I18n.t("administrator.roles.invalid_update") unless update_permissions(role)
redirect_to admin_roles_path(selected_role: role.id)
end
# DELETE admins/role/:role_id
# This deletes a role
def delete_role
role = Role.find(params[:role_id])
# Make sure no users are assigned to the role and the role isn't a reserved role
# before deleting
if role.users.count.positive?
flash[:alert] = I18n.t("administrator.roles.role_has_users", user_count: role.users.count)
return redirect_to admin_roles_path(selected_role: role.id)
elsif Role::RESERVED_ROLE_NAMES.include?(role) || role.provider != @user_domain ||
role.priority <= current_user.role.priority
return redirect_to admin_roles_path(selected_role: role.id)
else
role.role_permissions.delete_all
role.delete
end
redirect_to admin_roles_path
end
private
def find_user
@user = User.find_by(uid: params[:user_uid])
end
def find_deleted_user
@user = User.deleted.find_by(uid: params[:user_uid])
end
# Verifies that admin is an administrator of the user in the action
def verify_admin_of_user
redirect_to admins_path,
flash: { alert: I18n.t("administrator.flash.unauthorized") } unless current_user.admin_of?(@user, "can_manage_users")
end
# Creates the invite if it doesn't exist, or updates the updated_at time if it does
def create_or_update_invite(email)
invite = Invitation.find_by(email: email, provider: @user_domain)
# Invite already exists
if invite.present?
# Updates updated_at to now
invite.touch
else
# Creates invite
invite = Invitation.create(email: email, provider: @user_domain)
end
invite
end
end

View File

@ -1,8 +1,6 @@
# frozen_string_literal: true
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
@ -16,301 +14,46 @@
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
require 'bigbluebutton_api'
require 'digest/sha1'
class ApplicationController < ActionController::Base
include BbbServer
include Errors
protect_from_forgery with: :exception
before_action :set_locale
MEETING_NAME_LIMIT = 200
USER_NAME_LIMIT = 100
before_action :block_unknown_hosts, :redirect_to_https, :set_user_domain, :set_user_settings, :maintenance_mode?,
: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)
@current_user = nil
session.clear
def set_locale
I18n.locale = http_accept_language.language_region_compatible_from(I18n.available_locales)
end
@current_user
def current_user
@current_user ||= User.find_by(id: session[:user_id])
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 meeting_name_limit
MEETING_NAME_LIMIT
end
helper_method :meeting_name_limit
def user_name_limit
USER_NAME_LIMIT
end
helper_method :user_name_limit
def bigbluebutton_endpoint_default?
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)
def qrcode_generation_enabled?
Rails.configuration.enable_qrcode_generation
end
helper_method :qrcode_generation_enabled?
end

View File

@ -0,0 +1,383 @@
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
class BbbController < ApplicationController
include BbbApi
before_action :authorize_recording_owner!, only: [:update_recordings, :delete_recordings]
before_action :load_and_authorize_room_owner!, only: [:end]
skip_before_action :verify_authenticity_token, only: :callback
# GET /:resource/:id/join
# GET /:resource/:room_id/:id/join
def join
if params[:name].blank?
return render_bbb_response(
messageKey: "missing_parameter",
message: "user name was not included",
status: :unprocessable_entity
)
elsif params[:name].size > user_name_limit
return render_bbb_response(
messageKey: "invalid_parameter",
message: "user name is too long",
status: :unprocessable_entity
)
elsif params[:id].size > meeting_name_limit
return render_bbb_response(
messageKey: "invalid_parameter",
message: "meeting name is too long",
status: :unprocessable_entity
)
else
if params[:room_id]
user = User.find_by encrypted_id: params[:room_id]
if !user
return render_bbb_response(
messageKey: "not_found",
message: "User Not Found",
status: :not_found
)
end
meeting_id = "#{params[:room_id]}-#{params[:id]}"
meeting_name = params[:id]
meeting_path = "#{URI.encode(params[:room_id])}/#{URI.encode(params[:id]).gsub('/','%2F')}"
else
user = User.find_by encrypted_id: params[:id]
meeting_id = params[:id]
meeting_path = URI.encode(meeting_id).gsub('/','%2F')
end
options = if user
{
wait_for_moderator: true,
meeting_recorded: true,
meeting_name: meeting_name,
room_owner: params[:room_id],
user_is_moderator: current_user == user
}
else
{
user_is_moderator: true
}
end
base_url = "#{request.base_url}#{relative_root}/#{params[:resource]}/#{meeting_path}"
options[:meeting_logout_url] = base_url
options[:hook_url] = "#{base_url}/callback"
options[:moderator_message] = t('moderator_default_message', url: "<a href=\"#{base_url}\" target=\"_blank\"><u>#{base_url}</u></a>")
bbb_res = bbb_join_url(
meeting_id,
params[:name],
options
)
# the user can join the meeting
if user
if bbb_res[:returncode] && current_user == user
JoinMeetingJob.perform_later(user, params[:id], base_url)
WaitingList.empty(options[:room_owner], options[:meeting_name])
# the user can't join because they are on mobile and HTML5 is not enabled.
elsif bbb_res[:messageKey] == 'unable_to_join'
NotifyUserCantJoinJob.perform_later(user.encrypted_id, params[:id], params[:name])
# user will be waiting for a moderator
else
NotifyUserWaitingJob.perform_later(user.encrypted_id, params[:id], params[:name])
end
end
render_bbb_response bbb_res, bbb_res[:response]
end
end
# POST /:resource/:room_id/:id/callback
# Endpoint for webhook calls from BigBlueButton
def callback
# respond with 200 anyway so BigBlueButton knows the hook call was ok
# but abort execution
head(:ok) && return unless validate_checksum
begin
event = params['event']
data = event.is_a?(String) ? JSON.parse(params['event']) : event
treat_callback_event(data)
rescue Exception => e
logger.error "Error parsing webhook data. Data: #{data}, exception: #{e.inspect}"
# respond with 200 anyway so BigBlueButton knows the hook call was ok
head(:ok) && return
end
end
# DELETE /rooms/:room_id/:id/end
def end
load_and_authorize_room_owner!
bbb_res = bbb_end_meeting "#{@user.encrypted_id}-#{params[:id]}"
if bbb_res[:returncode] || bbb_res[:status] == :not_found
EndMeetingJob.perform_later(@user.encrypted_id, params[:id])
bbb_res[:status] = :ok
end
render_bbb_response bbb_res
end
# GET /rooms/:room_id/recordings
# GET /rooms/:room_id/:id/recordings
def recordings
load_room!
# bbb_res = bbb_get_recordings "#{@user.encrypted_id}-#{params[:id]}"
options = { "meta_room-id": @user.encrypted_id }
if params[:id]
options["meta_meeting-name"] = params[:id]
end
bbb_res = bbb_get_recordings(options)
render_bbb_response bbb_res, bbb_res[:recordings]
end
# PATCH /rooms/:room_id/recordings/:record_id
# PATCH /rooms/:room_id/:id/recordings/:record_id
def update_recordings
published = params[:published] == 'true'
metadata = params.select{ |k, v| k.match(/^meta_/) }
bbb_res = bbb_update_recordings(params[:record_id], published, metadata)
if bbb_res[:returncode]
RecordingUpdatesJob.perform_later(@user.encrypted_id, params[:record_id])
end
render_bbb_response bbb_res
end
# DELETE /rooms/:room_id/recordings/:record_id
# DELETE /rooms/:room_id/:id/recordings/:record_id
def delete_recordings
recording = bbb_get_recordings({recordID: params[:record_id]})[:recordings].first
bbb_res = bbb_delete_recordings(params[:record_id])
if bbb_res[:returncode]
RecordingDeletesJob.perform_later(@user.encrypted_id, params[:record_id], recording[:metadata][:"meeting-name"])
end
render_bbb_response bbb_res
end
# POST /rooms/:room_id/recordings/:record_id
# POST /rooms/:room_id/:id/recordings/:record_id
def youtube_publish
# If we can't get the client, then they don't have a Youtube account.
begin
client = Yt::Account.new(access_token: current_user.token)
video = client.upload_video(get_webcams_url(params[:record_id]),
title: params[:video_title],
description: t('youtube_description', url: 'https://bigbluebutton.org/'),
privacy_status: params[:privacy_status])
rescue => e
errors = e.response_body['error']['errors']
# Many complications, start by getting them to refresh their token.
if errors.length > 1
redirect_url = user_login_url
else
error = errors[0]
if error['message'] == "Unauthorized"
redirect_url = 'https://m.youtube.com/create_channel'
else
# In this case, they don't have a youtube channel connected to their account, so prompt to create one.
redirect_url = user_login_url
end
end
end
render json: {:url => redirect_url}
end
# POST /rooms/:room_id/recordings/can_upload
def can_upload
# The recording is uploadable if it contains webcam data and they are logged in through Google.
if Rails.configuration.enable_youtube_uploading == false then
uploadable = 'uploading_disabled'
elsif current_user.provider != 'google'
uploadable = 'invalid_provider'
else
uploadable = (Faraday.head(get_webcams_url(params[:rec_id])).status == 200 && current_user.provider == 'google').to_s
end
render json: {:uploadable => uploadable}
end
def get_webcams_url(recording_id)
uri = URI.parse(Rails.configuration.bigbluebutton_endpoint)
uri.scheme + '://' + uri.host + '/presentation/' + recording_id + '/video/webcams.webm'
end
private
def load_room!
@user = User.find_by encrypted_id: params[:room_id]
if !@user
render head(:not_found) && return
end
end
def load_and_authorize_room_owner!
load_room!
if !current_user || current_user != @user
render head(:unauthorized) && return
end
end
def authorize_recording_owner!
load_and_authorize_room_owner!
recordings = bbb_get_recordings({recordID: params[:record_id]})[:recordings]
recordings.each do |recording|
if recording[:recordID] == params[:record_id]
return true
end
end
render head(:not_found) && return
end
def render_bbb_response(bbb_res, response={})
@messageKey = bbb_res[:messageKey]
@message = bbb_res[:message]
@status = bbb_res[:status]
@response = response
render status: @status
end
def read_body(request)
request.body.read.force_encoding("UTF-8")
end
def treat_callback_event(event)
# Check if the event is a BigBlueButton 2.0 event.
if event.has_key?('envelope')
eventName = (event.present? && event['envelope'].present?) ? event['envelope']['name'] : nil
else # The event came from BigBlueButton 1.1 (or earlier).
eventName = (event.present? && event['header'].present?) ? event['header']['name'] : nil
end
# a recording is ready
if eventName == "publish_ended"
if event['payload'] && event['payload']['metadata'] && event['payload']['meeting_id']
token = event['payload']['metadata'][META_TOKEN]
room_id = event['payload']['metadata']['room-id']
record_id = event['payload']['meeting_id']
duration_data = event['payload']['duration']
# the webhook event doesn't have all the data we need, so we need
# to send a getRecordings anyway
# TODO: if the webhooks included all data in the event we wouldn't need this
rec_info = bbb_get_recordings({recordID: record_id})
rec_info = rec_info[:recordings].first
RecordingCreatedJob.perform_later(token, room_id, parse_recording_for_view(rec_info))
rec_info[:duration] = duration_data.to_json
# send an email to the owner of this recording, if defined
if Rails.configuration.mail_notifications
owner = User.find_by(encrypted_id: room_id)
RecordingReadyEmailJob.perform_later(owner, parse_recording_for_view(rec_info)) if owner.present?
end
else
logger.error "Bad format for event #{event}, won't process"
end
elsif eventName == "meeting_created_message" || eventName == "MeetingCreatedEvtMsg"
# Fire an Actioncable event that updates _previously_joined for the client.
actioncable_event('create')
elsif eventName == "meeting_destroyed_event" || eventName == "MeetingEndedEvtMsg"
actioncable_event('destroy')
# Since the meeting is destroyed we have no way get the callback url to remove the meeting, so we must build it.
remove_url = build_callback_url(params[:id], params[:room_id])
# Remove webhook for the meeting.
webhook_remove(remove_url)
elsif eventName == "user_joined_message"
actioncable_event('join', {user_id: event['payload']['user']['extern_userid'], user: event['payload']['user']['name'], role: event['payload']['user']['role']})
elsif eventName == "UserJoinedMeetingEvtMsg"
actioncable_event('join', {user_id: event['core']['body']['intId'], user: event['core']['body']['name'], role: event['core']['body']['role']})
elsif eventName == "user_left_message"
actioncable_event('leave', {user_id: event['payload']['user']['extern_userid']})
elsif eventName == "UserLeftMeetingEvtMsg"
actioncable_event('leave', {user_id: event['core']['body']['intId']})
else
logger.info "Callback event will not be treated. Event name: #{eventName}"
end
render head(:ok) && return
end
def build_callback_url(id, room_id)
"#{request.base_url}#{relative_root}/rooms/#{room_id}/#{URI.encode(id)}/callback"
end
def actioncable_event(method, data = {})
data = {method: method, meeting: params[:id], room: params[:room_id]}.merge(data)
ActionCable.server.broadcast('refresh_meetings', data)
end
# Validates the checksum received in a callback call.
# If the checksum doesn't match, renders an ok and aborts execution.
def validate_checksum
secret = ENV['BIGBLUEBUTTON_SECRET']
checksum = params["checksum"]
return false unless checksum
# Message is only encoded if it comes from the bbb-webhooks node application.
# The post process script does not encode it's response body.
begin
# Decode and break the body into parts.
parts = URI.decode_www_form(read_body(request))
# Convert the data into the correct checksum format, replace ruby hash arrows.
converted_data = {parts[0][0]=>parts[0][1],parts[1][0]=>parts[1][1].to_i}.to_s.gsub!('=>', ':')
# Manually remove the space between the two elements.
converted_data[converted_data.rindex("timestamp") - 2] = ''
callback_url = uri_remove_param(request.original_url, "checksum")
checksum_str = "#{callback_url}#{converted_data}#{secret}"
rescue
# Data was not recieved encoded (sent from post process script).
data = read_body(request)
callback_url = uri_remove_param(request.original_url, "checksum")
checksum_str = "#{callback_url}#{data}#{secret}"
end
calculated_checksum = Digest::SHA1.hexdigest(checksum_str)
if calculated_checksum != checksum
logger.error "Checksum did not match. Calculated: #{calculated_checksum}, received: #{checksum}"
false
else
true
end
end
# Removes parameters from an URI
def uri_remove_param(uri, params = nil)
return uri unless params
params = Array(params)
uri_parsed = URI.parse(uri)
return uri unless uri_parsed.query
new_params = uri_parsed.query.gsub(/&amp;/, '&').split('&').reject { |q| params.include?(q.split('=').first) }
uri = uri.split('?').first
if new_params.count > 0
"#{uri}?#{new_params.join('&')}"
else
uri
end
end
end

View File

@ -1,130 +0,0 @@
# frozen_string_literal: true
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
module Authenticator
extend ActiveSupport::Concern
# Logs a user into GreenLight.
def login(user)
migrate_twitter_user(user)
session[:user_id] = user.id
user.update(last_login: Time.zone.now)
logger.info("Support: #{user.email} has successfully logged in.")
# If there are not terms, or the user has accepted them, check for email verification
if !Rails.configuration.terms || user.accepted_terms
check_email_verified(user)
else
redirect_to terms_path
end
end
# If email verification is disabled, or the user has verified, go to their room
def check_email_verified(user)
# Admin users should be redirected to the admin page
if user.has_role? :super_admin
redirect_to admins_path
elsif user.activated?
# Dont redirect to any of these urls
dont_redirect_to = [root_url, signin_url, ldap_signin_url, ldap_callback_url, signup_url, unauthorized_url,
internal_error_url, not_found_url]
unless ENV['OAUTH2_REDIRECT'].nil?
dont_redirect_to.push(File.join(ENV['OAUTH2_REDIRECT'], "auth", "openid_connect", "callback"))
end
url = if cookies[:return_to] && !dont_redirect_to.include?(cookies[:return_to])
cookies[:return_to]
elsif user.role.get_permission("can_create_rooms")
user.main_room
else
cant_create_rooms_path
end
# Delete the cookie if it exists
cookies.delete :return_to if cookies[:return_to]
redirect_to url
else
session[:user_id] = nil
user.create_activation_token
redirect_to account_activation_path(digest: user.activation_digest)
end
end
def ensure_unauthenticated_except_twitter
redirect_to current_user.main_room || root_path if current_user && params[:old_twitter_user_id].nil?
end
# Logs current user out of GreenLight.
def logout
session.delete(:user_id) if current_user
end
# Check if the user is using local accounts
def auth_changed_to_local?(user)
Rails.configuration.loadbalanced_configuration && user.social_uid.present? && allow_greenlight_accounts?
end
# Check if the user exists under the same email with no social uid and that social accounts are allowed
def auth_changed_to_social?(email)
Rails.configuration.loadbalanced_configuration &&
User.exists?(email: email, provider: @user_domain, social_uid: nil) &&
!allow_greenlight_accounts?
end
# Sets the initial user role based on the email mapping
def initial_user_role(email)
mapping = @settings.get_value("Email Mapping")
return "user" unless mapping.present?
mapping.split(",").each do |map|
email_role = map.split("=")
return email_role[1] if email.ends_with?(email_role[0])
end
"user" # default to user if role not found
end
private
# Migrates all of the twitter users rooms to the new account
def migrate_twitter_user(user)
if !session["old_twitter_user_id"].nil? && user.provider != "twitter"
old_user = User.find(session["old_twitter_user_id"])
old_user.rooms.each do |room|
room.owner = user
room.name = "Old #{room.name}" if room.id == old_user.main_room.id
room.save!
end
# Query for the old user again so the migrated rooms don't get deleted
old_user.reload
old_user.destroy!
session["old_twitter_user_id"] = nil
flash[:success] = I18n.t("registration.deprecated.merge_success")
end
end
end

View File

@ -1,146 +0,0 @@
# frozen_string_literal: true
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
#
# This program is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 3.0 of the License, or (at your option) any later
# version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
require 'bigbluebutton_api'
module BbbServer
extend ActiveSupport::Concern
include BbbApi
META_LISTED = "gl-listed"
# Checks if a room is running on the BigBlueButton server.
def room_running?(bbb_id)
bbb_server.is_meeting_running?(bbb_id)
end
# Returns a list of all running meetings
def all_running_meetings
bbb_server.get_meetings
end
def get_recordings(meeting_id)
bbb_server.get_recordings(meetingID: meeting_id)
end
def get_multiple_recordings(meeting_ids)
bbb_server.get_recordings(meetingID: meeting_ids)
end
# Returns a URL to join a user into a meeting.
def join_path(room, name, options = {}, uid = nil)
# Create the meeting, even if it's running
start_session(room, options)
# Determine the password to use when joining.
password = options[:user_is_moderator] ? room.moderator_pw : room.attendee_pw
# Generate the join URL.
join_opts = {}
join_opts[:userID] = uid if uid
join_opts[:join_via_html5] = true
join_opts[:avatarURL] = options[:avatarURL] if options[:avatarURL].present?
join_opts[:createTime] = room.last_session.to_datetime.strftime("%Q") if room.last_session
bbb_server.join_meeting_url(room.bbb_id, name, password, join_opts)
end
# Creates a meeting on the BigBlueButton server.
def start_session(room, options = {})
create_options = {
record: options[:record].to_s,
logoutURL: options[:meeting_logout_url] || '',
moderatorPW: room.moderator_pw,
attendeePW: room.attendee_pw,
moderatorOnlyMessage: options[:moderator_message],
"meta_#{META_LISTED}": options[:recording_default_visibility] || false,
"meta_bbb-origin-version": Greenlight::Application::VERSION,
"meta_bbb-origin": "Greenlight",
"meta_bbb-origin-server-name": options[:host]
}
create_options[:muteOnStart] = options[:mute_on_start] if options[:mute_on_start]
create_options[:guestPolicy] = "ASK_MODERATOR" if options[:require_moderator_approval]
# Send the create request.
begin
meeting = if room.presentation.attached?
modules = BigBlueButton::BigBlueButtonModules.new
url = rails_blob_url(room.presentation).gsub("&", "%26")
logger.info("Support: Room #{room.uid} starting using presentation: #{url}")
modules.add_presentation(:url, url)
bbb_server.create_meeting(room.name, room.bbb_id, create_options, modules)
else
bbb_server.create_meeting(room.name, room.bbb_id, create_options)
end
unless meeting[:messageKey] == 'duplicateWarning'
room.update_attributes(sessions: room.sessions + 1, last_session: DateTime.strptime(meeting[:createTime].to_s, "%Q"))
end
rescue BigBlueButton::BigBlueButtonException => e
puts "BigBlueButton failed on create: #{e.key}: #{e.message}"
raise e
end
end
# Gets the number of recordings for this room
def recording_count(bbb_id)
bbb_server.get_recordings(meetingID: bbb_id)[:recordings].length
end
# Update a recording from a room
def update_recording(record_id, meta)
meta[:recordID] = record_id
bbb_server.send_api_request("updateRecordings", meta)
end
# Update a recording from a room
def publish_recording(record_id)
bbb_server.publish_recordings(record_id, true)
end
# Update a recording from a room
def unpublish_recording(record_id)
bbb_server.publish_recordings(record_id, false)
end
# Protect a recording
def protect_recording(record_id, meta = {})
meta[:recordID] = record_id
meta[:protect] = true
bbb_server.send_api_request("updateRecordings", meta)
end
# Unprotect a recording
def unprotect_recording(record_id, meta = {})
meta[:recordID] = record_id
meta[:protect] = false
bbb_server.send_api_request("updateRecordings", meta)
end
# Deletes a recording from a room.
def delete_recording(record_id)
bbb_server.delete_recordings(record_id)
end
# Deletes all recordings associated with the room.
def delete_all_recordings(bbb_id)
record_ids = bbb_server.get_recordings(meetingID: bbb_id)[:recordings].pluck(:recordID)
bbb_server.delete_recordings(record_ids) unless record_ids.empty?
end
end

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