/** * Given an object containing all the necessary data, * (defined by the form in mod_form.php) this function * will create a new instance and return the id number * of the new instance. * * @param object $opencast Moodle {opencast} table DB record * * @return int newly created instance ID */ function opencast_add_instance($opencast) { global $DB, $USER; $opencast->timemodified = time(); $scast = new mod_opencast_series(); if (isset($opencast->newchannelname)) { $scast->setChannelName($opencast->newchannelname); } //$scast->setCourseId(); // $scast->setLicense($opencast->license); // $scast->setDepartment($opencast->department); $scast->setAllowAnnotations($opencast->allow_annotations == OPENCAST_ANNOTATIONS); // if (isset($opencast->template_id)) { // not set if creating new instance with existing channel // $scast->setTemplateId($opencast->template_id); // } $scast->setIvt($opencast->is_ivt); if (isset($opencast->inviting)) { $scast->setInvitingPossible($opencast->inviting); } $scast->setOrganizationDomain(mod_opencast_series::getOrganizationByEmail($USER->email)); $opencast->organization_domain = $scast->getOrganization(); if ($opencast->channelnew == OPENCAST_CHANNEL_NEW) { // New channel $scast->setProducer(mod_opencast_user::getExtIdFromMoodleUserId($USER->id)); $scast->doCreate(); $opencast->ext_id = $scast->getExtId(); } else { // Existing channel $scast->setExtId($opencast->ext_id); $scast->update(); } if (empty($opencast->timerestrict)) { $opencast->timeopen = 0; $opencast->timeclose = 0; } $opencast->id = $DB->insert_record('opencast', $opencast); return $opencast->id; }
// Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Version information * * @package mod * @subpackage opencast * @copyright 2013-2015 Université de Lausanne * @author Nicolas.Dunand@unil.ch * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once '../../config.php'; require_once $CFG->dirroot . '/mod/opencast/lib.php'; $seriesExtId = required_param('ext_id', PARAM_RAW_TRIMMED); $sc_user = new mod_opencast_user(); $url = '/series/' . $seriesExtId; if ($sc_user->getExternalAccount() != '') { $runas = true; } else { $runas = false; } $series = new mod_opencast_series(); $series->fetch($seriesExtId, false); //$series = mod_opencast_apicall::sendRequest($url, 'GET', null, null, null, null, $runas); $channel_details = ['title' => $series->title]; echo json_encode($channel_details);
/** * Displays a user selector * * @param bool $withproducers shall the producers be included ? * @param string $action_url where the form shall be posted * @param string $buttonlabel value attribute of the submit button * @param bool $switchaaionly display users with ExternalAccount only * @param bool $with_emtpyoption display 'remove user' option or not * @param bool $selectonly display HTML SELECT element only * @param int $selected_id if not zero, select OPTION with this index */ function display_user_selector($withproducers = false, $action_url = '', $buttonlabel = 'OK', $switchaaionly = false, $with_emtpyoption = false, $selectonly = false, $selected_id = 0) { global $context, $url, $course; if ($withproducers === false) { $producers = get_users_by_capability($context, 'mod/opencast:isproducer', 'u.id'); } $possible_users = get_users_by_capability($context, 'mod/opencast:use', 'u.id, u.lastname, u.firstname, u.maildisplay, u.email', 'u.lastname, u.firstname'); $options = []; if ($with_emtpyoption) { $options[-1] = '(' . get_string('removeowner', 'opencast') . ')'; } foreach ($possible_users as $possible_user_id => $possible_user) { if (in_array($possible_user_id, $this->displayed_userids)) { continue; } if ($withproducers === false && array_key_exists($possible_user_id, $producers)) { continue; } if ($switchaaionly && !mod_opencast_user::getExtIdFromMoodleUserId($possible_user_id)) { continue; } $option_text = $possible_user->lastname . ', ' . $possible_user->firstname; if ($possible_user->maildisplay == 1 or $possible_user->maildisplay == 2 and $course->id != SITEID and !isguestuser() or has_capability('moodle/course:viewhiddenuserfields', $context)) { $option_text .= ' (' . $possible_user->email . ')'; } $options[$possible_user_id] = $option_text; } if (count($options)) { if (!$selectonly) { echo html_writer::start_tag('form', ['method' => 'post', 'action' => $action_url, 'onsubmit' => 'return document.getElementById(\'menuuserid\').selectedIndex != 0;']); echo html_writer::input_hidden_params($url, ['action', 'userid']); echo html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()]); echo html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'action', 'value' => 'add']); } echo html_writer::select($options, 'userid', $selected_id); if (!$selectonly) { echo html_writer::empty_tag('input', ['type' => 'submit', 'value' => $buttonlabel]); echo html_writer::end_tag('form'); } } else { if (!$selectonly) { echo html_writer::start_tag('form'); } echo html_writer::select($options, 'userid', null, null, ['disabled' => 'disabled']); if (!$selectonly) { echo html_writer::empty_tag('input', ['type' => 'submit', 'value' => $buttonlabel, 'disabled' => 'disabled']); echo html_writer::tag('div', get_string('nomoreusers', 'opencast')); echo html_writer::end_tag('form'); } } }
/** * Sends an API request to the NEW Matterhorn server * * @param $url * @param string $request_type request type * @param array $data input data for POST/PUT * @param boolean $return_rawdata return raw XML? * @param boolean $usecache try to use cache? * @param string $file video file to upload * @param bool $runas run the API request as the current logged in user? * @param bool $haltonerror halt on error, or just return FALSE * * @return bool|stdClass|array result object or false if error * @throws moodle_exception */ static function sendRequest($url, $request_type, $data = null, $return_rawdata = false, $usecache = true, $file = null, $runas = true, $haltonerror = true) { global $CFG, $USER; $request_url = mod_opencast_series::getValueForKey('switch_api_host') . $url; $cache_time = mod_opencast_series::getValueForKey('local_cache_time'); $cache_dir = $CFG->dataroot . '/cache/mod_opencast'; if ($request_type !== 'GET') { // a modification has been made, clear the cache for consistency $reason = $request_type . ' ' . $request_url; $matches = false; if (preg_match('/\\/series\\/([0-9a-zA-Z]+)/', $request_url, $matches)) { // TODO Check because not working when deleting an event mod_opencast_log::write("CACHE : destroying cache for series " . $matches[1] . " because " . $reason); self::clear_cache($cache_dir, $matches[1]); } else { // No need to destroy the cache, the requests not containning "/series/xyzxyz" have no effect on clip/series metadata // TODO yes I think we do! BUT NOT ALWAYS! e.g. not needed when /sign mod_opencast_log::write("CACHE : destroying entire cache because " . $reason); self::clear_cache($cache_dir); } } if (!file_exists($cache_dir)) { mod_opencast_log::write("CACHE : initializing empty cache"); mkdir($cache_dir); } if (is_array($data) || is_object($data)) { $data_json = json_encode($data); } mod_opencast_log::write("REQUEST " . $request_type . " " . $request_url); mod_opencast_log::write("INPUT " . print_r($data, true)); $cache_filename = $cache_dir . '/' . self::hashfilename($request_url); if ($usecache && (string) $request_type === 'GET' && $cache_time && $cache_dir && file_exists($cache_filename) && time() - filemtime($cache_filename) < $cache_time) { // use the appropriate cached file mod_opencast_log::write("CACHE : using cached file " . $cache_filename); $output = file_get_contents($cache_filename); } else { // no cache for this request mod_opencast_log::write("CACHE : no cached file"); libxml_use_internal_errors(true); $curl_request = curl_init(); curl_setopt($curl_request, CURLOPT_SAFE_UPLOAD, true); curl_setopt($curl_request, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl_request, CURLOPT_CUSTOMREQUEST, $request_type); if ($runas) { $role_user = '******' . mod_opencast_user::getExtIdFromMoodleUserId($USER->id); curl_setopt($curl_request, CURLOPT_HTTPHEADER, ['X-RUN-WITH-ROLES: ' . $role_user]); } else { curl_setopt($curl_request, CURLOPT_HTTPHEADER, ['X-RUN-WITH-ROLES: ROLE_EXTERNAL_APPLICATION']); } if (isset($file)) { if (!filesize($file) || !is_readable($file)) { mod_opencast_log::write("CURL UPLOAD ERROR : empty or unreadable file"); if ($haltonerror) { throw new moodle_exception('uploaderror', 'opencast'); } return false; } $curl_file = new CURLFile($file); curl_setopt($curl_request, CURLOPT_TIMEOUT_MS, 300000); // curl_setopt($curl_request, CURLOPT_PUT, true); // must be set, elsewise the multipart info will also be sent // curl_setopt($curl_request, CURLOPT_BINARYTRANSFER, 1); // curl_setopt($curl_request, CURLOPT_VERBOSE, (bool)mod_opencast_obj::getValueForKey('logging_enabled')); } else { curl_setopt($curl_request, CURLOPT_TIMEOUT_MS, (int) mod_opencast_series::getValueForKey('curl_timeout') * 1000); } curl_setopt($curl_request, CURLOPT_SSL_VERIFYPEER, false); // TODO WAIT FOR SWITCH : no signing for now (test phase) curl_setopt($curl_request, CURLOPT_USERPWD, mod_opencast_series::getValueForKey('switch_api_username') . ':' . mod_opencast_series::getValueForKey('switch_api_password')); curl_setopt($curl_request, CURLOPT_URL, $request_url); if (mod_opencast_series::getValueForKey('curl_proxy')) { curl_setopt($curl_request, CURLOPT_PROXY, mod_opencast_series::getValueForKey('curl_proxy')); } if (is_array($data)) { $postfields = []; if ($file) { $postfields['presentation'] = $curl_file; } foreach ($data as $key => $value) { $postfields[$key] = $value; // htmlentities($value, ENT_NOQUOTES); // not necessary anymore } curl_setopt($curl_request, CURLOPT_POSTFIELDS, $postfields); curl_setopt($curl_request, CURLOPT_HTTPHEADER, ['Accept: application/v1.0.0+json']); } $output = curl_exec($curl_request); $curl_errno = curl_errno($curl_request); // 0 if fine $response_details = curl_getinfo($curl_request); if (isset($file)) { if ($curl_errno) { curl_close($curl_request); mod_opencast_log::write("CURL UPLOAD ERROR : no. " . $curl_errno); mod_opencast_log::write(" : " . $output); if ($haltonerror) { throw new moodle_exception('uploaderror', 'opencast'); } return false; } } curl_close($curl_request); if ($output && (string) $request_type === 'GET' && (isset($response_details) && $response_details['http_code'] < 400) && ($cache_time && $cache_dir && is_writable($cache_dir))) { // write cache to file, only if response is not an error mod_opencast_log::write("CACHE : writing output to cache file " . $cache_filename); $fh_w = fopen($cache_filename, 'w'); fwrite($fh_w, $output); fclose($fh_w); // TODO SOMETIME : here, see if we can do sth like the following (i.e. cache all events from 1 API call) // if (strstr($request_url, 'clips.xml?full=true') !== false) { // // we're getting full clip matadata (woohoo!), so let's fill in the cache // // on these clips, before making any further API calls. // $channelfull = new SimpleXMLElement($output); // foreach ($channelfull as $clipxml) { // $clip_request_url = // preg_replace('/^(.*)clips\.xml\?full=true.*/', '\1clips/' . $clipxml->ext_id . '.xml', // $request_url); // $cache_clip_filename = $cache_dir . '/' . self::hashfilename($clip_request_url); // $fh_w = fopen($cache_clip_filename, 'w'); // fwrite($fh_w, $clipxml->asXML()); // fclose($fh_w); // } // } } } if ($return_rawdata) { return $output; } if ($output === false) { if ($curl_errno) { mod_opencast_log::write("CURL REQUEST ERROR : no. " . $curl_errno); } if ($haltonerror) { print_error('switch_api_down', 'opencast'); } return false; } mod_opencast_log::write("OUTPUT " . $output); // $output = html_entity_decode($output, ENT_NOQUOTES); // not necessary anymore try { $return = json_decode($output); } catch (Exception $e) { if ($haltonerror) { print_error('api_fail', 'opencast', null, $e->getMessage() . $e->getCode()); } return false; } if (isset($response_details) && $response_details['http_code'] >= 400) { if ($response_details['http_code'] == 404) { if ($haltonerror) { print_error('api_404', 'opencast', null, $response_details['http_code']); } } if ($haltonerror) { print_error('api_fail', 'opencast', null, $response_details['http_code']); } return false; } return $return; }
/** * Checks the current USER's permission on the event * * @param string $perm_type permission : 'read' or 'write' * * @return bool true if permission granted */ public function isAllowed($perm_type) { global $DB, $USER, $context; if (!has_capability('mod/opencast:use', $context)) { return false; } $mod_opencast_user = new mod_opencast_user(); $user_uploaded_events = $DB->get_records('opencast_uploadedclip', ['userid' => $USER->id]); $user_uploaded_events_extids = []; if (is_array($user_uploaded_events)) { foreach ($user_uploaded_events as $user_uploaded_event) { $user_uploaded_events_extids[] = $user_uploaded_event->ext_id; } } if ($perm_type == 'write') { if (has_capability('mod/opencast:isproducer', $context) || $mod_opencast_user->getExternalAccount() == $this->getOwner() && $this->getOwner() !== '' || in_array($this->getExtId(), $user_uploaded_events_extids)) { /* * the current $USER is channel producer * OR the current $USER is the clip owner * OR the current $USER is the user who uploaded the clip */ return true; } } else { if ($perm_type == 'read') { if (has_capability('mod/opencast:isproducer', $context) || has_capability('mod/opencast:seeallclips', $context) || $this->series->getIvt() && $this->getOwner() !== '' && $mod_opencast_user->getExternalAccount() == $this->getOwner() || $this->series->getIvt() == false || $this->series->getIvt() == true && $this->series->getInvitingPossible() == true && is_numeric(array_search($USER->id, $this->getMembers())) || mod_opencast_user::checkSameGroup(mod_opencast_user::getMoodleUserIdFromExtId($this->getOwner()), $USER->id) || in_array($this->getExtId(), $user_uploaded_events_extids)) { /* * the current $USER is channel producer * the current $USER has the mod/opencast:seeallclips capability * OR activity is set in individual mode AND the current $USER is the clip owner * OR there are no individual clip permissions set for this activity * OR activity is set in individual mode AND $USER is an invited member of a clip * OR is in the same user group as the clip owner * OR the current $USER is the user who uploaded the clip */ return true; } } } return false; }
if (in_array($action, ['edit']) && confirm_sesskey() && has_capability('mod/opencast:isproducer', $context)) { /* * $confirm * AND sesskey() ok * AND has producer rights */ if ($action === 'edit') { $sc_clip->setTitle(optional_param('title', $sc_clip->getTitle(), PARAM_RAW_TRIMMED)); $sc_clip->setSubtitle(optional_param('subtitle', $sc_clip->getSubtitle(), PARAM_RAW_TRIMMED)); $sc_clip->setPresenter(optional_param('presenter', $sc_clip->getPresenter(), PARAM_RAW_TRIMMED)); $sc_clip->setLocation(optional_param('location', $sc_clip->getLocation(), PARAM_RAW_TRIMMED)); if ($userid !== 0) { if ($userid == -1) { $sc_clip->setOwner(''); } else { $sc_user = new mod_opencast_user(null, $userid); $newowner_aaiUniqueId = $sc_user->getExternalAccount(); if ($newowner_aaiUniqueId) { $newowner = new mod_opencast_user($newowner_aaiUniqueId); $sc_clip->setOwner($newowner_aaiUniqueId); $sc_clip->update(); } else { print_error('owner_no_switch_account', 'opencast', $url, $setuser->lastname . ', ' . $setuser->firstname); } } } $sc_clip->update(); $eventparams = ['context' => $context, 'objectid' => $opencast->id]; $event = \mod_opencast\event\clip_editdetails::create($eventparams); $event->add_record_snapshot('course_modules', $cm); $event->add_record_snapshot('course', $course);
} if (!($course = $DB->get_record("course", ["id" => $cm->course]))) { print_error('coursemisconf'); } $return_course = new moodle_url('/course/view.php', ['id' => $course->id]); require_course_login($course, false, $cm); if (!($opencast = opencast_get_opencast($cm->instance))) { print_error('invalidcoursemodule', null, $return_course); } if (!($context = context_module::instance($cm->id))) { print_error('badcontext', null, $return_course); } $allclips = []; $sc_obj = new mod_opencast_series(); $sc_obj->fetch($opencast->id, true); $sc_user = new mod_opencast_user(); $arr_filter = []; $filters = explode('&', urldecode($filterstr)); foreach ($filters as $filter) { $parts = explode('=', $filter); if (count($parts) == 2) { $arr_filter[$parts[0]] = $parts[1]; } } $xml_clips = $sc_obj->getEvents($arr_filter); $xml_clips_access_allowed = $sc_obj->checkAccess($xml_clips); $clips = []; foreach ($xml_clips_access_allowed as $xml_clip) { $clips[] = (array) $xml_clip; } if (mod_opencast_series::getValueForKey('display_select_columns')) {
/** * To create a channel we need an aai account that is allowed to register a new channel. * Thus the first choice is the aai account of the current user, if he doesn't have an * account we use the system account. */ function doCreate() { global $USER; $scuser = new mod_opencast_user(); // if the current USER has no switchaai account, prevent channel creation if ($scuser->getExternalAccount() == '') { print_error('user_notaai', 'opencast'); } if ($this->getExtId() == '') { // No ext_id: that's a new channel to be created at SWITCHcast server $url = '/series'; $data = ['metadata' => json_encode([['label' => 'Opencast Series DublinCore', 'flavor' => 'dublincore/series', 'fields' => [['id' => 'title', 'value' => $this->title]]]], JSON_UNESCAPED_SLASHES), 'acl' => json_encode([['allow' => true, 'action' => 'read', 'role' => 'ROLE_ORG_PRODUCER'], ['allow' => true, 'action' => 'write', 'role' => 'ROLE_ORG_PRODUCER'], ['allow' => true, 'action' => 'read', 'role' => 'ROLE_EXTERNAL_APPLICATION'], ['allow' => true, 'action' => 'write', 'role' => 'ROLE_EXTERNAL_APPLICATION'], ['allow' => true, 'action' => 'read', 'role' => 'ROLE_AAI_USER_' . $scuser->getExternalAccount()], ['allow' => true, 'action' => 'write', 'role' => 'ROLE_AAI_USER_' . $scuser->getExternalAccount()]], JSON_UNESCAPED_SLASHES)]; $new_series = mod_opencast_apicall::sendRequest($url, 'POST', $data); // Check ext_id if ($new_series->identifier) { $this->setExtId($new_series->identifier); } else { print_error('errorchannelcreation', 'opencast'); } } else { // existing channel at SWITCHcast server, to be updated // basically, we only add our sysAccount as producer at the SWITCHcast server $this->update(); } }
print_error('fileis_notavideo', 'opencast', $url, $file->get_mimetype()); } $filename = $file->get_filename(); preg_match('/\\.([^.]+)$/', $filename, $extension); if (!in_array(strtolower($extension[1]), mod_opencast_series::getAllowedFileExtensions())) { $file->delete(); $a = new stdClass(); $a->yours = $extension[1]; $a->allowed = implode(', ', mod_opencast_series::getAllowedFileExtensions()); print_error('fileis_notextensionallowed', 'opencast', $url, $a); } $filetoupload = $CFG->dataroot . '/temp/files/mod_opencast_' . md5(microtime()) . '.' . $extension[1]; $a_file = $file->copy_content_to_temp(); rename($a_file, $filetoupload); try { $result = $sc_obj->createClip(['title' => $formdata->cliptitle, 'subtitle' => $formdata->clipsubtitle, 'presenter' => $formdata->clippresenter, 'location' => $formdata->cliplocation, 'ivt__owner' => mod_opencast_user::getExtIdFromMoodleUserId($USER->id), 'filename' => $filetoupload]); } catch (Exception $e) { unlink($filetoupload); $file->delete(); $retryurl = new moodle_url($url, ['formdata' => serialize($formdata)]); print_error('userupload_error', 'opencast', $retryurl); } unlink($filetoupload); $file->delete(); } } } if (isset($formdata) && isset($result)) { // data submitted: record file upload $uploaded_clip = new stdClass(); $uploaded_clip->userid = $USER->id;
function validation($data, $files) { global $DB; $errors = parent::validation($data, $files); $scuser = new mod_opencast_user(); if ($data['channelnew'] == OPENCAST_CHANNEL_NEW) { if ($scuser->getExternalAccount() == '') { $errors['channelnew'] = get_string('user_notaai', 'opencast'); } if (!$data['newchannelname']) { $errors['newchannelname'] = get_string('required'); } } if ($data['channelnew'] == OPENCAST_CHANNEL_EXISTING) { // make sure we can be external_authority for this channel $scobj = new mod_opencast_series(); $ext_id = isset($data['ext_id']) ? $data['ext_id'] : $this->current->ext_id; $scobj->setExtId($ext_id); $sysaccount_extid = mod_opencast_series::getSysAccountOfUser(); // we must explicitly set $USER as a producer in $scobj or we won't be allowed to add his system_user $scobj->setOrganizationDomain(mod_opencast_series::getOrganizationByEmail($sysaccount_extid)); $scobj->setProducer($scuser->getExternalAccount()); // first, add SysAccount as producer (using $USER account), so we can use SysAccount later to make API calls // $scobj->addProducer($sysaccount_extid, false); $channelid = empty($this->_instance) ? $ext_id : $this->current->id; // if there already is one instance we must refer to it by its Moodle ID otherwise there could // be several records! $thechannel = $scobj->fetch($channelid, !empty($this->_instance), true); } else { if ($data['groupmode'] != NOGROUPS && !$data['is_ivt']) { $errors['groupmode'] = get_string('nogroups_withoutivt', 'opencast'); } } return $errors; }