You are here

bbb.module in BigBlueButton 7

Same filename and directory in other branches
  1. 8 bbb.module
  2. 6 bbb.module

Big Blue Button - Enables universities and colleges to deliver a high-quality learning experience.

@author Stefan Auditor <stefan.auditor@erdfisch.de>

TODO:

  • Add 'start meeting' button
  • Rework waiting page
    • do a seperate page with a big throbber, that tells the status of the meeeting, so that it's clear you have to wait here.
    • Incorporate a Flash Player check, explain it and use the waiting time to adjust the audio/video settings

File

bbb.module
View source
<?php

/**
 * @file
 * Big Blue Button - Enables universities and colleges to deliver a high-quality
 * learning experience.
 *
 * @author
 * Stefan Auditor <stefan.auditor@erdfisch.de>
 *
 * TODO:
 * - Add 'start meeting' button
 * - Rework waiting page
 *   - do a seperate page with a big throbber, that tells the status of the
 *     meeeting, so that it's clear you have to wait here.
 *   - Incorporate a Flash Player check, explain it and use the waiting time
 *     to adjust the audio/video settings
 */

// API version
define('BIGBLUEBUTTON_API_VERSION', variable_get('bbb_api_version', NULL));
define('BIGBLUEBUTTON_GET_MEETING_INFO_URL', '/api/getMeetingInfo');
define('BIGBLUEBUTTON_CREATE_URL', '/api/create');
define('BIGBLUEBUTTON_JOIN_URL', '/api/join');
define('BIGBLUEBUTTON_IS_MEETING_RUNNING_URL', '/api/isMeetingRunning');

// Security Salt
// see /var/lib/tomcat6/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
define('BIGBLUEBUTTON_SECURITY_SALT', variable_get('bbb_security_salt', '76b71974198f26ecc5a594eddddf49d1'));
define('BIGBLUEBUTTON_BASE_URL', variable_get('bbb_base_url', 'http://example.com/bigbluebutton'));

// Define default settings
define('BIGBLUEBUTTON_DISPLAY_MODE', variable_get('bbb_display_mode', 'inline'));
define('BIGBLUEBUTTON_DISPLAY_HEIGHT', variable_get('bbb_display_height', '580px'));
define('BIGBLUEBUTTON_DISPLAY_WIDTH', variable_get('bbb_display_width', '100%'));
include_once drupal_get_path('module', 'bbb') . '/includes/api.bbb.inc';

/* Drupal Hooks */

/**
 * Implements hook_views_api().
 */
function bbb_views_api() {
  return array(
    'api' => 2,
    'path' => drupal_get_path('module', 'bbb') . '/includes',
  );
}

/**
 * Implements HOOK_permission().
 */
function bbb_permission() {
  return array(
    'administer big blue button' => array(
      'title' => t('Administer BigBlueButton'),
      'description' => t('Allow administration of BigBlueButton'),
    ),
    //TODO:

    //    'start meetings',
    //    'start own meetings',
    'moderate meetings' => array(
      'title' => t('Moderate meetings'),
      'description' => t('Allow moderation of meetings'),
    ),
    'moderate own meetings' => array(
      'title' => t('Moderate own meetings'),
      'description' => t('Allow moderation of own meetings'),
    ),
    'attend meetings' => array(
      'title' => t('Attend meetings'),
      'description' => t('Allow following meetings as an attendee'),
    ),
    'record meetings' => array(
      'title' => t('Record meetings'),
      'description' => t('Allow the user to record meetings.'),
    ),
  );
}

/**
 * Implement HOOK_menu().
 */
function bbb_menu() {
  $items = array();
  $show_local_tasks = variable_get('bbb_local_tasks', 1);
  $items['node/%node/meeting/attend'] = array(
    'title' => 'Attend Meeting',
    'page callback' => 'bbb_meeting_attend',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'bbb_access_attendee',
    'access arguments' => array(
      1,
    ),
    'type' => $show_local_tasks ? MENU_LOCAL_TASK : MENU_CALLBACK,
    'weight' => 2,
  );
  $items['node/%node/meeting/moderate'] = array(
    'title' => 'Moderate Meeting',
    'page callback' => 'bbb_meeting_moderate',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'bbb_access_moderator',
    'access arguments' => array(
      1,
    ),
    'type' => $show_local_tasks ? MENU_LOCAL_TASK : MENU_CALLBACK,
    'weight' => 2,
  );
  $items['node/%node/meeting/end-confirm'] = array(
    'title' => 'Terminate Meeting',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'bbb_end_confirm_form',
      1,
    ),
    'access callback' => 'bbb_access_moderator',
    'access arguments' => array(
      1,
    ),
    'type' => MENU_CALLBACK,
  );
  $items['node/%node/redirect/attend'] = array(
    'page callback' => 'bbb_redirect',
    'page arguments' => array(
      1,
      3,
    ),
    'access callback' => 'bbb_access_attendee',
    'access arguments' => array(
      1,
    ),
    'type' => MENU_CALLBACK,
  );
  $items['node/%node/redirect/moderate'] = array(
    'page callback' => 'bbb_redirect',
    'page arguments' => array(
      1,
      3,
    ),
    'access callback' => 'bbb_access_moderator',
    'access arguments' => array(
      1,
    ),
    'type' => MENU_CALLBACK,
  );
  $items['node/%node/meeting/status'] = array(
    'page callback' => 'bbb_status',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'bbb_access_attendee',
    'access arguments' => array(
      1,
    ),
    'type' => MENU_CALLBACK,
    'file' => 'includes/api.bbb.inc',
  );
  $items['admin/config/media/bigbluebutton'] = array(
    'title' => 'Big Blue Button meetings',
    'description' => 'Default server and meeting settings.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'bbb_settings',
    ),
    'access arguments' => array(
      'administer big blue button',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'includes/pages.bbb.inc',
  );
  return $items;
}

