View source
<?php
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_USER_EMAIL_PATTERN_DEFAULT', 'zoomuser_[user:uid]@[site:url-brief]');
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);
define('ZOOMAPI_MEETING_TIME_FORMAT_LOCAL', 'Y-m-d\\TH:i:s');
define('ZOOMAPI_MEETING_TIME_FORMAT_DEFAULT', 'Y-m-d\\TH:i:s\\Z');
define('ZOOMAPI_MEETING_TIME_FORMAT_TZ', 'GMT');
class ZoomAPISystemQueue extends SystemQueue {
public function createItem($data) {
$serial_data = serialize($data);
$query = db_merge('queue')
->key([
'name' => $this->name,
'data' => $serial_data,
])
->fields([
'name' => $this->name,
'data' => $serial_data,
'created' => REQUEST_TIME,
]);
return (bool) $query
->execute();
}
}
function zoomapi_permission() {
return [
'administer zoomapi' => [
'title' => t('Administer Zoom API'),
'description' => t('Administer the Zoom API settings.'),
'restrict access' => TRUE,
],
'access zoomapi reports' => [
'title' => t('Access Zoom API Reports'),
'description' => t('Access the Zoom API reports.'),
'restrict access' => TRUE,
],
];
}
function zoomapi_menu() {
$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.webhooks.inc',
'type' => MENU_CALLBACK,
];
$items['admin/config/services/zoomapi'] = [
'title' => 'Zoom API',
'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,
];
$items['admin/config/services/zoomapi/settings'] = [
'title' => 'Settings',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
];
$items['admin/config/services/zoomapi/webhooks'] = [
'title' => 'Webhooks',
'description' => 'Manage existing and create new webhooks.',
'page callback' => 'drupal_get_form',
'page arguments' => [
'zoomapi_report_custom_list_form',
'webhooks',
],
'access arguments' => [
'administer zoomapi',
],
'file' => 'zoomapi.admin.inc',
'type' => MENU_LOCAL_TASK,
];
$items['admin/reports/zoomapi'] = [
'title' => 'Zoom API',
'description' => 'A few basic custom reports and API reports.',
'position' => 'left',
'page callback' => 'system_admin_menu_block_page',
'access arguments' => [
'access zoomapi reports',
],
'file' => 'system.admin.inc',
'file path' => drupal_get_path('module', 'system'),
'type' => MENU_NORMAL_ITEM,
];
foreach ([
'users',
] as $api) {
$label = ucfirst($api);
$items["admin/reports/zoomapi/{$api}"] = [
'title' => $label,
'description' => "Basic listing of Zoom {$label}.",
'page callback' => 'drupal_get_form',
'page arguments' => [
'zoomapi_report_custom_list_form',
$api,
],
'access arguments' => [
'access zoomapi reports',
],
'file' => 'zoomapi.admin.inc',
'type' => MENU_NORMAL_ITEM,
];
}
return $items;
}
function zoomapi_webhooks_access() {
$http_verb = strtoupper($_SERVER['REQUEST_METHOD']);
if ($http_verb != 'POST') {
return FALSE;
}
if (!variable_get('zoomapi_webhooks_enabled', FALSE)) {
return FALSE;
}
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;
}
function zoomapi_user_insert(&$edit, $account, $category) {
if (!variable_get('zoomapi_create_on_new_user', FALSE)) {
return;
}
if (variable_get('zoomapi_create_on_new_user', FALSE) && !zoomapi_user_email_exists($account->mail)) {
zoomapi_create_user($account);
}
}
function zoomapi_cron_queue_info() {
$info['zoomapi_download_meeting_recordings_to_entity_queue'] = [
'worker callback' => 'zoomapi_download_meeting_recordings_to_entity_worker',
'time' => 60,
];
return $info;
}
function zoomapi_cron() {
$process_downloads = variable_get('zoomapi_recordings_download_process', 'cron');
if ($process_downloads == 'cron') {
$meeting_recordings_trackers = zoomapi_get_unsuccessful_recording_download_tracking_info();
if (!$meeting_recordings_trackers) {
return;
}
foreach ($meeting_recordings_trackers as $tracker) {
zoomapi_download_meeting_recordings_from_tracker($tracker);
}
}
elseif ($process_downloads == 'queue') {
if ($uuids = zoomapi_get_unsuccessful_recording_download_uuids()) {
$queue = DrupalQueue::get('zoomapi_download_meeting_recordings_to_entity_queue');
foreach ($uuids as $uuid) {
$queue
->createItem($uuid);
}
}
}
$retention_days = variable_get('zoomapi_retain_webhook_days', 30);
$expire_ts = REQUEST_TIME - $retention_days * 24 * 60 * 60;
db_delete('zoomapi_webhooks_log')
->condition('created', $expire_ts, '<')
->execute();
$retention_days = variable_get('zoomapi_recordings_download_retention_days', 60);
$expire_ts = REQUEST_TIME - $retention_days * 24 * 60 * 60;
db_delete('zoomapi_recordings_download_tracker')
->condition('created', $expire_ts, '<')
->execute();
$retention_days = variable_get('zoomapi_retain_meeting_index_days', 30);
$expire_ts = REQUEST_TIME - $retention_days * 24 * 60 * 60;
$expire_dt = new DateTime('@' . $expire_ts);
$expire_iso = $expire_dt
->format('Y-m-d\\TH:i:s\\Z');
db_delete('zoomapi_meetings_index')
->condition('start_time', '', '!=')
->condition('start_time', $expire_iso, '<')
->execute();
db_delete('zoomapi_meetings_index')
->condition('start_time', '')
->condition('created', $expire_ts, '<')
->execute();
}
function zoomapi_zoomapi_user_create($zoom_user, $account) {
$user_info['timezone'] = zoomapi_get_account_timezone($account);
zoomapi_update_user($account, $user_info);
}
function zoomapi_client() {
module_load_include('inc', 'zoomapi', 'zoomapi.api');
return zoomapi_api_client();
}
function zoomapi_user_email_exists($email, $api_check = FALSE) {
try {
$exists = FALSE;
if (!$api_check) {
$user_info = zoomapi_get_zoom_user_tracker_info($email);
$exists = !empty($user_info['email']);
}
else {
$exists = zoomapi_api_user_email_exists($email);
}
return $exists;
} catch (\Exception $e) {
return FALSE;
}
}
function zoomapi_create_user($account, array $user_info = [], $create_action = '') {
module_load_include('inc', 'zoomapi', 'zoomapi.api');
$uid = is_numeric($account) ? $account : $account->uid;
if (empty($user_info['type'])) {
$user_info['type'] = variable_get('zoomapi_user_create_type_default', ZOOMAPI_USER_TYPE_DEFAULT);
}
if (empty($user_info['email'])) {
$account = is_numeric($account) ? user_load($account) : $account;
$user_info['email'] = variable_get('zoomapi_use_account_email', FALSE) ? $account->mail : zoomapi_generate_user_email($account);
}
$params['action'] = $create_action ?: variable_get('zoomapi_user_create_action_default', 'custCreate');
$params['user_info'] = $user_info;
if (zoomapi_user_email_exists($params['user_info']['email'])) {
watchdog(__FUNCTION__, 'Unable to create a Zoom user. The email @email is already in use.', [
'@email' => $params['user_info']['email'],
], WATCHDOG_ERROR);
return FALSE;
}
return zoomapi_api_create_user($uid, $params);
}
function zoomapi_generate_user_email($account) {
$account = is_numeric($account) ? user_load($account) : $account;
$mail_pattern = variable_get('zoomapi_user_email_pattern', ZOOMAPI_USER_EMAIL_PATTERN_DEFAULT);
$token_data = [
'user' => $account,
];
$email = token_replace($mail_pattern, $token_data);
return $email;
}
function zoomapi_get_users() {
module_load_include('inc', 'zoomapi', 'zoomapi.api');
$zoom_users = zoomapi_api_get_users();
return $zoom_users;
}
function zoomapi_get_user($account, $autocreate = NULL) {
try {
module_load_include('inc', 'zoomapi', 'zoomapi.api');
$autocreate = !is_null($autocreate) ? $autocreate : variable_get('zoomapi_autocreate_on_get', FALSE);
$uid = is_numeric($account) ? $account : $account->uid;
$zoom_user_id = zoomapi_get_zoom_user_id($uid);
$zoom_email = '';
$zoom_user = [];
if ($zoom_user_id) {
$zoom_user = zoomapi_api_get_user($zoom_user_id);
}
if (!$zoom_user && ($zoom_email = zoomapi_generate_user_email($account))) {
$zoom_user = zoomapi_api_get_user($zoom_email, $uid);
}
if (!$zoom_user && $autocreate) {
$zoom_user = zoomapi_create_user($account);
}
return $zoom_user;
} catch (\Exception $e) {
watchdog(__FUNCTION__, 'Unable to retrieve zoom account @zoom_acct for user !uid. Error: @e', [
'@e' => $e
->getMessage(),
'@zoom_acct' => $zoom_user_id,
'!uid' => $uid,
], WATCHDOG_ERROR);
return [];
}
}
function zoomapi_update_user($account, array $user_info) {
module_load_include('inc', 'zoomapi', 'zoomapi.api');
$uid = is_numeric($account) ? $account : $account->uid;
$zoom_user_id = zoomapi_get_zoom_user_id($uid);
if (!$zoom_user_id) {
watchdog(__FUNCTION__, 'Unable to update Zoom account for user !uid. Unable to lookup Zoom account ID.', [
'!uid' => $uid,
], WATCHDOG_ERROR);
return FALSE;
}
return zoomapi_api_update_user($uid, $zoom_user_id, $user_info);
}
function zoomapi_update_user_email($account, $email) {
module_load_include('inc', 'zoomapi', 'zoomapi.api');
$uid = is_numeric($account) ? $account : $account->uid;
$zoom_user_id = zoomapi_get_zoom_user_id($uid);
if (!$zoom_user_id) {
watchdog(__FUNCTION__, 'Unable to change Zoom account email to @email for user !uid. Unable to lookup Zoom account ID.', [
'@email' => $email,
'!uid' => $uid,
], WATCHDOG_ERROR);
return FALSE;
}
return zoomapi_api_update_user_email($zoom_user_id, $email);
}
function zoomapi_track_user($uid, array $zoom_user) {
$realm = zoomapi_realm();
$record = [
'uid' => $uid,
'realm' => $realm,
'zoom_user_id' => $zoom_user['id'],
'zoom_email' => $zoom_user['email'],
];
$exists = db_query("\n SELECT\n uid,\n realm,\n zoom_user_id,\n zoom_email\n FROM {zoomapi_users}\n WHERE zoom_email = :email\n AND realm = :realm\n ", [
':email' => $record['zoom_email'],
':realm' => $realm,
])
->fetchAssoc();
if ($exists) {
if ($record !== $exists) {
$record['changed'] = REQUEST_TIME;
drupal_write_record('zoomapi_users', $record, [
'email',
]);
}
}
else {
$record['created'] = REQUEST_TIME;
$record['changed'] = REQUEST_TIME;
drupal_write_record('zoomapi_users', $record);
}
}
function zoomapi_get_zoom_users_tracker_info(array $uids_or_emails = []) {
$realm = zoomapi_realm();
$sql = "\n SELECT\n uid,\n realm,\n zoom_user_id,\n zoom_email\n FROM {zoomapi_users}\n WHERE realm = :realm\n ";
$sql_args = [
':realm' => $realm,
];
if ($uids_or_emails) {
$sql .= "\n AND (\n uid IN (:uids)\n OR zoom_email IN (:emails)\n )\n ";
$sql_args += [
':uids' => $uids_or_emails,
':emails' => $uids_or_emails,
];
}
$results = db_query($sql, $sql_args)
->fetchAllAssoc('zoom_email');
return $results;
}
function zoomapi_get_user_from_zoom_userid($zoom_user_id) {
$uid = db_query("\n SELECT\n uid\n FROM {zoomapi_users}\n WHERE zoom_user_id = :zoom_user_id\n AND realm = :realm\n ", [
':zoom_user_id' => $zoom_user_id,
':realm' => zoomapi_realm(),
])
->fetchField();
return $uid ?: 0;
}
function zoomapi_get_zoom_user_tracker_info($uid_or_email) {
$info = zoomapi_get_zoom_users_tracker_info([
$uid_or_email,
]);
return $info ? (array) reset($info) : FALSE;
}
function zoomapi_get_zoom_user_id($uid) {
$info = zoomapi_get_zoom_user_tracker_info($uid);
return !empty($info['zoom_user_id']) ? $info['zoom_user_id'] : FALSE;
}
function zoomapi_get_zoom_user_email($uid) {
$info = zoomapi_get_zoom_user_tracker_info($uid);
return !empty($info['zoom_email']) ? $info['zoom_email'] : FALSE;
}
function zoomapi_get_user_settings($account) {
module_load_include('inc', 'zoomapi', 'zoomapi.api');
$uid = is_numeric($account) ? $account : $account->uid;
$zoom_user_id = zoomapi_get_zoom_user_id($uid);
if (!$zoom_user_id) {
watchdog(__FUNCTION__, 'Unable to retrieve Zoom user settings for user !uid. Unable to lookup Zoom account ID.', [
'!uid' => $uid,
], WATCHDOG_ERROR);
return [];
}
return zoomapi_api_get_user_settings($zoom_user_id);
}
function zoomapi_update_user_settings($account, array $settings) {
module_load_include('inc', 'zoomapi', 'zoomapi.api');
$uid = is_numeric($account) ? $account : $account->uid;
$zoom_user_id = zoomapi_get_zoom_user_id($uid);
if (!$zoom_user_id) {
watchdog(__FUNCTION__, 'Unable to update Zoom user settings for user !uid. Unable to lookup Zoom account ID.', [
'!uid' => $uid,
], WATCHDOG_ERROR);
return [];
}
return zoomapi_api_update_user_settings($zoom_user_id, $settings);
}
function zoomapi_create_meeting($uid, array $params, array $context = []) {
module_load_include('inc', 'zoomapi', 'zoomapi.api');
$zoom_user_id = zoomapi_get_zoom_user_id($uid);
if (!$zoom_user_id) {
watchdog(__FUNCTION__, 'Unable to create Zoom meeting for user !uid. Unable to lookup Zoom account ID.', [
'!uid' => $uid,
], WATCHDOG_ERROR);
return FALSE;
}
if (empty($params['type'])) {
$params['type'] = ZOOMAPI_MEETING_TYPE_DEFAULT;
}
if (empty($params['start_time']) || empty($params['duration'])) {
unset($params['start_time']);
unset($params['duration']);
$params['type'] = ZOOMAPI_MEETING_TYPE_INSTANT;
}
elseif (is_numeric($params['start_time'])) {
$params['timezone'] = !empty($params['timezone']) ? $params['timezone'] : variable_get('date_default_timezone', @date_default_timezone_get());
$params['start_time'] = zoomapi_format_timestamp($params['start_time'], $params['timezone']);
}
drupal_alter('zoomapi_create_meeting', $params, $context);
return zoomapi_api_create_meeting($zoom_user_id, $params, $context);
}
function zoomapi_create_meeting_for_entity($entity, $entity_type, array $params = [], $uid = 0) {
if (is_numeric($entity)) {
$entity = entity_load_single($entity_type, $entity);
}
$context = [
'entity_type' => $entity_type,
'entity' => $entity,
'uid' => $uid ?: $entity->uid,
];
if (empty($params['topic'])) {
$entity_wrapper = entity_metadata_wrapper($entity_type, $entity);
$params['topic'] = $entity_wrapper
->label();
}
drupal_alter('zoomapi_create_meeting_for_entity', $params, $context);
return zoomapi_create_meeting($context['uid'], $params, $context);
}
function zoomapi_create_instant_meeting_for_user($account_uid, array $params = []) {
$params['type'] = ZOOMAPI_MEETING_TYPE_INSTANT;
return zoomapi_create_meeting_for_entity($account_uid, 'user', $params);
}
function zoomapi_get_meeting($meeting_id) {
module_load_include('inc', 'zoomapi', 'zoomapi.api');
return zoomapi_api_get_meeting($meeting_id);
}
function zoomapi_get_meeting_for_entity($entity_type, $entity_id, $api_refresh = FALSE) {
if (!is_numeric($entity_id)) {
list($entity_id, , ) = entity_extract_ids($entity_type, $entity_id);
}
$data = db_query("\n SELECT\n *\n FROM {zoomapi_meetings_index}\n WHERE entity_type = :entity_type\n AND entity_id = :entity_id\n AND realm = :realm\n ORDER BY created DESC\n ", [
':entity_type' => $entity_type,
':entity_id' => $entity_id,
':realm' => zoomapi_realm(),
])
->fetchAssoc();
if ($api_refresh && !empty($data['id'])) {
$data = zoomapi_get_meeting($data['id']);
}
return $data;
}
function zoomapi_get_entity_from_meeting($meeting_id) {
$results = db_query("\n SELECT\n entity_type,\n entity_id\n FROM {zoomapi_meetings_index}\n WHERE realm = :realm\n AND (\n uuid = :meeting_id\n OR id = :meeting_id\n )\n ORDER BY created DESC\n ", [
':meeting_id' => $meeting_id,
':realm' => zoomapi_realm(),
])
->fetchAssoc();
return $results ? [
$results['entity_type'],
$results['entity_id'],
] : [
'',
0,
];
}
function zoomapi_join_meeting($zoom_meeting) {
global $user;
if (is_numeric($zoom_meeting)) {
$zoom_meeting = zoomapi_get_meeting($zoom_meeting);
}
if ($zoom_user_id = zoomapi_get_zoom_user_id($user->uid)) {
$url = $zoom_user_id && $zoom_user_id == $zoom_meeting['host_id'] ? $zoom_meeting['start_url'] : $zoom_meeting['join_url'];
watchdog(__FUNCTION__, 'User (uid: !uid) joined meeting "@topic" (id: !meeting_id).', [
'!uid' => $user->uid,
'@topic' => $zoom_meeting['topic'],
'!meeting_id' => $zoom_meeting['id'],
], WATCHDOG_INFO);
drupal_goto($url, [
'external' => TRUE,
]);
}
drupal_goto();
}
function zoomapi_track_meeting(array $zoom_meeting, $entity_type = '', $entity_id = 0) {
$defaults = [
'uuid' => '',
'id' => '',
'host_id' => '',
'topic' => '',
'type' => 0,
'start_time' => '',
'duration' => 0,
'timezone' => '',
'start_url' => '',
'join_url' => '',
'created' => REQUEST_TIME,
'realm' => zoomapi_realm(),
'entity_type' => $entity_type,
'entity_id' => $entity_id,
];
$record = array_merge($defaults, array_intersect_key($zoom_meeting, $defaults));
db_merge('zoomapi_meetings_index')
->key([
'uuid' => $record['uuid'],
])
->fields($record)
->execute();
}
function zoomapi_extract_entity_info_from_meeting_context($context) {
$entity_type = !empty($context['entity_type']) ? $context['entity_type'] : '';
$entity_id = !empty($context['entity_id']) ? $context['entity_id'] : 0;
if (!empty($context['entity'])) {
$entity_type = $entity_type ?: $context['entity']
->entityType();
if (!$entity_id) {
list($entity_id, , ) = entity_extract_ids($entity_type, $context['entity']);
}
}
return [
$entity_type,
$entity_id,
];
}
function zoomapi_get_user_recordings($account) {
module_load_include('inc', 'zoomapi', 'zoomapi.api');
$uid = is_numeric($account) ? $account : $account->uid;
$zoom_user_id = zoomapi_get_zoom_user_id($uid);
if (!$zoom_user_id) {
watchdog(__FUNCTION__, 'Unable to get recordings for user !uid. Unable to lookup Zoom account ID.', [
'!uid' => $uid,
], WATCHDOG_ERROR);
return FALSE;
}
return zoomapi_api_get_user_recordings($zoom_user_id);
}
function zoomapi_get_meeting_recordings($meeting_id, $refresh = FALSE) {
$zoom_meeting_recordings = [];
if (!$refresh && ($tracker = zoomapi_get_recording_download_tracking_info($meeting_id))) {
$zoom_meeting_recordings = !empty($tracker['data']) ? $tracker['data'] : [];
}
if ($refresh || empty($zoom_meeting_recordings)) {
module_load_include('inc', 'zoomapi', 'zoomapi.api');
$zoom_meeting_recordings = zoomapi_api_get_meeting_recordings($meeting_id);
}
return $zoom_meeting_recordings;
}
function zoomapi_get_meeting_recordings_for_entity($entity_type, $entity_id) {
if (!is_numeric($entity_id)) {
list($entity_id, , ) = entity_extract_ids($entity_type, $entity_id);
}
$meeting_info = zoomapi_get_meeting_for_entity($entity_type, $entity_id);
$recordings = $meeting_info ? zoomapi_get_meeting_recordings($meeting_info['id']) : [];
return $recordings;
}
function zoomapi_download_meeting_recordings($zoom_meeting_recordings, $destination_directory, $context = []) {
$filename_prefix = !empty($zoom_meeting_recordings['topic']) ? $zoom_meeting_recordings['topic'] : $zoom_meeting_recordings['id'];
$files = [];
drupal_alter('zoomapi_download_meeting_recordings', $zoom_meeting_recordings, $context);
foreach ($zoom_meeting_recordings['recording_files'] as $zoom_meeting_recording) {
if ($zoom_meeting_recording['status'] != 'completed') {
watchdog(__FUNCTION__, 'Unable to download recording !recording_id. The recording has not completed.', [
'!recording_id' => $zoom_meeting_recording['id'],
], WATCHDOG_ERROR);
$files[$zoom_meeting_recording['id']] = FALSE;
continue;
}
$filename = $filename_prefix . '-' . strtotime($zoom_meeting_recording['recording_start']) . '.' . strtolower($zoom_meeting_recording['file_type']);
$context['meeting'] = $zoom_meeting_recordings;
$context['recording'] = $zoom_meeting_recording;
unset($context['meeting']['recording_files']);
drupal_alter('zoomapi_meeting_recording_filename', $filename, $context);
$file = zoomapi_download_recording($zoom_meeting_recording, $destination_directory, $filename, $context);
$files[$zoom_meeting_recording['id']] = $file;
}
return $files;
}
function zoomapi_download_meeting_recordings_to_entity($entity, $entity_type, $field_name, array $zoom_meeting_recordings = []) {
if (is_numeric($entity)) {
$entity = entity_load_single($entity_type, $entity);
}
list($entity_id, , ) = entity_extract_ids($entity_type, $entity);
$destination_directory = zoomapi_get_entity_field_location($entity, $entity_type, $field_name);
$zoom_meeting_recordings = $zoom_meeting_recordings ?: zoomapi_get_meeting_recordings_for_entity($entity_type, $entity);
if (!zoomapi_get_recording_download_tracking_info($zoom_meeting_recordings['uuid'])) {
$destination_info = "{$entity_type}:{$entity_id}:{$field_name}";
zoomapi_start_recording_download_tracking($zoom_meeting_recordings, $destination_info, 'entity');
}
$context = [
'entity' => $entity,
'entity_id' => $entity_id,
'entity_type' => $entity_type,
'field_name' => $field_name,
];
$files = $zoom_meeting_recordings ? zoomapi_download_meeting_recordings($zoom_meeting_recordings, $destination_directory, $context) : [];
$success = count($files) == count(array_filter($files));
zoomapi_update_recording_download_tracking($zoom_meeting_recordings['uuid'], $success);
zoomapi_attach_recording_files_to_entity_field($entity, $entity_type, $field_name, $files, $success);
return $success;
}
function zoomapi_download_meeting_recordings_to_entity_worker($meeting_uuid) {
$tracker = db_query("\n SELECT\n *\n FROM {zoomapi_recordings_download_tracker}\n WHERE meeting_uuid = :meeting_uuid\n ", [
':meeting_uuid' => $meeting_uuid,
])
->fetchObject();
if ($tracker && !$tracker->success) {
zoomapi_download_meeting_recordings_from_tracker($tracker);
}
}
function zoomapi_download_meeting_recordings_from_tracker($tracker) {
try {
if ($tracker->destination_type != 'entity') {
throw new Exception('Meeting is not associated with an entity.');
}
list($entity_type, $entity_id, $field_name) = explode(':', $tracker->destination);
$entity = entity_load_single($entity_type, $entity_id);
if (!$entity) {
throw new Exception('Unable to load entity to download meeting recordings.');
}
$data = is_array($tracker->data) ? $tracker->data : unserialize($tracker->data);
return zoomapi_download_meeting_recordings_to_entity($entity, $entity_type, $field_name, $data);
} catch (\Exception $e) {
watchdog(__FUNCTION__, 'Unable to download pending meeting recordings for meeting. Error: @e -- Debug: !debug', [
'@e' => $e
->getMessage(),
'!debug' => '<pre>' . print_r($tracker, TRUE) . '</pre>',
], WATCHDOG_ERROR);
return FALSE;
}
}
function zoomapi_attach_recording_files_to_entity_field($entity, $entity_type, $field_name, array $files, $replace_all_files = FALSE) {
if (is_numeric($entity)) {
$entity = entity_load_single($entity_type, $entity);
}
$context = [
'entity_type' => $entity_type,
'field_name' => $field_name,
'replace' => $replace_all_files,
];
$files = array_values(array_filter($files));
drupal_alter('zoomapi_attach_recordings_to_entity', $entity, $files, $context);
$field_files = !empty($entity->{$field_name}[LANGUAGE_NONE]) ? $entity->{$field_name}[LANGUAGE_NONE] : [];
$field_files = $replace_all_files ? $files : array_merge($field_files, $files);
$entity->{$field_name}[LANGUAGE_NONE] = $field_files;
entity_save($entity_type, $entity);
return $entity;
}
function zoomapi_start_recording_download_tracking(array $meeting_recordings, $destination, $destination_type = 'entity') {
$record = [
'meeting_uuid' => $meeting_recordings['uuid'],
'meeting_id' => $meeting_recordings['id'],
'destination_type' => $destination_type,
'destination' => $destination,
'success' => 0,
'attempts' => 0,
'data' => is_array($meeting_recordings) ? serialize($meeting_recordings) : $meeting_recordings,
'realm' => zoomapi_realm(),
'created' => REQUEST_TIME,
'changed' => REQUEST_TIME,
];
db_merge('zoomapi_recordings_download_tracker')
->key([
'meeting_uuid' => $meeting_recordings['uuid'],
])
->fields($record)
->execute();
return $record;
}
function zoomapi_get_recording_download_tracking_info($meeting_uuid) {
$record = db_query("\n SELECT\n *\n FROM {zoomapi_recordings_download_tracker}\n WHERE realm = :realm\n AND (\n meeting_uuid = :meeting_uuid\n OR meeting_id = :meeting_uuid\n )\n ", [
':realm' => zoomapi_realm(),
':meeting_uuid' => $meeting_uuid,
])
->fetchAssoc();
if (!empty($record)) {
$data = is_string($record['data']) ? unserialize($record['data']) : $record['data'];
if (zoomapi_validate_recording_tracking_info($data)) {
$record['data'] = $data;
}
}
return $record;
}
function zoomapi_validate_recording_tracking_info($info) {
if (empty($info['total_size'])) {
return FALSE;
}
if ($info['recording_count'] !== count($info['recording_files'])) {
return FALSE;
}
foreach ($info['recording_files'] as $recording_file) {
if (empty($recording_file['file_type'])) {
return FALSE;
}
if (empty($recording_file['file_size'])) {
return FALSE;
}
if ($recording_file['status'] !== 'completed') {
return FALSE;
}
}
return TRUE;
}
function zoomapi_get_unsuccessful_recording_download_uuids($max_attempts = NULL, $how_far_back = NULL) {
$max_attempts = $max_attempts ?: variable_get('zoomapi_recordings_download_max_attempts', 3);
$how_far_back = $how_far_back ?: variable_get('zoomapi_recordings_download_how_far_back', '-30 days');
$uuids = db_query("\n SELECT\n meeting_uuid\n FROM {zoomapi_recordings_download_tracker}\n WHERE realm = :realm\n AND success = :nosuccess\n AND :max_attempts > attempts\n AND created >= :created_after\n ", [
':realm' => zoomapi_realm(),
':nosuccess' => 0,
':max_attempts' => $max_attempts,
':created_after' => strtotime($how_far_back),
])
->fetchCol();
return $uuids;
}
function zoomapi_get_unsuccessful_recording_download_tracking_info($set_size = NULL, $max_attempts = NULL, $how_far_back = NULL) {
$set_size = $set_size ?: variable_get('zoomapi_recordings_download_cron_set_size', 10);
$max_attempts = $max_attempts ?: variable_get('zoomapi_recordings_download_max_attempts', 3);
$how_far_back = $how_far_back ?: variable_get('zoomapi_recordings_download_how_far_back', '-30 days');
$recordings = db_query_range("\n SELECT\n *\n FROM {zoomapi_recordings_download_tracker}\n WHERE realm = :realm\n AND success = :nosuccess\n AND :max_attempts > attempts\n AND created >= :created_after\n ", 0, $set_size, [
':realm' => zoomapi_realm(),
':nosuccess' => 0,
':max_attempts' => $max_attempts,
':created_after' => strtotime($how_far_back),
])
->fetchAllAssoc('meeting_uuid');
foreach ($recordings as &$recording) {
$data = is_string($recording->data) ? unserialize($recording->data) : $recording->data;
if (zoomapi_validate_recording_tracking_info($data)) {
$recording->data = $data;
}
}
return $recordings;
}
function zoomapi_update_recording_download_tracking($meeting_uuid, $success) {
if ($record = zoomapi_get_recording_download_tracking_info($meeting_uuid)) {
$record['attempts']++;
$record['success'] = (int) $success;
$record['changed'] = REQUEST_TIME;
$record['data'] = is_array($record['data']) ? serialize($record['data']) : $record['data'];
db_merge('zoomapi_recordings_download_tracker')
->key([
'meeting_uuid' => $meeting_uuid,
])
->fields($record)
->execute();
return $record;
}
return [];
}
function zoomapi_get_entity_field_location($entity, $entity_type, $field_name) {
$field_info = field_info_field($field_name);
$uri_scheme = $field_info['settings']['uri_scheme'];
if (is_numeric($entity)) {
$entity = entity_load_single($entity_type, $entity);
}
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
$field_instance_info = field_info_instance($entity_type, $field_name, $bundle);
$file_directory = $field_instance_info['settings']['file_directory'];
if (module_exists('filefield_paths') && !empty($field_instance_info['settings']['filefield_paths'])) {
$settings = $field_instance_info['settings']['filefield_paths'];
$settings['file_path']['options']['context'] = 'file_path';
$token_data = [
$entity_type => $entity,
];
$file_directory = filefield_paths_process_string($settings['file_path']['value'], $token_data, $settings['file_path']['options']);
}
$destination_directory = "{$uri_scheme}://{$file_directory}";
return $destination_directory;
}
function zoomapi_download_recording($zoom_meeting_recording, $destination_directory, $filename = '', $context = []) {
module_load_include('inc', 'zoomapi', 'zoomapi.api');
return zoomapi_api_download_recording($zoom_meeting_recording, $destination_directory, $filename, $context);
}
function zoomapi_clean_filename($filepath, $extensions = '') {
$filename = basename($filepath);
if (module_exists('transliteration')) {
module_load_include('inc', 'transliteration');
$langcode = NULL;
if (!empty($_POST['language'])) {
$languages = language_list();
$langcode = isset($languages[$_POST['language']]) ? $_POST['language'] : NULL;
}
$filename = transliteration_clean_filename($filename, $langcode);
}
if (empty($extensions)) {
$extensions = 'mp4 m4a';
}
$filename = file_munge_filename($filename, $extensions);
$directory = drupal_dirname($filepath);
return ($directory != '.' ? $directory . '/' : '') . $filename;
}
function zoomapi_download_recording_curl_write(&$ch, $data) {
$options = zoomapi_get_transfer_options();
$data_length = 0;
if ($fp = @fopen($options['filepath'], 'a')) {
fwrite($fp, $data);
fclose($fp);
$data_length = strlen($data);
}
return $data_length;
}
function zoomapi_set_transfer_options($options = NULL) {
static $current = FALSE;
if (isset($options)) {
$current = $options;
}
return $current;
}
function zoomapi_get_transfer_options() {
return zoomapi_set_transfer_options();
}
function zoomapi_save_file($filepath, $destination_directory, $replace = FILE_EXISTS_REPLACE) {
$file = new stdClass();
$file->status = 1;
$file->display = 1;
$file->filename = trim(basename($filepath), '.');
$file->uri = $filepath;
$file->filemime = file_get_mimetype($file->filename);
$file->filesize = filesize($filepath);
$extensions = 'mp4 mpa';
$file->filename = file_munge_filename($file->filename, $extensions);
$destination_scheme = file_uri_scheme($destination_directory);
if (!$destination_scheme || !file_stream_wrapper_valid_scheme($destination_scheme)) {
watchdog(__FUNCTION__, 'The recording could not be saved, because the destination %destination is invalid.', [
'%destination' => $destination_directory,
]);
return FALSE;
}
if (substr($destination_directory, -1) != '/') {
$destination_directory .= '/';
}
file_prepare_directory($destination_directory, FILE_CREATE_DIRECTORY);
$file->destination = file_destination($destination_directory . $file->filename, $replace);
$file->uri = $file->destination;
if (!file_unmanaged_copy($filepath, $file->uri, $replace)) {
watchdog(__FUNCTION__, 'Recording error. Could not move recording file %file to destination %destination.', [
'%file' => $file->filename,
'%destination' => $file->uri,
]);
return FALSE;
}
drupal_chmod($file->uri);
if ($replace == FILE_EXISTS_REPLACE) {
$existing_files = file_load_multiple(array(), [
'uri' => $file->uri,
]);
if (count($existing_files)) {
$existing = reset($existing_files);
$file->fid = $existing->fid;
}
}
if ($file = file_save($file)) {
return $file;
}
return FALSE;
}
function zoomapi_format_timestamp($timestamp, $timezone = '') {
$format = $timezone ? ZOOMAPI_MEETING_TIME_FORMAT_LOCAL : ZOOMAPI_MEETING_TIME_FORMAT_DEFAULT;
$timezone = $timezone ?: ZOOMAPI_MEETING_TIME_FORMAT_TZ;
return format_date($timestamp, 'custom', $format, $timezone);
}
function zoomapi_realm($url = '') {
global $base_url;
$realm = variable_get('zoomapi_realm', '');
if (!$realm || $url) {
$url = $url ?: $base_url;
$realm = explode('.', parse_url($url)['host'])[0];
}
return $realm;
}
function zoomapi_format_timestamp_local_server($timestamp) {
$timezone = variable_get('date_default_timezone', @date_default_timezone_get());
return zoomapi_format_timestamp($timestamp, $timezone);
}
function zoomapi_format_timestamp_local_user($timestamp, $account = NULL) {
$timezone = zoomapi_get_account_timezone($account);
return zoomapi_format_timestamp($timestamp, $timezone);
}
function zoomapi_get_account_timezone($account) {
$timezone = drupal_get_user_timezone();
if ($account) {
$account = is_numeric($account) ? user_load($account) : $account;
if (!empty($account->timezone)) {
$timezone = $account->timezone;
}
}
return $timezone;
}
function _zoomapi_get_webhook_url_part($url) {
$prefix = 'zoomapi/webhook';
$parsed_url = parse_url($url);
$path = trim($parsed_url['path']);
$part = substr($path, 0, strlen($prefix)) == $prefix ? substr($url, strlen($prefix)) : '';
return $part;
}