Welcome to the Django Big Blue Button web conferencing server. If you are here to attend a webinar then you should have been issued a URL to log into the meeting.
+{% endif %}
+{% endblock %}
diff --git a/bbb_django/bbb/tests.py b/bbb_django/bbb/tests.py
new file mode 100755
index 0000000..2247054
--- /dev/null
+++ b/bbb_django/bbb/tests.py
@@ -0,0 +1,23 @@
+"""
+This file demonstrates two different styles of tests (one doctest and one
+unittest). These will both pass when you run "manage.py test".
+
+Replace these with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+class SimpleTest(TestCase):
+ def test_basic_addition(self):
+ """
+ Tests that 1 + 1 always equals 2.
+ """
+ self.failUnlessEqual(1 + 1, 2)
+
+__test__ = {"doctest": """
+Another way to test that 1 + 1 is equal to 2.
+
+>>> 1 + 1 == 2
+True
+"""}
+
diff --git a/bbb_django/bbb/urls.py b/bbb_django/bbb/urls.py
new file mode 100644
index 0000000..e62c1b2
--- /dev/null
+++ b/bbb_django/bbb/urls.py
@@ -0,0 +1,26 @@
+from django.conf.urls.defaults import *
+from bbb.views.core import (home_page, create_meeting, begin_meeting, meetings,
+ join_meeting, delete_meeting)
+
+# Uncomment the next two lines to enable the admin:
+# from django.contrib import admin
+# admin.autodiscover()
+
+urlpatterns = patterns('',
+ url('^$', home_page, name='home'),
+ url(r'^login/$', 'django.contrib.auth.views.login', {
+ 'template_name': 'login.html',
+ }, name='login'),
+ url(r'^logoff/$', 'django.contrib.auth.views.logout', {'next_page': '/'},
+ name='logout'),
+ url('^create/$', create_meeting, name='create'),
+ url('^begin/$', begin_meeting, name='begin'),
+ url('^meetings/$', meetings, name='meetings'),
+ url('^meeting/(?P[a-zA-Z0-9 _-]+)/join$', join_meeting,
+ name='join'),
+ url('^meeting/(?P[a-zA-Z0-9 _-]+)/(?P.*)/delete$', delete_meeting,
+ name='delete'),
+ url('^help.html$', 'django.views.generic.simple.redirect_to', {
+ 'url': 'http://www.bigbluebutton.org/content/videos' ,
+ }, name='help'),
+)
diff --git a/bbb_django/bbb/views/__init__.py b/bbb_django/bbb/views/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/bbb_django/bbb/views/core.py b/bbb_django/bbb/views/core.py
new file mode 100644
index 0000000..b7b4588
--- /dev/null
+++ b/bbb_django/bbb/views/core.py
@@ -0,0 +1,114 @@
+from django.http import (Http404, HttpResponseRedirect, HttpResponseNotFound,
+ HttpResponse)
+from django.shortcuts import render_to_response
+from django.contrib.auth.decorators import login_required, permission_required
+from django.contrib.auth.views import login as django_login
+from django.template import RequestContext
+from django.core.urlresolvers import reverse
+from django.conf import settings
+from django.contrib import messages
+import hashlib
+
+from bbb.models import Meeting
+
+def home_page(request):
+ context = RequestContext(request, {
+ })
+ return render_to_response('home.html', context)
+
+
+@login_required
+def begin_meeting(request):
+
+ if request.method == "POST":
+ begin_url = "http://bigbluebutton.org"
+ return HttpResponseRedirect(begin_url)
+
+ context = RequestContext(request, {
+ })
+
+ return render_to_response('begin.html', context)
+
+@login_required
+def meetings(request):
+
+ #meetings = Meeting.objects.all()
+ meetings = Meeting.get_meetings()
+
+ context = RequestContext(request, {
+ 'meetings': meetings,
+ })
+
+ return render_to_response('meetings.html', context)
+
+def join_meeting(request, meeting_id):
+ form_class = Meeting.JoinForm
+
+ if request.method == "POST":
+ # Get post data from form
+ form = form_class(request.POST)
+ if form.is_valid():
+ data = form.cleaned_data
+ name = data.get('name')
+ password = data.get('password')
+
+ return HttpResponseRedirect(Meeting.join_url(meeting_id, name, password))
+ else:
+ form = form_class()
+
+ context = RequestContext(request, {
+ 'form': form,
+ 'meeting_name': meeting_id,
+ })
+
+ return render_to_response('join.html', context)
+
+@login_required
+def delete_meeting(request, meeting_id, password):
+ if request.method == "POST":
+ #meeting = Meeting.objects.filter(meeting_id=meeting_id)
+ #meeting.delete()
+ Meeting.end_meeting(meeting_id, password)
+
+ msg = 'Successfully ended meeting %s' % meeting_id
+ messages.success(request, msg)
+ return HttpResponseRedirect(reverse('meetings'))
+ else:
+ msg = 'Unable to end meeting %s' % meeting_id
+ messages.error(request, msg)
+ return HttpResponseRedirect(reverse('meetings'))
+
+@login_required
+def create_meeting(request):
+ form_class = Meeting.CreateForm
+
+ if request.method == "POST":
+ # Get post data from form
+ form = form_class(request.POST)
+ if form.is_valid():
+ data = form.cleaned_data
+ meeting = Meeting()
+ meeting.name = data.get('name')
+ #password = hashlib.sha1(data.get('password')).hexdigest()
+ meeting.attendee_password = data.get('attendee_password')
+ meeting.moderator_password = data.get('moderator_password')
+ meeting.meeting_id = data.get('meeting_id')
+ try:
+ url = meeting.start()
+ meeting.save()
+ msg = 'Successfully created meeting %s' % meeting.meeting_id
+ messages.success(request, msg)
+ return HttpResponseRedirect(reverse('meetings'))
+ except:
+ return HttpResponse("An error occureed whilst creating the " \
+ "meeting. The meeting has probably been "
+ "deleted recently but is still running.")
+
+ else:
+ form = form_class()
+
+ context = RequestContext(request, {
+ 'form': form,
+ })
+
+ return render_to_response('create.html', context)
diff --git a/bbb_django/manage.py b/bbb_django/manage.py
new file mode 100755
index 0000000..5e78ea9
--- /dev/null
+++ b/bbb_django/manage.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+from django.core.management import execute_manager
+try:
+ import settings # Assumed to be in the same directory.
+except ImportError:
+ import sys
+ sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+ sys.exit(1)
+
+if __name__ == "__main__":
+ execute_manager(settings)
diff --git a/bbb_django/settings.py b/bbb_django/settings.py
new file mode 100644
index 0000000..e4974bd
--- /dev/null
+++ b/bbb_django/settings.py
@@ -0,0 +1,112 @@
+# Django settings for bbb project.
+import os
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+ 'NAME': 'bbb_django', # Or path to database file if using sqlite3.
+ 'USER': 'bbb_django', # Not used with sqlite3.
+ 'PASSWORD': '', # Not used with sqlite3.
+ 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
+ 'PORT': '', # Set to empty string for default. Not used with sqlite3.
+ }
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'Europe/London'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-gb'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale
+USE_L10N = True
+
+# Absolute path to the directory that holds media.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash if there is a path component (optional in other cases).
+# Examples: "http://media.lawrence.com", "http://example.com/media/"
+MEDIA_URL = ''
+
+# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
+# trailing slash.
+# Examples: "http://foo.com/media/", "/media/".
+ADMIN_MEDIA_PREFIX = '/media/'
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = ''
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+ 'django.template.loaders.filesystem.Loader',
+ 'django.template.loaders.app_directories.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+ 'django.middleware.common.CommonMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+)
+
+AUTHENTICATION_BACKENDS = (
+ 'django_auth_ldap.backend.LDAPBackend',
+ 'django.contrib.auth.backends.ModelBackend',
+)
+
+LOGIN_URL = '/login'
+LOGIN_REDIRECT_URL = '/'
+
+SALT = ""
+BBB_API_URL = "http://yourdomain.com/bigbluebutton/api/"
+
+ROOT_URLCONF = 'bbb.urls'
+
+PROJECT_PATH = os.path.abspath(os.path.dirname(__file__))
+
+TEMPLATE_DIRS = (
+ # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+ os.path.join(PROJECT_PATH, 'templates'),
+ os.path.join(PROJECT_PATH, 'bbb', 'templates'),
+)
+
+INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'django.contrib.messages',
+ 'bbb',
+ 'gunicorn',
+ # Uncomment the next line to enable the admin:
+ # 'django.contrib.admin',
+ # Uncomment the next line to enable admin documentation:
+ # 'django.contrib.admindocs',
+)
diff --git a/bbb_django/urls.py b/bbb_django/urls.py
new file mode 100644
index 0000000..94c5ce4
--- /dev/null
+++ b/bbb_django/urls.py
@@ -0,0 +1,5 @@
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('',
+ (r'^/', include('bbb.urls')),
+)
diff --git a/readme.rst b/readme.rst
new file mode 100644
index 0000000..ea8d853
--- /dev/null
+++ b/readme.rst
@@ -0,0 +1,42 @@
+====================
+django-bigbluebutton
+====================
+:Info: A Django project for interacting with BigBlueButton
+:Author: Steve Challis (http://schallis.com)
+:Requirements: BigBlueButton >= 0.71, Django >= 1.0
+
+This is a simple Django project and application that interacts with the
+`BigBlueButton `_ API to allow you to create and
+interact with web conference meetings It currently supports:
+
+* Password protected administration
+* Meeting creation/ending
+* Meeting joining
+* List all currently running meetings
+
+
+Setup
+=====
+You'll first need to edit settings.py in the bbb_django project or your own
+project. The following custom variables must be added/set:
+
+* SALT = "[your_salt]"
+* BBB_API_URL = "http://yourdomain.com/bigbluebutton/api/"
+
+The `bbb` application is where all the controllers and views are contained so
+you should be able to drop this into any Django project.
+
+You can quickly test the project with the Django default webserver but you'll
+probably want to have it running permenantly. `Gunicorn
+`_ has already been added in as a dependancy so
+you should be able to use `gunicorn_django` once gunicorn is installed.
+
+It is assumed you are using FreeSWITCH for the voice calling but it is easy
+enough to change the extension to that required by Asterisk.
+
+Screenshots
+===========
+.. image::
+https://github.com/schallis/django-bigbluebutton/raw/master/screenshots/screenshot-create.png
+
+.. image:: https://github.com/schallis/django-bigbluebutton/raw/master/screenshots/screenshot-meetings.png
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..514e259
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+django >= 1.0
diff --git a/screenshots/screenshot-create.png b/screenshots/screenshot-create.png
new file mode 100644
index 0000000..fbbcb5d
Binary files /dev/null and b/screenshots/screenshot-create.png differ
diff --git a/screenshots/screenshot-home.png b/screenshots/screenshot-home.png
new file mode 100644
index 0000000..7c50d12
Binary files /dev/null and b/screenshots/screenshot-home.png differ
diff --git a/screenshots/screenshot-join.png b/screenshots/screenshot-join.png
new file mode 100644
index 0000000..c6b35a8
Binary files /dev/null and b/screenshots/screenshot-join.png differ
diff --git a/screenshots/screenshot-meetings-detail.png b/screenshots/screenshot-meetings-detail.png
new file mode 100644
index 0000000..71b0aea
Binary files /dev/null and b/screenshots/screenshot-meetings-detail.png differ
diff --git a/screenshots/screenshot-meetings.png b/screenshots/screenshot-meetings.png
new file mode 100644
index 0000000..e224b42
Binary files /dev/null and b/screenshots/screenshot-meetings.png differ