You are here

bbb_node.module in BigBlueButton 8

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.

File

modules/bbb_node/bbb_node.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.
 */

// API version
use BigBlueButton\Parameters\CreateMeetingParameters;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\node\NodeInterface;

/**
 * Implement HOOK_menu().
 */
function bbb_menu() {
  $items = [];

  // @ToDo: Move rest of hook_menu into the relevant configuration.
  $show_local_tasks = \Drupal::config('bbb_node.settings')
    ->get('local_tasks');
  $items['node/%node/meeting/attend'] = [
    'title' => 'Attend Meeting',
    'route_name' => 'bbb_node.meeting.attend',
    'type' => $show_local_tasks ? 'MENU_LOCAL_TASK' : 'MENU_CALLBACK',
    'weight' => 2,
  ];
  $items['node/%node/meeting/moderate'] = [
    'title' => 'Moderate Meeting',
    'route_name' => 'bbb_node.meeting.moderate',
    'type' => $show_local_tasks ? 'MENU_LOCAL_TASK' : 'MENU_CALLBACK',
    'weight' => 2,
  ];
  $items['node/%node/meeting/end-confirm'] = [
    'title' => 'Terminate Meeting',
    'type' => 'MENU_CALLBACK',
  ];
  $items['node/%node/redirect/attend'] = [
    'type' => 'MENU_CALLBACK',
  ];
  $items['node/%node/redirect/moderate'] = [
    'type' => 'MENU_CALLBACK',
  ];
  $items['node/%node/meeting/status'] = [
    'type' => 'MENU_CALLBACK',
  ];
  return $items;
}

/**
 * Implements hook_node_access().
 */
function bbb_node_node_access(NodeInterface $node, $op, AccountInterface $account) {

  /** @var \Drupal\bbb_node\Service\NodeMeeting $node_meeting */
  $node_meeting = \Drupal::service('bbb_node.meeting');
  if (in_array($op, [
    'create',
    'update',
  ]) && $node_meeting
    ->isTypeOf($node)) {

    // No bother to check for node access since this will be on edit/create pages.
    if ($account
      ->hasPermission('bbb_node record meetings') || $account
      ->hasPermission('administer big blue button')) {
      return AccessResult::allowed();
    }
    return AccessResult::forbidden();
  }
  return AccessResult::neutral();
}

/**
 * Implements hook_form_alter().
 */
function bbb_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Node type settings form
  switch ($form_id) {
    case 'node_type_edit_form':
    case 'node_type_add_form':
      $manager = \Drupal::entityTypeManager();
      $storage = $manager
        ->getStorage('bbb_node_type');

      /** @var \Drupal\node\NodeTypeInterface $node_type */
      $node_type = $form_state
        ->getFormObject()
        ->getEntity();

      /** @var \Drupal\bbb_node\Service\NodeMeeting $node_meeting */
      $node_meeting = \Drupal::service('bbb_node.meeting');
      if (!$node_meeting
        ->isTypeOf($node_type)) {
        return;
      }
      if ($form_id == 'node_type_edit_form') {
        $id = $node_type
          ->id();
        $label = $node_type
          ->label();
        $entity = $storage
          ->load($id);
        if (!$entity) {
          $entity = $storage
            ->create([
            'id' => $id,
            'label' => $label,
          ]);
        }
      }
      else {
        $entity = $storage
          ->create();
      }
      $bbb_form = $manager
        ->getFormObject($entity
        ->getEntityTypeId(), 'edit');
      $bbb_form
        ->setEntity($entity);
      $form['bbb'] = $bbb_form
        ->buildForm([], new FormState());
      if (!empty($form['bbb']['#submit'])) {
        $form['#submit'][] = array_pop($form['bbb']['#submit']);
      }
      if (!empty($form['actions']['submit']['#validate']) && !empty($form['bbb']['actions']['submit']['#validate'])) {
        $form['actions']['submit']['#validate'] = array_merge($form['actions']['submit']['#validate'], $form['bbb']['actions']['submit']['#validate']);
      }
      if (!empty($form['actions']['submit']['#submit']) && !empty($form['bbb']['actions']['submit']['#submit'])) {
        $form['actions']['submit']['#submit'] = array_merge($form['actions']['submit']['#submit'], $form['bbb']['actions']['submit']['#submit']);
        $form['actions']['submit']['#submit'][] = [
          $bbb_form,
          'saveEntity',
        ];
      }
      $form['bbb']['actions']['#access'] = FALSE;
      break;
    default:
      break;
  }
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function bbb_node_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  /** @var \Drupal\bbb_node\Service\NodeMeeting $node_meeting */
  $node_meeting = \Drupal::service('bbb_node.meeting');

  /** @var \Drupal\node\NodeInterface $node */
  $node = $form_state
    ->getFormObject()
    ->getEntity();
  if ($node_meeting
    ->isTypeOf($node)) {
    $meeting = $node_meeting
      ->get($node);

    /** @var \BigBlueButton\Parameters\CreateMeetingParameters $meeting_info */
    $meeting_info = !empty($meeting['info']) ? $meeting['info'] : $node_meeting
      ->init($node);
    $form['bbb'] = [];
    $record = [
      '#title' => t('Record meeting'),
      '#type' => 'select',
      '#default_value' => $meeting_info
        ->isRecorded(),
      '#options' => [
        0 => t('Do not record'),
        1 => t('Record'),
      ],
      '#weight' => -1,
    ];
    if (\Drupal::currentUser()
      ->hasPermission('administer big blue button')) {
      $form['bbb']['welcome'] = [
        '#title' => t('Welcome message'),
        '#type' => 'textfield',
        '#default_value' => $meeting_info
          ->getWelcomeMessage(),
        '#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']['dialNumber'] = [
        '#title' => t('Dial number'),
        '#type' => 'textfield',
        '#default_value' => $meeting_info
          ->getDialNumber(),
        '#maxlength' => 32,
        '#description' => t('The dial access number that participants can call in using regular phone.'),
      ];
      $form['bbb']['moderatorPW'] = [
        '#title' => t('Moderator password'),
        '#type' => 'textfield',
        '#default_value' => $meeting_info
          ->getModeratorPassword(),
        '#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']['attendeePW'] = [
        '#title' => t('Attendee password'),
        '#type' => 'textfield',
        '#default_value' => $meeting_info
          ->getAttendeePassword(),
        '#maxlength' => 32,
        '#description' => t('The password that will be required for attendees to join the meeting.'),
      ];
      $form['bbb']['logoutURL'] = [
        '#title' => t('Logout URL'),
        '#type' => 'textfield',
        '#default_value' => $meeting_info
          ->getLogoutUrl(),
        '#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>.'),
      ];
      $form['bbb']['record'] = $record;
    }
    if (\Drupal::currentUser()
      ->hasPermission('bbb_node 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']['record'] = $record;
    }

    // Add details block and show if any of form elements added.
    $form['bbb'] += [
      '#access' => !empty($form['bbb']),
      '#title' => t('Meeting settings'),
      '#type' => 'details',
      '#description' => t("The following settings may be changed until the meeting first starts."),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#tree' => TRUE,
      '#group' => 'advanced',
      '#weight' => 100,
    ];
  }
}

