zoomapi.module in Zoom API 7
Same filename and directory in other branches
Main file for the Zoom API module.
File
zoomapi.moduleView source
<?php
/**
* @file
* Main file for the Zoom API module.
*/
define('ZOOMAPI_USER_TYPE_BASIC', 1);
define('ZOOMAPI_USER_TYPE_PRO', 2);
define('ZOOMAPI_USER_TYPE_CORP', 3);
define('ZOOMAPI_USER_TYPE_DEFAULT', ZOOMAPI_USER_TYPE_BASIC);
define('ZOOMAPI_MEETING_TYPE_INSTANT', 1);
define('ZOOMAPI_MEETING_TYPE_NORMAL', 2);
define('ZOOMAPI_MEETING_TYPE_RECURRING_NO_FIXED_TIME', 3);
define('ZOOMAPI_MEETING_TYPE_RECURRING_FIXED_TIME', 8);
define('ZOOMAPI_MEETING_TYPE_DEFAULT', ZOOMAPI_MEETING_TYPE_NORMAL);
/**
* Implements hook_permission().
*/
function zoomapi_permission() {
return [
'administer zoomapi' => [
'title' => t('Administer Zoom API'),
'description' => t('Administer the Zoom API settings.'),
'restrict access' => TRUE,
],
];
}
/**
* Implements hook_menu().
*/
function zoomapi_menu() {
// Webhook.
$items['zoomapi/webhook'] = [
'title' => 'Zoom API Webhook',
'description' => 'See https://zoom.github.io/api/#webhooks',
'page callback' => 'zoomapi_webhooks_callback',
'access callback' => 'zoomapi_webhooks_access',
'file' => 'zoomapi.pages.inc',
'type' => MENU_CALLBACK,
];
// Admin settings.
$items['admin/config/services/zoomapi'] = [
'title' => 'Zoom API Settings',
'description' => 'Configuration for Zoom API',
'page callback' => 'drupal_get_form',
'page arguments' => [
'zoomapi_settings_form',
],
'access arguments' => [
'administer zoomapi',
],
'file' => 'zoomapi.admin.inc',
'type' => MENU_NORMAL_ITEM,
];
return $items;
}
/**
* Access callback: Zoom API Webhooks.
*/
function zoomapi_webhooks_access() {
// Only allow POST requests.
$http_verb = strtoupper($_SERVER['REQUEST_METHOD']);
if ($http_verb != 'POST') {
return FALSE;
}
// Webhooks not enabled.
if (!variable_get('zoomapi_webhooks_enabled', FALSE)) {
return FALSE;
}
// Validate basic auth.
if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW']) && $_SERVER['PHP_AUTH_USER'] == variable_get('zoomapi_webhooks_username', FALSE) && $_SERVER['PHP_AUTH_PW'] == variable_get('zoomapi_webhooks_password', FALSE)) {
return TRUE;
}
return FALSE;
}
/**
* Implements hook_cron().
*/
function zoomapi_cron() {
db_delete('zoomapi_meeting_tracker')
->condition('expires', time(), '<=')
->execute();
}
/**
* Get account zoom user ID.
*
* @param object $account
* The Drupal user account.
*
* @return string
* The zoom user ID.
*/
function zoomapi_get_zoom_user_id($account) {
return !empty($account->data['zoomapi_user_id']) ? $account->data['zoomapi_user_id'] : '';
}
/**
* Get account zoom user email.
*
* @param object $account
* The Drupal user account.
*
* @return string
* The zoom user email.
*/
function zoomapi_get_zoom_user_email($account) {
return !empty($account->data['zoomapi_user_email']) ? $account->data['zoomapi_user_email'] : '';
}
/**
* Does Zoom user exist for account.
*
* @param object $account
* The Drupal user account to check if matching Zoom API account exists.
*
* @return bool
* TRUE if Zoom user account exists for $account, FALSE otherwise.
*/
function zoomapi_user_checkemail($account) {
if (empty($account->data['zoomapi_user_email'])) {
return FALSE;
}
$zoomapi_user = new ZoomAPIUser();
return $zoomapi_user
->checkemail($account->date['zoomapi_user_email']);
}
/**
* Find Zoom user by email.
*
* The Zoom API does offer an endpoint to search by email, however login_type
* must be known. This function attempts to work around that by getting a list
* of all users and searching for the email address.
*
* @param string $email
* The email address to search for.
* @param int $page_size
* The total number of records to retrieve.
*
* @return array
* The Zoom user information.
*/
function zoomapi_user_find_by_email($email, $page_size = 100) {
$zoomapi_user = new ZoomAPIUser();
$list['page_number'] = 0;
do {
$list = $zoomapi_user
->list($page_size, $list['page_number'] + 1);
if (empty($list['users'])) {
break;
}
foreach ($list['users'] as $delta => $zoom_user) {
if ($zoom_user['email'] == $email) {
return $zoom_user;
}
}
} while ($list['page_number'] < $list['page_count']);
return [];
}
/**
* Connect account with zoom account.
*
* There may be cases where a user account exists but the zoomapi_user_id is
* not set on the Drupal user account data property. This function attempts to
* check if a zoom user account exists that would link to this user account and
* update the data properties.
*
* @param object $account
* The Drupal user account to check if matching Zoom API account exists.
*
* @todo
*/
function zoomapi_user_connect_accounts($account) {
if (!empty($account->data['zoomapi_user_id'])) {
return TRUE;
}
}
/**
* Get Zoom User.
*
* @param object $account
* The Drupal user account to check if matching Zoom API account exists.
*
* @return array
* An array of the Zoom user account information.
*/
function zoomapi_user_get($account) {
if (empty($account->data['zoomapi_user_id'])) {
return [];
}
$zoomapi_user = new ZoomAPIUser();
$zoom_user = $zoomapi_user
->get($account->data['zoomapi_user_id']);
// If we're unable to retrieve the user using the zoomapi_user_id on the
// account then it's possible the user account was deleted directly on Zoom.
// First search by email and update the data properties or remove the data
// properties from the user.
if (empty($zoom_user)) {
// Zoom account found by email so update id.
if ($zoom_user = zoomapi_user_find_by_email($account->data['zoomapi_user_email'])) {
$account->data['zoomapi_user_id'] = $zoom_user['id'];
user_save($account);
}
else {
zoomapi_user_clean_account($account);
}
}
return $zoom_user;
}
/**
* Create Zoom User.
*
* Create a user account on Zoom for a given Drupal user account if the zoom
* user ID is not already attached to the Drupal user account.
*
* @param object $account
* The Drupal user account to generate the Zoom account for.
* @param array $options
* Optional array of Zoom user configuration options.
*
* @return array
* An array of the Zoom user account information.
*/
function zoomapi_user_create($account, array $options = []) {
$zoomapi_user = new ZoomAPIUser();
$email = !empty($options['email']) ? $options['email'] : $account->mail;
$type = !empty($options['type']) ? $options['type'] : variable_get('zoomapi_user_type_default', ZOOMAPI_USER_TYPE_DEFAULT);
// The 'track_id' on the zoom user will be used to track the Drupal user ID.
$options['track_id'] = $account->uid;
if (empty($account->data['zoomapi_user_id']) && ($zoom_user = $zoomapi_user
->create($email, $type, $options))) {
$account->data['zoomapi_user_id'] = $zoom_user['id'];
$account->data['zoomapi_user_email'] = $zoom_user['email'];
user_save($account);
return $zoom_user;
}
return [];
}
/**
* Create Custom Zoom User.
*
* Zoom custom accounts are accounts without a password. They are unable to log
* into the Zoom app or website. These are typically used to generate accounts
* used to host meetings. Users that use the meeting start url are automatically
* logged in as the host (custom) account.
*
* Given that emails must still be unique, a fake email address is generated
* based on the account.
*
* @param object $account
* The Drupal user account to generate the Zoom account for.
* @param array $options
* Optional array of Zoom user configuration options.
*
* @return array
* An array of the Zoom user account information.
*/
function zoomapi_user_custcreate($account, array $options = []) {
$zoomapi_user = new ZoomAPIUser();
$email = !empty($options['email']) ? $options['email'] : zoomapi_user_custcreate_email_generate($account);
$type = !empty($options['type']) ? $options['type'] : variable_get('zoomapi_user_type_default', ZOOMAPI_USER_TYPE_DEFAULT);
// The 'track_id' on the zoom user will be used to track the Drupal user ID.
$options['track_id'] = $account->uid;
if (empty($account->data['zoomapi_user_id']) && ($zoom_user = $zoomapi_user
->custcreate($email, $type, $options))) {
$account->data['zoomapi_user_id'] = $zoom_user['id'];
$account->data['zoomapi_user_email'] = $zoom_user['email'];
user_save($account);
return $zoom_user;
}
return [];
}
/**
* Update User Info.
*
* @param object $account
* The Drupal user account to generate the Zoom account for.
* @param array $options
* Optional array of Zoom user configuration options.
*
* @return array
* An array of the Zoom user account information.
*/
function zoomapi_user_update_info($account, array $options) {
// In some cases an outdated account is passed without the zoom_user_id. If it
// doesn't exist then try reloading the account.
if (empty($account->data['zoomapi_user_id'])) {
$account = user_load($account->uid);
}
if (empty($account->data['zoomapi_user_id'])) {
watchdog(__FUNCTION__, 'Unable to update account (uid: !uid) due to missing zoom_user_id', [
'!uid' => $account->uid,
], WATCHDOG_ERROR);
return [];
}
$zoomapi_user = new ZoomAPIUser();
$zoom_user = $zoomapi_user
->update($account->data['zoomapi_user_id'], $options);
return $zoom_user;
}
/**
* Upload picture to Zoom account.
*
* Note: The Zoom API docs mention the image file must be jpg/jpeg.
*
* @param object $account
* The Drupal user account to check if matching Zoom API account exists.
* @param object $image_file
* The Drupal file object.
*
* @todo add watchdog.
* @todo figure out 'multipart/form-data' error.
*/
function zoomapi_user_uploadpicture($account, $image_file = NULL) {
if (TRUE) {
// Figure out @todo error.
return;
}
if (empty($account->data['zoomapi_user_id'])) {
return;
}
if (!$image_file) {
if (empty($account->picture)) {
return;
}
$image_file = $account->picture;
}
if ($image_file->filemime != 'image/jpeg') {
return;
}
$image_url = file_create_url($image_file->uri);
$zoomapi_user = new ZoomAPIUser();
$zoomapi_user
->uploadpicture($account->data['zoomapi_user_id'], $image_url);
}
/**
* Generate custom email address for custcreate.
*
* @param object $account
* The drupal user account to base the custom email from.
*
* @return string
* The generated email address.
*
* @todo document alter hook.
*/
function zoomapi_user_custcreate_email_generate($account) {
global $base_url;
$parts = parse_url($base_url);
$email = "user{$account->uid}@{$parts['host']}";
$context = [
'account' => $account,
];
drupal_alter('zoomapi_user_custcreate_email', $email, $context);
return $email;
}
/**
* Join Meeting.
*
* @param array $meeting
* The meeting array returned from the create meeting functions.
*/
function zoomapi_meeting_join(array $meeting) {
global $user;
// Ensure we have the latest account information in case the zoomapi_user_id
// was recently created.
$account = user_load($user->uid);
if (!empty($account->data['zoomapi_user_id']) && $account->data['zoomapi_user_id'] == $meeting['host_id']) {
$url = $meeting['start_url'];
}
else {
$url = $meeting['join_url'];
}
watchdog(__FUNCTION__, 'User @name (uid: !uid) joined meeting "@topic" (id: !meeting_id).', [
'@name' => $account->name,
'!uid' => $account->uid,
'@topic' => $meeting['topic'],
'!meeting_id' => $meeting['id'],
], WATCHDOG_INFO);
drupal_goto($url, [
'external' => TRUE,
]);
}
/**
* Create Meeting.
*
* @param object $account
* The Drupal account to act as the host.
* @param array $options
* Optional array of meeting options.
*
* @return array
* An array of the meeting setup.
*
* @todo add meeting tracking for easier start/join url lookups for live
* meetings.
*/
function zoomapi_meeting_create($account, array $options = []) {
if (empty($account->data['zoomapi_user_id'])) {
return [];
}
if (empty($options['type'])) {
$options['type'] = variable_get('zoomapi_meeting_type_default', ZOOMAPI_MEETING_TYPE_DEFAULT);
}
if (empty($options['topic'])) {
$options['topic'] = zoomapi_meeting_topic_generate($account, $options);
}
$zoomapi_meeting = new ZoomAPIMeeting();
$zoom_meeting = $zoomapi_meeting
->create($account->data['zoomapi_user_id'], $options['topic'], $options['type'], $options);
zoomapi_track_meeting($zoom_meeting);
return $zoom_meeting;
}
/**
* Create Meeting for Entity.
*
* Create a meeting for a specific Drupal entity (node). The typical use case is
* an Event content type (node) or entity.
*
* @param object $account
* The Drupal account to act as the host.
* @param object $entity
* The entity the meeting is being created for.
* @param string $entity_type
* The type (node, user, etc.) of entity.
* @param array $options
* Optional array of meeting options.
*
* @return array
* An array of the meeting setup.
*
* @todo add meeting tracking for easier start/join url lookups for live
* meetings.
*/
function zoomapi_meeting_create_for_entity($account, $entity, $entity_type, array $options = []) {
if (empty($account->data['zoomapi_user_id'])) {
return [];
}
if (empty($options['type'])) {
$options['type'] = variable_get('zoomapi_meeting_type_default', ZOOMAPI_MEETING_TYPE_DEFAULT);
}
if (empty($options['topic'])) {
$options['topic'] = zoomapi_meeting_topic_generate_for_entity($account, $entity, $entity_type, $options);
}
list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity);
$zoomapi_meeting = new ZoomAPIMeeting();
$zoom_meeting = $zoomapi_meeting
->create($account->data['zoomapi_user_id'], $options['topic'], $options['type'], $options);
$zoom_meeting['entity_type'] = $entity_type;
$zoom_meeting['entity_id'] = $entity_id;
zoomapi_track_meeting($zoom_meeting);
return $zoom_meeting;
}
/**
* Get Meeting for Entity.
*
* @param object $entity
* The entity the meeting is being created for.
* @param string $entity_type
* The type (node, user, etc.) of entity.
*
* @return array
* An array of the meeting setup.
*/
function zoomapi_meeting_get_meeting_for_entity($entity, $entity_type) {
list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity);
$sql = 'SELECT data FROM {zoomapi_meeting_tracker}';
$sql .= ' WHERE entity_type = :entity_type';
$sql .= ' AND entity_id = :entity_id';
// We only expect a single record for any entity_type/entity_id. Just in case
// that is a false assumption, sort by the created date DESC so we grab the
// most recent record.
$sql .= ' ORDER BY created DESC';
$sql_args = [
':entity_type' => $entity_type,
':entity_id' => $entity_id,
];
if ($data = db_query($sql, $sql_args)
->fetchField()) {
return unserialize($data);
}
return [];
}
/**
* Create Instant Meeting for Account.
*
* @param object $account
* The Drupal account to act as the host.
* @param array $options
* Optional array of meeting options.
*
* @return array
* An array of the meeting setup.
*/
function zoomapi_meeting_create_instant_meeting($account, array $options = []) {
if (empty($account->data['zoomapi_user_id'])) {
return [];
}
$options['type'] = ZOOMAPI_MEETING_TYPE_INSTANT;
if (empty($options['topic'])) {
$options['topic'] = zoomapi_meeting_topic_generate_for_entity($account, $account, 'user', $options);
}
$zoom_meeting = zoomapi_meeting_create_for_entity($account, $account, 'user', $options);
$zoom_meeting['entity_type'] = 'user';
$zoom_meeting['entity_id'] = $account->uid;
zoomapi_track_meeting($zoom_meeting);
return $zoom_meeting;
}
/**
* Get account instant meeting information.
*
* @param object $account
* The Drupal account to act as the host.
*
* @return array
* The meeting information.
*/
function zoomapi_meeting_get_account_instant_meeting($account) {
if (empty($account->data['zoomapi_user_id'])) {
return [];
}
$sql = 'SELECT data FROM {zoomapi_meeting_tracker}';
$sql .= ' WHERE host_zoom_user_id = :host_id';
$sql .= ' AND meeting_type = :instant';
$sql_args = [
':host_id' => $account->data['zoomapi_user_id'],
':instant' => ZOOMAPI_MEETING_TYPE_INSTANT,
];
if ($data = db_query($sql, $sql_args)
->fetchField()) {
return unserialize($data);
}
return [];
}
/**
* Generate topic for a Zoom meeting.
*
* @param object $account
* The drupal account acting as meeting host.
* @param array $options
* Optional array of meeting options.
*
* @return string
* The meeting topic.
*
* @todo add drupal_alter hook.
*/
function zoomapi_meeting_topic_generate($account, array $options = []) {
$topic = t('Meeting for @name', [
'@name' => $account->name,
]);
$context = [
'account' => $account,
'options' => $options,
];
drupal_alter('zoomapi_meeting_topic', $topic, $context);
return $topic;
}
/**
* Generate topic for a entity-based Zoom meeting.
*
* @param object $account
* The drupal account acting as meeting host.
* @param object $entity
* The entity the meeting is being created for.
* @param string $entity_type
* The type (node, user, etc.) of entity.
* @param array $options
* Optional array of meeting options.
*
* @return string
* The meeting topic.
*
* @todo add drupal_alter hook.
*/
function zoomapi_meeting_topic_generate_for_entity($account, $entity, $entity_type, array $options = []) {
$entity_wrapper = entity_metadata_wrapper($entity_type, $entity);
$topic = $entity_wrapper
->label();
$context = [
'account' => $account,
'entity' => $entity,
'entity_type' => $entity_type,
'options' => $options,
];
drupal_alter('zoomapi_meeting_topic_for_entity', $topic, $context);
if (empty($topic)) {
$topic = zoomapi_meeting_topic_generate($account);
}
return $topic;
}
/**
* Track meeting information.
*
* @param array $meeting
* The meeting array provided by Zoom.
*/
function zoomapi_track_meeting(array $meeting) {
$record = [
'uuid' => $meeting['uuid'],
'meeting_id' => $meeting['id'],
'host_zoom_user_id' => $meeting['host_id'],
'topic' => $meeting['topic'],
'meeting_type' => $meeting['type'],
'start_time' => !empty($meeting['start_time']) ? strtotime($meeting['start_time']) : 0,
'duration' => !empty($meeting['duration']) ? $meeting['duration'] : 0,
'timezone' => !empty($meeting['timezone']) ? $meeting['timezone'] : '',
'start_url' => $meeting['start_url'],
'join_url' => $meeting['join_url'],
'created' => time(),
'entity_type' => !empty($meeting['entity_type']) ? $meeting['entity_type'] : '',
'entity_id' => !empty($meeting['entity_id']) ? $meeting['entity_id'] : 0,
// Default record expires after 24 hrs.
'expires' => strtotime('+24 hours'),
'data' => serialize($meeting),
];
// If the start time and duration is found, then expire the record several
// hours after the meeting is set to end.
if (!empty($record['start_time']) && !empty($record['duration'])) {
$record['expires'] = $record['start_time'] + $record['duration'] + 2 * 60 * 60;
}
// There should not be multiple instant meetings for the same host account.
// While not performing a logic check here to stop that, we do want to at
// least eliminate any existing instant records for that host. This will avoid
// any potential look ups later to find an account's instant meeting.
if ($record['meeting_type'] == ZOOMAPI_MEETING_TYPE_INSTANT) {
$num_deleted = db_delete('zoomapi_meeting_tracker')
->condition('host_zoom_user_id', $record['host_zoom_user_id'])
->condition('meeting_type', ZOOMAPI_MEETING_TYPE_INSTANT)
->execute();
if ($num_deleted && variable_get('zoomapi_debug', FALSE)) {
watchdog('zoomapi_debug', 'Removed !count existing instant meeting tracker for host_id !host_id', [
'!count' => $num_deleted,
'!host_id' => $record['host_zoom_user_id'],
], WATCHDOG_DEBUG);
}
}
// There should only be one meeting per entity_type/entity_id. While not
// performing logic check here, at least make sure table only has one record.
if (!empty($record['entity_type']) && !empty($record['entity_id'])) {
// At this time do not delete the record and instead check to see if one
// already exists.
if (variable_get('zoomapi_debug', FALSE)) {
$count = db_query('SELECT count(uuid) FROM {zoomapi_meeting_tracker} WHERE entity_type = :entity_type AND entity_id = :entity_id', [
':entity_type' => $record['entity_type'],
':entity_id' => $record['entity_id'],
])
->rowCount();
if ($count) {
watchdog('zoomapi_debug', '!count zoomapi_meeting_tracker records already exist for entity_type !entity_type entity_id !entity_id', [
'!count' => $count,
'!entity_type' => $record['entity_type'],
'!entity_id' => $record['entity_id'],
], WATCHDOG_DEBUG);
}
}
// @todo
// Do we need to do this?
if (FALSE) {
db_delete('zoomapi_meeting_tracker')
->condition('entity_type', $record['entity_type'])
->condition('entity_id', $record['entity_id'])
->execute();
}
}
db_merge('zoomapi_meeting_tracker')
->key([
'uuid' => $record['uuid'],
])
->fields($record)
->execute();
}
/**
* Get account cloud recordings.
*
* @param object $account
* The user account to retrieve the cloud recordings for.
* @param array $options
* An optional array of options to filter the recordings list.
*
* @see ZoomAPIRecording->list()
*/
function zoomapi_recordings_get_list($account, array $options = []) {
$account_recordings = [];
if ($host_zoom_user_id = zoomapi_get_zoom_user_id($account)) {
$zoomapi_recordings = new ZoomAPIRecording();
// @todo utilize $options.
$account_recordings = $zoomapi_recordings
->list($host_zoom_user_id);
}
return $account_recordings;
}
/**
* Clean Drupal account of zoom api elements.
*
* @param object $account
* The drupal account to clean.
*/
function zoomapi_user_clean_account($account) {
unset($account->data['zoomapi_user_id']);
unset($account->data['zoomapi_user_email']);
user_save($account);
}
Functions
Name | Description |
---|---|
zoomapi_cron | Implements hook_cron(). |
zoomapi_get_zoom_user_email | Get account zoom user email. |
zoomapi_get_zoom_user_id | Get account zoom user ID. |
zoomapi_meeting_create | Create Meeting. |
zoomapi_meeting_create_for_entity | Create Meeting for Entity. |
zoomapi_meeting_create_instant_meeting | Create Instant Meeting for Account. |
zoomapi_meeting_get_account_instant_meeting | Get account instant meeting information. |
zoomapi_meeting_get_meeting_for_entity | Get Meeting for Entity. |
zoomapi_meeting_join | Join Meeting. |
zoomapi_meeting_topic_generate | Generate topic for a Zoom meeting. |
zoomapi_meeting_topic_generate_for_entity | Generate topic for a entity-based Zoom meeting. |
zoomapi_menu | Implements hook_menu(). |
zoomapi_permission | Implements hook_permission(). |
zoomapi_recordings_get_list | Get account cloud recordings. |
zoomapi_track_meeting | Track meeting information. |
zoomapi_user_checkemail | Does Zoom user exist for account. |
zoomapi_user_clean_account | Clean Drupal account of zoom api elements. |
zoomapi_user_connect_accounts | Connect account with zoom account. |
zoomapi_user_create | Create Zoom User. |
zoomapi_user_custcreate | Create Custom Zoom User. |
zoomapi_user_custcreate_email_generate | Generate custom email address for custcreate. |
zoomapi_user_find_by_email | Find Zoom user by email. |
zoomapi_user_get | Get Zoom User. |
zoomapi_user_update_info | Update User Info. |
zoomapi_user_uploadpicture | Upload picture to Zoom account. |
zoomapi_webhooks_access | Access callback: Zoom API Webhooks. |