/**
 * Check attendance access permissions; Menu access callback
 */
function bbb_access_attendee($node) {
  if (!bbb_is_meeting_type($node->type)) {
    return FALSE;
  }

  // Check for node access and access to attend meetings
  if (node_access('view', $node) && (user_access('attend meetings') || user_access('administer big blue button'))) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Check moderation access permissions; Menu access callback
 */
function bbb_access_moderator($node) {
  global $user;
  if (!bbb_is_meeting_type($node->type)) {
    return FALSE;
  }

  // Check for node access and access start meetings
  if (node_access('view', $node) && (user_access('moderate meetings') || user_access('administer big blue button') || $user->uid == $node->uid && user_access('moderate own meetings'))) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Check that the user is allowed to set meetings to record
 */
function bbb_access_record($node) {
  if (!bbb_is_meeting_type($node->type)) {
    return FALSE;
  }

  // No bother to check for node access since this will be on edit/create pages.
  if (user_access('record meetings') || user_access('administer big blue button')) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Implements HOOK_block_info().
 */
function bbb_block_info() {
  $blocks['bbb_login_meeting'] = array(
    'info' => t('BBB Meeting details'),
  );
  return $blocks;
}

/**
 * Implements HOOK_block_view().
 */
function bbb_block_view($delta = '') {
  $block = '';
  switch ($delta) {
    case 'bbb_login_meeting':
      $block = array(
        'subject' => t('Meeting details'),
        'content' => bbb_block_meeting($delta),
      );
      break;
    default:
      break;
  }
  return $block;
}

/**
 * Meeting details block
 */
function bbb_block_meeting($delta) {
  $output = '';
  switch ($delta) {
    case 'bbb_login_meeting':
      $meeting = bbb_get_meeting(arg(1));
      if ($meeting) {
        $output = theme('bbb_block_meeting', array(
          'meeting' => $meeting,
        ));
      }
      break;
  }
  return $output;
}

/**
 * Implements HOOK_form_alter().
 */
function bbb_form_alter(&$form, &$form_state, $form_id) {

  // Node type settings form
  if ($form_id == 'node_type_form') {
    $form['bbb'] = array(
      '#title' => t('Big Blue Button settings'),
      '#type' => 'fieldset',
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#group' => 'additional_settings',
    );
    $form['bbb']['bbb_content_type'] = array(
      '#type' => 'checkbox',
      '#title' => t('Treat this node type as meeting'),
      '#default_value' => variable_get('bbb_content_type_' . $form['#node_type']->type, FALSE),
    );
    $form['bbb']['bbb_content_type_show_links'] = array(
      '#type' => 'checkbox',
      '#title' => t('Show links to attend, moderate or terminate a meeting beneath the node'),
      '#default_value' => variable_get('bbb_content_type_show_links_' . $form['#node_type']->type, FALSE),
    );
    $form['bbb']['bbb_content_type_show_status'] = array(
      '#type' => 'checkbox',
      '#title' => t('Display meeting status on node'),
      '#default_value' => variable_get('bbb_content_type_show_status_' . $form['#node_type']->type, FALSE),
    );
    $form['bbb']['bbb_content_type_moderator_required'] = array(
      '#type' => 'checkbox',
      '#title' => t('Require a moderator present to run the meeting.'),
      '#default_value' => variable_get('bbb_content_type_moderator_required_' . $form['#node_type']->type, FALSE),
    );
    $form['bbb']['bbb_content_type_welcome'] = array(
      '#title' => t('Welcome message'),
      '#type' => 'textfield',
      '#default_value' => variable_get('bbb_content_type_welcome_' . $form['#node_type']->type, ''),
      '#maxlength' => 255,
      '#description' => t('A welcome message that gets displayed on the chat window when the participant joins. You can include keywords (%%CONFNAME%%, %%DIALNUM%%, %%CONFNUM%%) which will be substituted automatically.'),
    );
    $form['bbb']['bbb_content_type_dialNumber'] = array(
      '#title' => t('Dial number'),
      '#type' => 'textfield',
      '#default_value' => variable_get('bbb_content_type_dialNumber_' . $form['#node_type']->type, ''),
      '#maxlength' => 32,
      '#description' => t('The dial access number that participants can call in using regular phone.'),
    );
    $form['bbb']['bbb_content_type_moderatorPW'] = array(
      '#title' => t('Moderator password'),
      '#type' => 'textfield',
      '#default_value' => variable_get('bbb_content_type_moderatorPW_' . $form['#node_type']->type, ''),
      '#maxlength' => 32,
      '#description' => t('The password that will be required for moderators to join the meeting or for certain administrative actions (i.e. ending a meeting).'),
    );
    $form['bbb']['bbb_content_type_attendeePW'] = array(
      '#title' => t('Attendee password'),
      '#type' => 'textfield',
      '#default_value' => variable_get('bbb_content_type_attendeePW_' . $form['#node_type']->type, ''),
      '#maxlength' => 32,
      '#description' => t('The password that will be required for attendees to join the meeting.'),
    );
    $form['bbb']['bbb_content_type_logoutURL'] = array(
      '#title' => t('Logout URL'),
      '#type' => 'textfield',
      '#default_value' => variable_get('bbb_content_type_logoutURL_' . $form['#node_type']->type, ''),
      '#maxlength' => 255,
      '#description' => t('The URL that the Big Blue Button client will go to after users click the OK button on the <em>You have been logged out message</em>.'),
    );
    if (user_access('record meetings')) {
      $form['bbb']['bbb_content_type_record'] = array(
        '#title' => t('Record new meetings of this type, by default.'),
        '#type' => 'checkbox',
        '#default_value' => variable_get('bbb_content_type_record_' . $form['#node_type']->type, ''),
        '#description' => 'Meetings that are recorded can be viewed at <strong>http://example.com/playback/slides/playback.html?meetingId=<meetingId></strong> (The meeting ID is about 54 characters long.)',
        '#weight' => -1,
      );
    }
  }

  // Node edit form
  if (array_key_exists('#node_edit_form', $form)) {
    $node_type = $form['type']['#value'];
    if (drupal_substr($form_id, drupal_strlen($form_id) - 10, 10) == '_node_form' && bbb_is_meeting_type($node_type)) {
      if (is_numeric($form['nid']['#value'])) {
        $meeting = bbb_get_meeting($form['nid']['#value']);
      }
      if (user_access('administer big blue button')) {
        $form['bbb'] = array(
          '#title' => t('Meeting settings'),
          '#type' => 'fieldset',
          '#description' => t("The following settings may be changed until the meeting first starts."),
          '#collapsible' => TRUE,
          '#collapsed' => TRUE,
          '#tree' => TRUE,
          '#group' => 'additional_settings',
        );
        $form['bbb']['welcome'] = array(
          '#title' => t('Welcome message'),
          '#type' => 'textfield',
          '#default_value' => !empty($meeting->welcome) ? $meeting->welcome : variable_get('bbb_content_type_welcome_' . $node_type, ''),
          '#maxlength' => 255,
          '#description' => t('A welcome message that gets displayed on the chat window when the participant joins. You can include keywords (%%CONFNAME%%, %%DIALNUM%%, %%CONFNUM%%) which will be substituted automatically.'),
          '#attributes' => !empty($meeting->meetingID) ? array(
            'disabled' => 'disabled',
          ) : array(),
        );
        $form['bbb']['dialNumber'] = array(
          '#title' => t('Dial number'),
          '#type' => 'textfield',
          '#default_value' => !empty($meeting->dialNumber) ? $meeting->dialNumber : variable_get('bbb_content_type_dialNumber_' . $node_type, ''),
          '#maxlength' => 32,
          '#description' => t('The dial access number that participants can call in using regular phone.'),
          '#attributes' => !empty($meeting->meetingID) ? array(
            'disabled' => 'disabled',
          ) : array(),
        );
        $form['bbb']['moderatorPW'] = array(
          '#title' => t('Moderator password'),
          '#type' => 'textfield',
          '#default_value' => !empty($meeting->moderatorPW) ? $meeting->moderatorPW : variable_get('bbb_content_type_moderatorPW_' . $node_type, ''),
          '#maxlength' => 32,
          '#description' => t('The password that will be required for moderators to join the meeting or for certain administrative actions (i.e. ending a meeting).'),
          '#attributes' => !empty($meeting->meetingID) ? array(
            'disabled' => 'disabled',
          ) : array(),
        );
        $form['bbb']['attendeePW'] = array(
          '#title' => t('Attendee password'),
          '#type' => 'textfield',
          '#default_value' => !empty($meeting->attendeePW) ? $meeting->attendeePW : variable_get('bbb_content_type_attendeePW_' . $node_type, ''),
          '#maxlength' => 32,
          '#description' => t('The password that will be required for attendees to join the meeting.'),
          '#attributes' => !empty($meeting->meetingID) ? array(
            'disabled' => 'disabled',
          ) : array(),
        );
        $form['bbb']['logoutURL'] = array(
          '#title' => t('Logout URL'),
          '#type' => 'textfield',
          '#default_value' => !empty($meeting->logoutURL) ? $meeting->logoutURL : variable_get('bbb_content_type_logoutURL_' . $node_type, ''),
          '#maxlength' => 255,
          '#description' => t('The URL that the Big Blue Button client will go to after users click the OK button on the <em>You have been logged out message</em>.'),
          '#attributes' => !empty($meeting->meetingID) ? array(
            'disabled' => 'disabled',
          ) : array(),
        );
        $form['bbb']['record'] = array(
          '#title' => t('Record meeting'),
          '#type' => 'select',
          '#default_value' => !empty($meeting->record) ? $meeting->record : variable_get('bbb_content_type_record_' . $node_type, ''),
          '#options' => array(
            0 => t('Do not record'),
            1 => t('Record'),
          ),
          '#attributes' => empty($meeting->initialized) ? array() : array(
            'disabled' => 'disabled',
          ),
          '#weight' => -1,
        );
      }
      if (user_access('record meetings') && empty($form['bbb']['record'])) {

        // this clause is for users who have the record meetings permission but no permissions to "administer BBB"
        $form['bbb'] = array(
          '#title' => t('Recording settings'),
          '#type' => 'fieldset',
          '#description' => t("The following settings may be changed until the meeting first starts."),
          '#collapsible' => TRUE,
          '#collapsed' => TRUE,
          '#tree' => TRUE,
        );
        $form['bbb']['record'] = array(
          '#title' => t('Record meeting'),
          '#type' => 'select',
          '#default_value' => !empty($meeting->record) ? $meeting->record : variable_get('bbb_content_type_record_' . $node_type, ''),
          '#options' => array(
            0 => t('Do not record'),
            1 => t('Record'),
          ),
          '#attributes' => empty($meeting->initialized) ? array() : array(
            'disabled' => 'disabled',
          ),
          '#weight' => -1,
        );
      }
    }
  }
}

/**
 * Implements HOOK_node_insert().
 */
function bbb_node_insert($node) {
  if (!bbb_is_meeting_type($node->type)) {
    return;
  }
  bbb_store_meeting($node);
}

/**
 * Implement HOOK_node_update().
 */
function bbb_node_update($node) {
  if (!bbb_is_meeting_type($node->type)) {
    return;
  }
  bbb_update_meeting($node, (array) $node->bbb);
}

/**
 * Implement HOOK_node_view().
 */
function bbb_node_view($node, $view_mode, $langcode) {
  if (!bbb_is_meeting_type($node->type)) {
    return;
  }
  if (!variable_get('bbb_content_type_show_status_' . $node->type, FALSE)) {
    return;
  }
  $meeting = bbb_get_meeting($node->nid);
  $node->content['bbb_meeting_status'] = array(
    '#markup' => theme('bbb_meeting_status', array(
      'meeting' => $meeting,
    )),
    '#weight' => 10,
  );
  $node->content['bbb_meeting_record'] = array(
    '#markup' => theme('bbb_meeting_record', array(
      'meeting' => $meeting,
    )),
    '#weight' => 11,
  );
}

/**
 * Implement HOOK_node_load().
 */
function bbb_node_load($nodes, $types) {
  foreach ($nodes as $node) {
    if (bbb_is_meeting_type($node->type)) {
      $node->bbb = bbb_get_meeting($node->nid);
    }
  }
}

/**
 * Implement HOOK_node_delete().
 */
function bbb_node_delete($node) {
  if (!bbb_is_meeting_type($node->type)) {
    return;
  }
  bbb_delete_meeting($node->nid);
}

/**
 * Implement HOOK_link().
 */
function bbb_link($type, $object, $teaser = FALSE) {
  $links = array();
  if ($type == 'node' && bbb_is_meeting_type($object->type)) {
    $show = variable_get('bbb_content_type_show_links_' . $object->type, FALSE);
    if (!$teaser && $show) {
      $meeting = bbb_get_meeting($object->nid);
      if (user_access('attend meetings') || user_access('administer big blue button')) {
        $links['bbb_meeting_attend'] = array(
          'title' => t('Attend meeting'),
          'href' => "node/{$object->nid}/meeting/attend",
        );
      }
      if (user_access('moderate meetings') || user_access('administer big blue button') || $user->uid == $node->uid && user_access('moderate own meetings')) {
        $links['bbb_meeting_moderate'] = array(
          'title' => t('Moderate meeting'),
          'href' => "node/{$object->nid}/meeting/moderate",
        );
      }
      if (user_access('moderate meetings') || user_access('administer big blue button') || $user->uid == $node->uid && user_access('moderate own meetings') && $meeting->running) {
        $links['bbb_meeting_end'] = array(
          'title' => t('Terminate meeting'),
          'href' => "node/{$object->nid}/meeting/end-confirm",
        );
      }
    }
  }
  return $links;
}

/**
 * Implement HOOK_theme().
 */
function bbb_theme($existing, $type, $theme, $path) {
  return array(
    'bbb_meeting' => array(
      'variables' => array(
        'meeting' => NULL,
        'mode' => NULL,
        'params' => array(),
      ),
    ),
    'bbb_meeting_status' => array(
      'variables' => array(
        'meeting' => NULL,
      ),
    ),
    'bbb_meeting_record' => array(
      'variables' => array(
        'meeting' => NULL,
      ),
    ),
    'bbb_meeting_join_moderator' => array(
      'variables' => array(
        'meeting' => NULL,
      ),
    ),
    'bbb_meeting_join_attendee' => array(
      'variables' => array(
        'meeting' => NULL,
      ),
    ),
    'bbb_block_meeting' => array(
      'variables' => array(
        'meeting' => NULL,
      ),
    ),
  );
}

/* Helper functions */

/**
 * Check if user is meeting owner
 */
function bbb_is_meeting_owner($nid, $account = NULL) {
  if (!$account) {
    global $user;
    $account = $user;
  }
  $sql = "SELECT COUNT(nid) FROM {bbb_meetings} bm\n           INNER JOIN {node} n ON bm.nid = n.nid\n           WHERE n.uid = :uid\n             AND nid = :nid";
  $owner = db_result(db_query($sql, array(
    ':uid' => $account->uid,
    ':nid' => $nid,
  )));
  return $owner ? TRUE : FALSE;
}

/**
 * Check if node type is meeting
 */
function bbb_is_meeting_type($type) {
  return variable_get('bbb_content_type_' . $type, FALSE);
}

/**
 * Return a meeting object
 */
function bbb_get_meeting($nid, $account = NULL, $cached = TRUE) {

  // Simple static cache
  static $meetings = array();
  if (!$account) {
    global $user;
    $account = $user;
  }
  if (!isset($meetings[$nid]) || !$cached) {
    $meeting = new stdClass();
    $result = db_query("SELECT * FROM {bbb_meetings} WHERE nid = :nid", array(
      ':nid' => $nid,
    ));
    $meeting = $result
      ->fetchObject();
    if ($meeting) {
      $meeting->running = bbb_api_isMeetingRunning(array(
        'meetingID' => $meeting->meetingID,
      ));

      // insert the recording option
      $recording = $meeting->record == 1 ? array(
        'record' => 'true',
      ) : array();
      $attend = array(
        'fullName' => format_username($account),
        'meetingID' => $meeting->meetingID,
        'password' => $meeting->attendeePW,
      );
      $moderate = array(
        'fullName' => format_username($account),
        'meetingID' => $meeting->meetingID,
        'password' => $meeting->moderatorPW,
      );
      $attend = array_merge($attend, $recording);
      $moderate = array_merge($moderate, $recording);
      $meeting->url = array(
        'attend' => bbb_api_join($attend),
        'moderate' => bbb_api_join($moderate),
      );

      // Allow alteration for e.g. access control
      // Just implement hook_bbb_meeting_alter(&$data) {} in your module
      drupal_alter('bbb_meeting', $meeting);

      // Static cache
      $meetings[$nid] = $meeting;
    }
  }
  return isset($meetings[$nid]) ? $meetings[$nid] : FALSE;
}

/**
 * Init meeting
 */
function bbb_init_meeting($node = NULL, $params = array()) {
  $welcome = variable_get('bbb_content_type_welcome_' . $node->type, '');
  $dialNumber = variable_get('bbb_content_type_dialNumber_' . $node->type, '');
  $moderatorPW = variable_get('bbb_content_type_moderatorPW_' . $node->type, '');
  $attendeePW = variable_get('bbb_content_type_attendeePW_' . $node->type, '');
  $logoutURL = variable_get('bbb_content_type_logoutURL_' . $node->type, '');
  $default_rec = variable_get('bbb_content_type_record_' . $node->type, '');
  if (isset($node->bbb)) {
    $record = (array) $node->bbb;
    $record = empty($record['record']) ? $default_rec : 1;
  }
  $settings = array(
    'nid' => $node->nid,
    'name' => $node->title,
    'meetingID' => bbb_create_meeting_id($node->nid),
    'attendeePW' => !empty($attendeePW) ? $attendeePW : user_password(),
    'moderatorPW' => !empty($moderatorPW) ? $moderatorPW : user_password(),
    'welcome' => !empty($welcome) ? $welcome : t('Welcome to @title', array(
      '@title' => $node->title,
    )),
    'dialNumber' => $dialNumber ? $dialNumber : NULL,
    // TODO: This is limited to 90000 voice bridges and may
    // collide sooner, as rand() may return x twice in a row
    'voiceBridge' => rand(10000, 99999),
    'logoutURL' => $logoutURL,
    //TODO: make absolute, as soon as supported
    'record' => $record,
    'initialized' => 0,
    'created' => time(),
  );
  $params = (array) $params;
  $params = array_merge($params, $settings);
  return $params;
}

/**
 * Update meeting
 */
function bbb_update_meeting($node, $params = array()) {
  $params = (array) $params;
  $params += bbb_init_meeting($node, $params);
  return drupal_write_record('bbb_meetings', $params, 'nid');
}

/**
 * Create meeting
 */
function bbb_create_meeting($node, $params = array()) {
  $params += bbb_init_meeting($node, $params);

  // we need to change the "record" param (0 or 1 in the database), to "true" if 1
  if ($params['record'] == 1) {
    $params['record'] = 'true';
  }
  else {
    unset($params['record']);
  }
  if ($data = bbb_api_create($params)) {
    return $data;
  }
  return FALSE;
}

/**
 * Store meeting
 */
function bbb_store_meeting($node, $params = array()) {
  $params += bbb_init_meeting($node, $params);
  return drupal_write_record('bbb_meetings', $params);
}

/**
 * Delete meeting
 */
function bbb_delete_meeting($nid) {
  return db_query("DELETE FROM {bbb_meetings} WHERE nid = :nid", array(
    ':nid' => $nid,
  ));
}

/* Menu Callbacks */

/**
 * Return meeting status; Menu callback
 */
function bbb_status($node) {
  $meeting = bbb_get_meeting($node->nid);
  drupal_json_output(array(
    'running' => $meeting->running,
  ));
  die;
}

/**
 * Redirect to big blue button instance; Menu callback
 *
 * @param OBJECT $node
 *   A Drupal node object
 */
function bbb_meeting_attend($node) {
  global $user;
  $url = array();
  $mode = 'attend';
  $meeting = bbb_get_meeting($node->nid);
  $params = array(
    'meetingID' => $meeting->meetingID,
    'password' => $meeting->attendeePW,
  );
  $status = bbb_api_getMeetingInfo($params);
  if ($status && property_exists($status, 'hasBeenForciblyEnded') && $status->hasBeenForciblyEnded == 'true') {
    drupal_set_message('The meeting has been terminated and is not available for attending.');
    drupal_goto('node/' . $node->nid);
  }
  drupal_set_title($node->title);
  if ($meeting->running) {
    if (BIGBLUEBUTTON_DISPLAY_MODE == 'blank') {
      bbb_redirect($node, $mode);
    }
  }
  else {
    if (variable_get('bbb_content_type_moderator_required_' . $node->type, FALSE)) {
      drupal_add_js('var bbb_check_status_url = ' . drupal_json_encode(url('node/' . $node->nid . '/meeting/status')), 'inline');
      drupal_add_js(drupal_get_path('module', 'bbb') . '/js/check_status.bbb.js');
      drupal_set_message(t('You signed up for this meeting. Please stay on this page, you will be redirected immediately after the meeting has started.'));
      return node_show($node, NULL);
    }
    else {
      if (empty($meeting->initialized)) {
        if ($data = bbb_create_meeting($node, (array) $params)) {

          // Update local data
          bbb_update_meeting($node, array_merge((array) $meeting, (array) $data));
        }
      }
      if (BIGBLUEBUTTON_DISPLAY_MODE == 'blank') {
        bbb_redirect($node, $mode);
      }
    }
  }
  $variables = array(
    'meeting' => $meeting,
    'mode' => $mode,
    'height' => BIGBLUEBUTTON_DISPLAY_HEIGHT,
    'width' => BIGBLUEBUTTON_DISPLAY_WIDTH,
  );
  return theme('bbb_meeting', $variables);
}

/**
 * Redirect to big blue button instance; Menu callback
 *
 * @param OBJECT $node
 *   A Drupal node object
 */
function bbb_meeting_moderate($node) {
  global $user;
  $url = array();
  $mode = 'moderate';
  $meeting = bbb_get_meeting($node->nid);
  $params = array(
    'meetingID' => $meeting->meetingID,
    'password' => $meeting->moderatorPW,
  );
  $status = bbb_api_getMeetingInfo($params);
  if ($status && property_exists($status, 'hasBeenForciblyEnded') && $status->hasBeenForciblyEnded == 'true') {
    drupal_set_message('The meeting has been terminated and is not available for reopening.');
    drupal_goto('node/' . $node->nid);
  }
  drupal_set_title($node->title);

  // Implicitly create meeting
  if (empty($meeting->initialized)) {
    if ($data = bbb_create_meeting($node, (array) $params)) {

      // Update local data
      bbb_update_meeting($node, array_merge((array) $meeting, (array) $data));
    }
  }
  if (BIGBLUEBUTTON_DISPLAY_MODE == 'blank') {
    bbb_redirect($node, $mode);
  }
  $variables = array(
    'meeting' => $meeting,
    'mode' => $mode,
    'height' => BIGBLUEBUTTON_DISPLAY_HEIGHT,
    'width' => BIGBLUEBUTTON_DISPLAY_WIDTH,
  );
  return theme('bbb_meeting', $variables);
}

/**
 * Terminate confirm form
 */
function bbb_end_confirm_form($form, &$form_state) {
  $node = node_load(arg(1));
  return confirm_form(array(
    'nid' => array(
      '#type' => 'value',
      '#value' => $node->nid,
    ),
  ), t('Are you sure you want to terminate the meeting !name?', array(
    '!name' => $node->title,
  )), 'node/' . $node->nid, t('This action cannot be undone, all attendes will be removed from the meeting.'), t('Terminate'), t('Cancel'));
}
function bbb_end_confirm_form_submit($form, &$form_state) {
  $node = node_load($form_state['values']['nid']);
  $request = bbb_meeting_end($node);
  if ($request === FALSE) {
    drupal_set_message(t('There was an error terminating the meeting.'), 'error');
  }
  else {
    drupal_set_message(t('The meeting has been terminated.'));
  }
  drupal_goto('node/' . $node->nid);
}

/**
 * Redirect to big blue button instance; Menu callback
 *
 * @param OBJECT $node
 *   A Drupal node object
 */
function bbb_meeting_end($node) {
  $meeting = bbb_get_meeting($node->nid);
  $params = array(
    'meetingID' => $meeting->meetingID,
    'password' => $meeting->moderatorPW,
  );
  $request = bbb_api_endMeeting($params);
  return $request;
}

/**
 * Redirect to meeting
 */
function bbb_redirect($node, $mode = 'attend') {
  $meeting = bbb_get_meeting($node->nid, NULL, FALSE);
  switch ($mode) {
    case 'attend':

      // Get redirect URL
      $url = parse_url($meeting->url[$mode]);
      $fullurl = $url['scheme'] . '://' . $url['host'] . (isset($url['port']) ? ':' . $url['port'] : '') . $url['path'] . '?' . $url['query'];
      header('Location: ' . $fullurl, TRUE, 301);
      break;
    case 'moderate':

      // Get redirect URL
      $url = parse_url($meeting->url[$mode]);
      $fullurl = $url['scheme'] . '://' . $url['host'] . (isset($url['port']) ? ':' . $url['port'] : '') . $url['path'] . '?' . $url['query'];
      header('Location: ' . $fullurl, TRUE, 301);
      break;
  }
}
function bbb_encode_int($string) {
  $ords = array();
  for ($i = 0; $i < drupal_strlen($string); $i++) {
    $ords[] = ord(drupal_substr($string, $i, 1));
  }
  return implode($ords);
}
function bbb_create_meeting_id($salt = NULL) {
  $private_key = drupal_get_private_key();
  return bbb_encode_int($private_key) . $salt;
}

/* Themeables */

/**
 * Theme inline meeting
 */
function theme_bbb_meeting($meeting, $variables = array()) {
  $url = url('node/' . $meeting['meeting']->nid . '/redirect/' . $meeting['mode'], array(
    'absolute' => TRUE,
  ));
  $output = '<iframe src="' . $url . '" style="height:' . $meeting['height'] . ';width:' . $meeting['width'] . ';border:0;"></iframe>';
  return $output;
}

/**
 * Theme meeting status
 */
function theme_bbb_meeting_status($meeting) {
  $meeting = $meeting['meeting'];
  if (isset($meeting->running) && $meeting->running) {
    $output = '<div class="bbb-status-is-running">' . t('Status: Meeting is in progress.') . '</div>';
  }
  else {
    $output = '<div class="bbb-status-is-not-running">' . t('Status: Meeting is not running') . '</div>';
  }
  return $output;
}

/**
 * Theme meeting recording
 */
function theme_bbb_meeting_record($meeting) {
  $meeting = $meeting['meeting'];
  $output = '';

  // Only if the meeting is set to record do we output as such.
  if (isset($meeting->record) && $meeting->record) {
    $output = '<div class="bbb-meeting-record">' . t('This meeting is set to be recorded.') . '</div>';
  }
  return $output;
}

/**
 * Theme meeting details block
 */
function theme_bbb_block_meeting($meeting) {
  $meeting = $meeting['meeting'];
  $output = '<div class="bbb-meeting-details">';
  if ($meeting->welcome) {
    $output .= '<div class="bbb-welcome">' . $meeting->welcome . '</div>';
  }
  $output .= theme('bbb_meeting_status', array(
    'meeting' => $meeting,
  ));

  // format links according to settings:
  $display_mode = variable_get('bbb_display_mode', BIGBLUEBUTTON_DISPLAY_MODE);

  // block links
  $attend = 'node/' . $meeting->nid . '/meeting/attend';
  $attend_options = array();
  $moderate = 'node/' . $meeting->nid . '/meeting/moderate';
  $moderate_options = array();
  if ($display_mode == 'blank') {

    // if it opens in a new window, make absolute paths and add some javascript
    global $base_url;
    $attend = $base_url . '/' . $attend;
    $attend_options = array(
      'attributes' => array(
        'onClick' => 'window.open(\'' . $attend . '\');return false',
        'html' => TRUE,
      ),
    );
    $moderate = $base_url . '/' . $moderate;
    $moderate_options = array(
      'attributes' => array(
        'onClick' => 'window.open(\'' . $moderate . '\');return false',
        'html' => TRUE,
      ),
    );
    $nolink = 'node/' . $meeting->nid;
  }
  if ($meeting->dialNumber) {
    $output .= '<div class="bbb-dial-number">' . t('Phone: @number', array(
      '@number' => $meeting->dialNumber,
    )) . '</div>';
  }
  if (user_access('attend meetings') || user_access('administer big blue button')) {
    $output .= '<div class="bbb-meeting-attend">' . l(t('Attend meeting'), $display_mode == 'blank' ? $nolink : $attend, $attend_options) . '</div>';
  }
  if (user_access('moderate meetings') || user_access('administer big blue button')) {
    $output .= '<div class="bbb-meeting-moderate">' . l(t('Moderate meeting'), $display_mode == 'blank' ? $nolink : $moderate, $moderate_options) . '</div>';
  }
  if (user_access('moderate meetings') || user_access('administer big blue button')) {
    $output .= '<div class="bbb-meeting-end">' . l(t('Terminate meeting'), 'node/' . $meeting->nid . '/meeting/end-confirm') . '</div>';
  }
  $output .= theme('bbb_meeting_record', array(
    'meeting' => $meeting,
  ));
  $output .= '</div>';
  return $output;
}

Functions

Namesort descending Description
bbb_access_attendee Check attendance access permissions; Menu access callback
bbb_access_moderator Check moderation access permissions; Menu access callback
bbb_access_record Check that the user is allowed to set meetings to record
bbb_block_info Implements HOOK_block_info().
bbb_block_meeting Meeting details block
bbb_block_view Implements HOOK_block_view().
bbb_create_meeting Create meeting
bbb_create_meeting_id
bbb_delete_meeting Delete meeting
bbb_encode_int
bbb_end_confirm_form Terminate confirm form
bbb_end_confirm_form_submit
bbb_form_alter Implements HOOK_form_alter().
bbb_get_meeting Return a meeting object
bbb_init_meeting Init meeting
bbb_is_meeting_owner Check if user is meeting owner
bbb_is_meeting_type Check if node type is meeting
bbb_link Implement HOOK_link().
bbb_meeting_attend Redirect to big blue button instance; Menu callback
bbb_meeting_end Redirect to big blue button instance; Menu callback
bbb_meeting_moderate Redirect to big blue button instance; Menu callback
bbb_menu Implement HOOK_menu().
bbb_node_delete Implement HOOK_node_delete().
bbb_node_insert Implements HOOK_node_insert().
bbb_node_load Implement HOOK_node_load().
bbb_node_update Implement HOOK_node_update().
bbb_node_view Implement HOOK_node_view().
bbb_permission Implements HOOK_permission().
bbb_redirect Redirect to meeting
bbb_status Return meeting status; Menu callback
bbb_store_meeting Store meeting
bbb_theme Implement HOOK_theme().
bbb_update_meeting Update meeting
bbb_views_api Implements hook_views_api().
theme_bbb_block_meeting Theme meeting details block
theme_bbb_meeting Theme inline meeting
theme_bbb_meeting_record Theme meeting recording
theme_bbb_meeting_status Theme meeting status

Constants