/**
 * Implements hook_ENTITY_TYPE_insert().
 * @see hook_ENTITY_TYPE_insert()
 */
function bbb_node_node_insert(EntityInterface $node) {

  /** @var \Drupal\bbb_node\Service\NodeMeeting $node_meeting */
  $node_meeting = \Drupal::service('bbb_node.meeting');

  /** @var \Drupal\node\NodeInterface $node */
  if ($node_meeting
    ->isTypeOf($node)) {
    $params = new CreateMeetingParameters($node
      ->uuid(), $node
      ->getTitle());
    \Drupal::moduleHandler()
      ->alter('bbb_node_create', $params, $node);
    $meeting = $node_meeting
      ->create($node, $params);
    $node_meeting
      ->store($node, $params);
  }
}

/**
 * Implements hook_ENTITY_TYPE_update().
 */
function bbb_node_node_update(EntityInterface $node) {

  /** @var \Drupal\bbb_node\Service\NodeMeeting $meeting */
  $meeting = \Drupal::service('bbb_node.meeting');

  /** @var \Drupal\node\NodeInterface $node */
  if ($meeting
    ->isTypeOf($node)) {
    $meeting
      ->update($node, $node->bbb['info'] ?: new CreateMeetingParameters($node
      ->uuid(), $node
      ->getTitle()));
  }
}

/**
 * Implements hook_ENTITY_TYPE_view().
 */
