/** * Create method for this handler * * @return array of assets created **/ public function create() { // Include needed files require_once dirname(dirname(__DIR__)) . DS . 'tables' . DS . 'asset.association.php'; require_once dirname(dirname(__DIR__)) . DS . 'tables' . DS . 'asset.php'; require_once dirname(__DIR__) . DS . 'asset.php'; // Get the file if (isset($_FILES['files'])) { $file = $_FILES['files']['name'][0]; $size = (int) $_FILES['files']['size']; // Get the file extension $pathinfo = pathinfo($file); $filename = $pathinfo['filename']; $ext = $pathinfo['extension']; } else { return array('error' => 'No files provided'); } // @FIXME: should these come from the global settings, or should they be courses specific // Get config $config = Component::params('com_media'); // Max upload size $sizeLimit = (int) $config->get('upload_maxsize'); $sizeLimit = $sizeLimit * 1024 * 1024; // Check to make sure we have a file and its not too big if ($size == 0) { return array('error' => 'File is empty'); } if ($size > $sizeLimit) { $max = preg_replace('/<abbr \\w+=\\"\\w+\\">(\\w{1,3})<\\/abbr>/', '$1', \Hubzero\Utility\Number::formatBytes($sizeLimit)); return array('error' => "File is too large. Max file upload size is {$max}"); } // Create our asset table object $assetObj = new Tables\Asset($this->db); $this->asset['title'] = $filename; $this->asset['type'] = !empty($this->asset['type']) ? $this->asset['type'] : 'file'; $this->asset['subtype'] = !empty($this->asset['subtype']) ? $this->asset['subtype'] : 'file'; $this->asset['url'] = $file; $this->asset['created'] = Date::toSql(); $this->asset['created_by'] = App::get('authn')['user_id']; $this->asset['course_id'] = Request::getInt('course_id', 0); // Save the asset if (!$assetObj->save($this->asset)) { return array('error' => 'Asset save failed'); } // Create asset assoc object $assocObj = new Tables\AssetAssociation($this->db); $this->assoc['asset_id'] = $assetObj->get('id'); $this->assoc['scope'] = Request::getCmd('scope', 'asset_group'); $this->assoc['scope_id'] = Request::getInt('scope_id', 0); // Save the asset association if (!$assocObj->save($this->assoc)) { return array('error' => 'Asset association save failed'); } // Get courses config $cconfig = Component::params('com_courses'); // Build the upload path if it doesn't exist $uploadDirectory = PATH_APP . DS . trim($cconfig->get('uploadpath', '/site/courses'), DS) . DS . $this->asset['course_id'] . DS . $this->assoc['asset_id'] . DS; // Make sure upload directory exists and is writable if (!is_dir($uploadDirectory)) { if (!Filesystem::makeDirectory($uploadDirectory, 0755, true)) { return array('error' => 'Server error. Unable to create upload directory'); } } if (!is_writable($uploadDirectory)) { return array('error' => 'Server error. Upload directory isn\'t writable'); } // Get the final file path $target_path = $uploadDirectory . $filename . '.' . $ext; // Move the file to the site folder set_time_limit(60); // Scan for viruses if (!Filesystem::isSafe($_FILES['files']['tmp_name'][0])) { // Scan failed, delete asset and association and return an error $assetObj->delete(); $assocObj->delete(); Filesystem::deleteDirectory($uploadDirectory); return array('error' => 'File rejected because the anti-virus scan failed.'); } if (!($move = move_uploaded_file($_FILES['files']['tmp_name'][0], $target_path))) { // Move failed, delete asset and association and return an error $assetObj->delete(); $assocObj->delete(); Filesystem::deleteDirectory($uploadDirectory); return array('error' => 'Move file failed'); } // Get the url to return to the page $course_id = Request::getInt('course_id', 0); $offering_alias = Request::getCmd('offering', ''); $course = new \Components\Courses\Models\Course($course_id); $url = Route::url('index.php?option=com_courses&controller=offering&gid=' . $course->get('alias') . '&offering=' . $offering_alias . '&asset=' . $assetObj->get('id')); $url = rtrim(str_replace('/api', '', Request::root()), '/') . '/' . ltrim($url, '/'); $return_info = array('asset_id' => $this->assoc['asset_id'], 'asset_title' => $this->asset['title'], 'asset_type' => $this->asset['type'], 'asset_subtype' => $this->asset['subtype'], 'asset_url' => $url, 'course_id' => $this->asset['course_id'], 'offering_alias' => Request::getCmd('offering', ''), 'scope_id' => $this->assoc['scope_id'], 'asset_ext' => $ext, 'upload_path' => $uploadDirectory, 'target_path' => $target_path); // Return info return array('assets' => $return_info); }
/** * Reorders assets * * @apiMethod POST * @apiUri /courses/asset/reorder * @apiParameter { * "name": "asset", * "description": "Array of IDs of assets to reorder", * "type": "array", * "required": true, * "default": null * } * @apiParameter { * "name": "scope", * "description": "Asset scope", * "type": "string", * "required": true, * "default": null * } * @apiParameter { * "name": "scope_id", * "description": "Asset scope ID", * "type": "integer", * "required": true, * "default": null * } * @return void */ public function reorderTask() { // Get our asset group object $database = App::get('db'); $assetAssocationObj = new AssetAssociation($database); $assets = Request::getVar('asset', array()); $scope_id = Request::getInt('scope_id', 0); $scope = Request::getWord('scope', 'asset_group'); $order = 1; foreach ($assets as $asset_id) { if (!$assetAssocationObj->loadByAssetScope($asset_id, $scope_id, $scope)) { App::abort(500, "Loading asset association {$asset_id} failed"); } // Save the asset group if (!$assetAssocationObj->save(array('ordering' => $order))) { App::abort(500, 'Asset asssociation save failed'); } $order++; } // Return message $this->send('New asset order saved'); }
/** * Copy an entry and associated data * * @param integer $offering_id New offering to copy to * @param boolean $deep Copy associated data? * @return boolean True on success, false on error */ public function copy($offering_id = null, $deep = true) { // Get some old info we may need // - Unit ID // - Offering ID $u_id = $this->get('id'); $o_id = $this->get('offering_id'); // Reset the ID. This will force store() to create a new record. $this->set('id', 0); // Are we copying to a new offering? if ($offering_id) { $this->set('offering_id', $offering_id); } else { // Copying to the same offering so we want to distinguish // this unit from the one we copied from $this->set('title', $this->get('title') . ' (copy)'); $this->set('alias', $this->get('alias') . '_copy'); } if (!$this->store()) { return false; } if ($deep) { // Copy assets $tbl = new Tables\AssetAssociation($this->_db); //foreach ($this->assets(array('asset_scope_id' => $u_id)) as $asset) foreach ($tbl->find(array('scope_id' => $u_id, 'scope' => 'unit')) as $asset) { $tbl->bind($asset); $tbl->id = 0; $tbl->scope_id = $this->get('id'); //if (!$asset->copy($this->get('id'))) if (!$tbl->store()) { $this->setError($tbl->getError()); } } // Copy asset groups foreach ($this->assetgroups(null, array('unit_id' => $u_id, 'parent' => 0)) as $assetgroup) { if (!$assetgroup->copy($this->get('id'), $deep)) { $this->setError($assetgroup->getError()); } } } return true; }
/** * Cancel a course page task * * @return void */ public function reorderTask() { // Check for request forgeries Request::checkToken(); $move = $this->_task == 'orderup' ? -1 : 1; // Incoming $id = Request::getVar('id', array()); $id = $id[0]; $tmpl = Request::getVar('tmpl', ''); $scope = Request::getVar('scope', 'asset_group'); $scope_id = Request::getInt('scope_id', 0); $course_id = Request::getInt('course_id', 0); // Get the element moving down - item 1 $tbl = new Tables\AssetAssociation($this->database); $tbl->loadByAssetScope($id, $scope_id, $scope); if (!$tbl->move($move, "scope=" . $this->database->Quote($scope) . " AND scope_id=" . $this->database->Quote(intval($scope_id)))) { echo $tbl->getError(); return; } App::redirect(Route::url('index.php?option=' . $this->_option . '&controller=' . $this->_controller . '&tmpl=' . $tmpl . '&scope=' . $scope . '&scope_id=' . $scope_id . '&course_id=' . $course_id, false)); }
/** * Copy an entry and associated data * * @param integer $course_id New course to copy to * @param boolean $deep Copy associated data? * @return boolean True on success, false on error */ public function copy($course_id = null, $deep = true) { // Get some old info we may need // - Offering ID // - Course ID $o_id = $this->get('id'); $c_id = $this->get('course_id'); $oldOfferingAssets = $this->assets(); // Reset the ID. This will force store() to create a new record. $this->set('id', 0); // Are we copying to a new course? if ($course_id) { $this->set('course_id', $course_id); } else { // Copying to the same course so we want to distinguish // this offering from the one we copied from $this->set('title', $this->get('title') . ' (copy)'); $this->set('alias', $this->get('alias') . '_copy'); } if (!$this->store()) { return false; } if ($deep) { // Copy pages foreach ($this->pages(array('offering_id' => $o_id, 'active' => array(0, 1)), true) as $page) { if (!$page->copy($this->get('course_id'), $this->get('id'))) { $this->setError($page->getError()); } } // Copy units foreach ($this->units(array('offering_id' => $o_id, 'section_id' => -1), true) as $unit) { if (!$unit->copy($this->get('id'))) { $this->setError($unit->getError()); } } // Copy logo if ($file = $this->logo('file')) { $src = DS . trim($this->config('uploadpath', '/site/courses'), '/') . DS . $c_id . '/offerings/' . $o_id . DS . $file; if (file_exists(PATH_APP . $src)) { $dest = DS . trim($this->config('uploadpath', '/site/courses'), '/') . DS . $this->get('course_id') . '/offerings/' . $this->get('id'); if (!is_dir(PATH_APP . $dest)) { if (!Filesystem::makeDirectory(PATH_APP . $dest)) { $this->setError(Lang::txt('UNABLE_TO_CREATE_UPLOAD_PATH')); } } $dest .= DS . $file; if (!Filesystem::copy(PATH_APP . $src, PATH_APP . $dest)) { $this->setError(Lang::txt('Failed to copy offering logo.')); } } } // Copy assets (grab the assets from the original offering) if ($oldOfferingAssets) { foreach ($oldOfferingAssets as $asset) { $oldAssetId = $asset->get('id'); if (!$asset->copy()) { $this->setError($asset->getError()); } else { // Copy asset associations $tbl = new Tables\AssetAssociation($this->_db); foreach ($tbl->find(array('scope_id' => $o_id, 'scope' => 'offering', 'asset_id' => $oldAssetId)) as $aa) { $tbl->bind($aa); $tbl->id = 0; $tbl->scope_id = $this->get('id'); $tbl->asset_id = $asset->get('id'); if (!$tbl->store()) { $this->setError($tbl->getError()); } } } } } } return true; }
/** * Get a count or list of parents for this entry * * @param array $filters Fitlers to apply to results query * @return array */ public function parents($filters = array()) { if (!isset($filters['asset_id'])) { $filters['asset_id'] = (int) $this->get('id'); } $tbl = new Tables\AssetAssociation($this->_db); if (isset($filters['count']) && $filters['count']) { return $tbl->count($filters); } if (!($results = $tbl->find($filters))) { $results = array(); } return $results; }
/** * Create method for this handler * * @return array of assets created **/ public function create() { // Include needed files require_once dirname(dirname(__DIR__)) . DS . 'tables' . DS . 'asset.association.php'; require_once dirname(dirname(__DIR__)) . DS . 'tables' . DS . 'asset.php'; require_once dirname(__DIR__) . DS . 'asset.php'; // Get the file if (isset($_FILES['files'])) { $file = $_FILES['files']['name'][0]; $size = (int) $_FILES['files']['size']; // Get the file extension $pathinfo = pathinfo($file); $filename = $pathinfo['filename']; $ext = $pathinfo['extension']; } else { return array('error' => 'No files provided'); } // @FIXME: should these come from the global settings, or should they be courses specific // Get config $config = Component::params('com_media'); // Max upload size $sizeLimit = (int) $config->get('upload_maxsize'); $sizeLimit = $sizeLimit * 1024 * 1024; // Check to make sure we have a file and its not too big if ($size == 0) { return array('error' => 'File is empty'); } if ($size > $sizeLimit) { $max = preg_replace('/<abbr \\w+=\\"\\w+\\">(\\w{1,3})<\\/abbr>/', '$1', \Hubzero\Utility\Number::formatBytes($sizeLimit)); return array('error' => "File is too large. Max file upload size is {$max}"); } // get request vars $course_id = Request::getInt('course_id', 0); $offering_alias = Request::getCmd('offering', ''); $scope = Request::getCmd('scope', 'asset_group'); $scope_id = Request::getInt('scope_id', 0); // get all assets in group $assetGroup = new \Components\Courses\Models\Assetgroup($scope_id); $assets = $assetGroup->assets(); // check to see if any of our assets are html5? $hubpresenter = null; foreach ($assets as $asset) { if ($asset->get('type') == 'video' && $asset->get('subtype') == 'video' && in_array($asset->get('state'), array(0, 1)) && strpos($asset->get('url'), 'zip')) { $hubpresenter = $asset; break; } } // make sure we have asset if ($hubpresenter === null) { return array('error' => 'Unable to locate html5 video or hubpresenter asset to attach subtitle file to.'); } // build path to asset $pathToAsset = $hubpresenter->path($course_id); $pathToAssetFolder = trim(dirname($pathToAsset), DS); // build target path $target_path = PATH_APP . DS . $pathToAssetFolder . DS . $filename . '.' . $ext; // Move the file to the site folder set_time_limit(60); // Scan for viruses if (!Filesystem::isSafe($_FILES['files']['tmp_name'][0])) { // Scan failed, return an error return array('error' => 'File rejected because the anti-virus scan failed.'); } // move file if (!($move = move_uploaded_file($_FILES['files']['tmp_name'][0], $target_path))) { // Move failed, delete asset and association and return an error return array('error' => 'Move file failed'); } // get json file $jsonFile = $pathToAssetFolder . DS . 'presentation.json'; // get manifest $manifest = file_get_contents(PATH_APP . DS . $jsonFile); $manifest = json_decode($manifest); // make sure we have a subtitles section $currentSubtitles = array(); if (!isset($manifest->presentation->subtitles)) { $manifest->presentation->subtitles = array(); } else { foreach ($manifest->presentation->subtitles as $subtitle) { $currentSubtitles[] = $subtitle->source; } } // create subtitle details based on filename $info = pathinfo($file); $name = str_replace('-auto', '', $info['filename']); $autoplay = strstr($info['filename'], '-auto') ? 1 : 0; $source = $file; // use only the last segment from name (ex. ThisIsATest.English => English) $nameParts = explode('.', $name); $name = array_pop($nameParts); // add subtitle $subtitle = new stdClass(); $subtitle->type = 'SRT'; $subtitle->name = ucfirst($name); $subtitle->source = $source; $subtitle->autoplay = $autoplay; // only add sub if we dont already have it if (!in_array($subtitle->source, $currentSubtitles)) { $manifest->presentation->subtitles[] = $subtitle; } // update json file file_put_contents(PATH_APP . DS . $jsonFile, json_encode($manifest, JSON_PRETTY_PRINT)); //parse subtitle file $lines = self::_parseSubtitleFile($target_path); // make transcript file $transcript = ''; foreach ($lines as $line) { $transcript .= ' ' . trim($line->text); } // trim transcript and replace add slide markers $transcript = str_replace(array("\r\n", "\n"), array('', ''), $transcript); $transcript = preg_replace("/\\[([^\\]]*)\\]/ux", "\n\n[\$1]", $transcript); // add title to transcript $transcript = $manifest->presentation->title . PHP_EOL . str_repeat('==', 20) . PHP_EOL . ltrim($transcript, PHP_EOL); // Create our asset table object $assetObj = new Tables\Asset($this->db); $this->asset['title'] = 'Video Transcript'; $this->asset['type'] = 'file'; $this->asset['subtype'] = 'file'; $this->asset['url'] = $info['filename'] . '.txt'; $this->asset['created'] = Date::toSql(); $this->asset['created_by'] = App::get('authn')['user_id']; $this->asset['course_id'] = $course_id; // Save the asset if (!$assetObj->save($this->asset)) { return array('error' => 'Asset save failed'); } // Create asset assoc object $assocObj = new Tables\AssetAssociation($this->db); $this->assoc['asset_id'] = $assetObj->get('id'); $this->assoc['scope'] = $scope; $this->assoc['scope_id'] = $scope_id; // Save the asset association if (!$assocObj->save($this->assoc)) { return array('error' => 'Asset association save failed'); } // Get courses config $cconfig = Component::params('com_courses'); // Build the upload path if it doesn't exist $uploadDirectory = PATH_APP . DS . trim($cconfig->get('uploadpath', '/site/courses'), DS) . DS . $this->asset['course_id'] . DS . $this->assoc['asset_id'] . DS; // Make sure upload directory exists and is writable if (!is_dir($uploadDirectory)) { if (!Filesystem::makeDirectory($uploadDirectory)) { return array('error' => 'Server error. Unable to create upload directory'); } } if (!is_writable($uploadDirectory)) { return array('error' => 'Server error. Upload directory isn\'t writable'); } // Get the final file path $transcript_target_path = $uploadDirectory . $this->asset['url']; // make transcript file file_put_contents($transcript_target_path, $transcript); // Get the url to return to the page $course_id = Request::getInt('course_id', 0); $offering_alias = Request::getCmd('offering', ''); $course = new \Components\Courses\Models\Course($course_id); $url = Route::url('index.php?option=com_courses&controller=offering&gid=' . $course->get('alias') . '&offering=' . $offering_alias . '&asset=' . $assetObj->get('id')); // build return info $return_info = array('asset_id' => $this->assoc['asset_id'], 'asset_title' => $this->asset['title'], 'asset_type' => $this->asset['type'], 'asset_subtype' => $this->asset['subtype'], 'asset_url' => $url, 'course_id' => $this->asset['course_id'], 'offering_alias' => Request::getCmd('offering', ''), 'scope_id' => $this->assoc['scope_id'], 'asset_ext' => 'txt', 'upload_path' => $uploadDirectory, 'target_path' => $transcript_target_path); // Return info return array('assets' => $return_info); }
/** * Save method for this handler * // @FIXME: reduce code duplication here * * @return array of assets created **/ public function save() { // Include needed files require_once PATH_CORE . DS . 'components' . DS . 'com_courses' . DS . 'models' . DS . 'asset.php'; // Create our asset object $id = Request::getInt('id', null); $asset = new \Components\Courses\Models\Asset($id); // Grab the incoming content $content = Request::getVar('content', '', 'default', 'none', 2); // Get everything ready to store // Check if vars are already set (i.e. by a sub class), before setting them here $asset->set('title', !empty($this->asset['title']) ? $this->asset['title'] : strip_tags(substr($content, 0, 25))); $asset->set('type', !empty($this->asset['type']) ? $this->asset['type'] : 'text'); $asset->set('subtype', !empty($this->asset['subtype']) ? $this->asset['subtype'] : 'content'); $asset->set('content', !empty($this->asset['content']) ? $this->asset['content'] : $content); // If we have a state coming in as an int if ($graded = Request::getInt('graded', false)) { $asset->set('graded', $graded); // By default, weight asset as a 'homework' type $grade_weight = $asset->get('grade_weight'); if (empty($grade_weight)) { $asset->set('grade_weight', 'homework'); } else { $asset->set('grade_weight', $grade_weight); } } elseif ($graded = Request::getInt('edit_graded', false)) { $asset->set('graded', 0); } // If we're saving progress calculation var if ($progress = Request::getInt('progress_factors', false)) { $asset->set('progress_factors', array('asset_id' => $asset->get('id'), 'section_id' => Request::getInt('section_id', 0))); } elseif (Request::getInt('edit_progress_factors', false)) { $asset->set('section_id', Request::getInt('section_id', 0)); $asset->set('progress_factors', 'delete'); } // Save the asset if (!$asset->store()) { return array('error' => 'Asset save failed'); } $scope_id = Request::getInt('scope_id', null); $original_scope_id = Request::getInt('original_scope_id', null); $scope = Request::getCmd('scope', 'asset_group'); // Only worry about this if scope id is changing if (!is_null($scope_id) && !is_null($original_scope_id) && $scope_id != $original_scope_id) { // Create asset assoc object require_once PATH_CORE . DS . 'components' . DS . 'com_courses' . DS . 'tables' . DS . 'asset.association.php'; $assoc = new Tables\AssetAssociation($this->db); if (!$assoc->loadByAssetScope($asset->get('id'), $original_scope_id, $scope)) { return array('error' => 'Failed to load asset association'); } // Save the asset association if (!$assoc->save(array('scope_id' => $scope_id))) { return array('error' => 'Asset association save failed'); } } // Get the url to return to the page $course_id = Request::getInt('course_id', 0); $offering_alias = Request::getCmd('offering', ''); $course = new \Components\Courses\Models\Course($course_id); $course->offering($offering_alias); $url = Route::url($course->offering()->link() . '&asset=' . $asset->get('id')); $url = rtrim(str_replace('/api', '', Request::root()), '/') . '/' . ltrim($url, '/'); $files = array('asset_id' => $asset->get('id'), 'asset_title' => $asset->get('title'), 'asset_type' => $asset->get('type'), 'asset_subtype' => $asset->get('subtype'), 'asset_url' => $url, 'asset_state' => $asset->get('state'), 'scope_id' => $scope_id); $return_info = array('asset_id' => $asset->get('id'), 'asset_title' => $asset->get('title'), 'asset_type' => $asset->get('type'), 'asset_subtype' => $asset->get('subtype'), 'asset_url' => $url, 'course_id' => $asset->get('course_id'), 'offering_alias' => $offering_alias, 'scope_id' => $scope_id, 'files' => array($files)); // Return info return array('assets' => $return_info); }