function bbb_node_node_view(array &$build, EntityInterface $node, EntityViewDisplayInterface $display, $view_mode) {

  /** @var \Drupal\bbb_node\Service\NodeMeeting $node_meeting */
  $node_meeting = \Drupal::service('bbb_node.meeting');

  /** @var \Drupal\node\NodeInterface $node */
  $node_type = $node
    ->getType();
  if (!$node_meeting
    ->isTypeOf($node)) {
    return;
  }
  $meeting = $node_meeting
    ->get($node);
  $BBBNodeTypeConfig = \Drupal::entityTypeManager()
    ->getStorage('bbb_node_type')
    ->load($node
    ->getType());

  // Show meeting status.
  if ($BBBNodeTypeConfig
    ->get('showStatus')) {
    $build['content']['bbb_meeting_status'] = [
      '#theme' => 'bbb_meeting_status',
      '#meeting' => $meeting,
      '#weight' => 10,
    ];
    $build['content']['bbb_meeting_record'] = [
      '#theme' => 'bbb_meeting_record',
      '#meeting' => $meeting,
      '#weight' => 11,
    ];
  }

  // Add links to content.
  $links = [];
  $show = $BBBNodeTypeConfig
    ->get('showLinks');
  if ($view_mode !== 'teaser' && $show) {
    $meeting = $node_meeting
      ->get($node);
    $current_user = \Drupal::currentUser();
    if ($current_user
      ->hasPermission('bbb_node attend meetings') || $current_user
      ->hasPermission('administer big blue button')) {
      $links['meeting_attend'] = [
        'title' => t('Attend meeting'),
        'href' => Url::fromRoute('bbb_node.meeting.attend', [
          'node' => $node
            ->id(),
        ])
          ->toString(),
      ];
    }
    if (\Drupal::currentUser()
      ->hasPermission('bbb_node moderate meetings') || $current_user
      ->hasPermission('administer big blue button') || $current_user
      ->id() == $node
      ->getAuthor()
      ->id() && $current_user
      ->hasPermission('bbb_node moderate own meetings')) {
      $links['meeting_moderate'] = [
        'title' => t('Moderate meeting'),
        'href' => Url::fromRoute('bbb_node.meeting.moderate', [
          'node' => $node
            ->id(),
        ])
          ->toString(),
      ];
    }
    if ($current_user
      ->hasPermission('bbb_node moderate meetings') || $current_user
      ->hasPermission('administer big blue button') || $current_user
      ->id() == $node
      ->getAuthor()
      ->id() && $current_user
      ->hasPermission('bbb_node moderate own meetings') && $meeting['info']
      ->isRunning()) {
      $links['meeting_end'] = [
        'title' => t('Terminate meeting'),
        'href' => Url::fromRoute('bbb_node.meeting.end_meeting_confirm_form', [
          'node' => $node
            ->id(),
        ])
          ->toString(),
      ];
    }
    if (!empty($links)) {
      $build['content']['links']['bbb'] = [
        '#links' => $links,
      ];
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_load().
 */
function bbb_node_node_load($entities) {

  /** @var \Drupal\bbb_node\Service\NodeMeeting $meeting */
  $meeting = \Drupal::service('bbb_node.meeting');

  /** @var NodeInterface[] $entities */
  foreach ($entities as $node) {
    if ($meeting
      ->isTypeOf($node)) {
      $node->bbb = $meeting
        ->get($node);
    }
  }
}

/**
 * Implements hook_ENTITY_TYPE_delete().
 */
function bbb_node_node_delete(EntityInterface $entity) {

  /** @var \Drupal\bbb_node\Service\NodeMeeting $meeting */
  $meeting = \Drupal::service('bbb_node.meeting');

  /** @var \Drupal\node\NodeInterface $entity */
  if ($meeting
    ->isTypeOf($entity)) {
    $meeting
      ->delete($entity);
  }
}

/**
 * Implements hook_theme().
 */
function bbb_node_theme($existing, $type, $theme, $path) {
  return [
    'bbb_meeting' => [
      'variables' => [
        'meeting' => NULL,
        'mode' => NULL,
        'params' => [],
      ],
    ],
    'bbb_meeting_status' => [
      'variables' => [
        'meeting' => NULL,
      ],
    ],
    'bbb_meeting_record' => [
      'variables' => [
        'meeting' => NULL,
      ],
    ],
    'bbb_meeting_join_moderator' => [
      'variables' => [
        'meeting' => NULL,
      ],
    ],
    'bbb_meeting_join_attendee' => [
      'variables' => [
        'meeting' => NULL,
      ],
    ],
    'bbb_block_meeting' => [
      'variables' => [
        'meeting' => NULL,
      ],
    ],
  ];
}

/* Themeables */

/**
 * Theme inline meeting
 */
function theme_bbb_meeting($meeting) {

  /** @var \Drupal\bbb\Service\Theme $theme */
  $theme = \Drupal::service('bbb.theme');
  return $theme
    ->meeting($meeting);
}

/**
 * Theme meeting status
 */
function theme_bbb_meeting_status($meeting) {

  /** @var \Drupal\bbb\Service\Theme $theme */
  $theme = \Drupal::service('bbb.theme');
  return $theme
    ->meetingStatus($meeting);
}

/**
 * Theme meeting recording
 */
function theme_bbb_meeting_record($meeting) {

  /** @var \Drupal\bbb\Service\Theme $theme */
  $theme = \Drupal::service('bbb.theme');
  return $theme
    ->meetingRecord($meeting);
}

/**
 * Theme meeting details block
 */
function theme_bbb_block_meeting($meeting) {

  /** @var \Drupal\bbb\Service\Theme $theme */
  $theme = \Drupal::service('bbb.theme');
  return $theme
    ->blockMeeting($meeting);
}