Skip to content

Commit

Permalink
adds attempt to use api endpoint for historical purposes
Browse files Browse the repository at this point in the history
  • Loading branch information
cayb0rg committed Feb 6, 2024
1 parent 7087d21 commit 3980454
Show file tree
Hide file tree
Showing 14 changed files with 307 additions and 131 deletions.
108 changes: 58 additions & 50 deletions fuel/app/classes/controller/widgets.php
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ public function get_preview_widget($inst_id, $is_embedded = false)
* @param string $qset_id (optional) The qset id to export
* @param string $asset (optional) The asset type to export. Can be either 'all', 'qset', or 'media'
*/
public function get_export(string $inst_id, string $asset = 'all')
public function get_export(string $inst_id, string $asset = 'all', string $timestamp = '')
{
// check if instance exists
if ( ! $inst = Materia\Widget_Instance_Manager::get($inst_id)) throw new HttpNotFoundException;
Expand All @@ -256,79 +256,87 @@ public function get_export(string $inst_id, string $asset = 'all')

$filename = preg_replace('/[^a-zA-Z0-9]/', '', $inst->name);

$qset = Materia\Api_V1::question_set_get($inst_id);
$qset = Materia\Api_V1::question_set_get($inst_id, null, $timestamp);
if ($qset instanceof \Materia\Msg) return $qset;

if ($asset == 'qset')
{
// return just the JSON file
header('Content-Type: application/json');
header("Content-Disposition: attachment; filename={$filename}.json");
echo json_encode($qset);
exit;
}

// For all assets, we need to zip them up
$zip = new \ZipArchive();
$zip->open('assets_export', \ZipArchive::CREATE);

if ($asset == 'all' || $asset == 'qset')
if ($asset == 'all')
{
$zip->addFromString('qset.json', json_encode($qset));
}

if ($asset == 'all' || $asset == 'media')
$bytes = 0;
$asset_ids = Materia\Api_V1::assets_get_for_instance($inst_id, false, $qset->id);
$size = 'original';

if (count($asset_ids) == 0)
{
// $zip->addFromString('no_assets.txt', 'No assets found for this widget.');
$zip->setArchiveComment('zipped on '.date('Y-M-d'));
$zip->close();

return new Response('No assets found for this widget.', 403);
}

foreach ($asset_ids as $id)
{
$bytes = 0;
$asset_ids = Materia\Api_V1::assets_get_for_instance($inst_id, false, $qset->id);
$size = 'original';
$asset = Materia\Widget_Asset::fetch_by_id($id);

if (count($asset_ids) == 0)
if ( ! ($asset instanceof Materia\Widget_Asset))
{
// $zip->addFromString('no_assets.txt', 'No assets found for this widget.');
$zip->setArchiveComment('zipped on '.date('Y-M-d'));
$zip->close();

return new Response('No assets found for this widget.', 403);
throw new HttpNotFoundException;
}

foreach ($asset_ids as $id)
try
{
$asset = Materia\Widget_Asset::fetch_by_id($id);

if ( ! ($asset instanceof Materia\Widget_Asset))
// requested size doesnt exist?
$driver = \Config::get('materia.asset_storage_driver', 'db');
$_storage_driver = Materia\Widget_Asset::get_storage_driver($driver);
if ( ! $_storage_driver->exists($asset->id, $size))
{
$zip->close();
throw new HttpNotFoundException;
}
// if size is original, just 404
if ($size === 'original') throw new \Exception("Missing asset data for asset: {$asset->id} {$size}");

try
{
// requested size doesnt exist?
$driver = \Config::get('materia.asset_storage_driver', 'db');
$_storage_driver = Materia\Widget_Asset::get_storage_driver($driver);
if ( ! $_storage_driver->exists($asset->id, $size))
{
// if size is original, just 404
if ($size === 'original') throw new \Exception("Missing asset data for asset: {$asset->id} {$size}");

// rebuild the size (hopefully - we may not )
$asset_path = $asset->build_size($size);
}
else
{
$asset_path = $asset->copy_asset_to_temp_file($asset->id, $size);
}
} catch (\Throwable $e)
// rebuild the size (hopefully - we may not )
$asset_path = $asset->build_size($size);
}
else
{
$zip->close();
throw new \HttpNotFoundException;
$asset_path = $asset->copy_asset_to_temp_file($asset->id, $size);
}
} catch (\Throwable $e)
{
$zip->close();
throw new \HttpNotFoundException;
}

// register a shutdown function that will render the image
// allowing all of fuel's other shutdown methods to do their jobs
\Event::register('fuel-shutdown', function() use($asset_path) {
// register a shutdown function that will render the image
// allowing all of fuel's other shutdown methods to do their jobs
\Event::register('fuel-shutdown', function() use($asset_path) {

if ( ! file_exists($asset_path)) throw new \HttpNotFoundException;
$bytes += filesize($asset_path);
// turn off and clean output buffer
while (ob_get_level() > 0) ob_end_clean();
});
if ( ! file_exists($asset_path)) throw new \HttpNotFoundException;
$bytes += filesize($asset_path);
// turn off and clean output buffer
while (ob_get_level() > 0) ob_end_clean();
});

// Add asset to zip file
$zip->addFromString($asset->title, file_get_contents($asset_path));
}
// Add asset to zip file
$zip->addFromString($asset->title, file_get_contents($asset_path));
}

$zip->setArchiveComment('zipped on '.date('Y-M-d'));
$zip->close();

Expand Down
157 changes: 153 additions & 4 deletions fuel/app/classes/materia/api/v1.php
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,37 @@ static public function widget_instance_update($inst_id=null, $name=null, $qset=n
}
}

/**
* Validates media URLs in qset
* @param object $data
* Returns false if any URL is invalid, true otherwise
*/
static function validate_asset_urls($data)
{
foreach ($data as $key => $value)
{
if ($key === 'url' && is_string($value))
{
// Check if the key is 'url' and the value is a string
// media id must be 5 characters long and end with the ID
$url_regex = '/^https:\/\/\S+\/media\/[A-Za-z0-9]{5}$/';
if ( ! preg_match($url_regex, $value))
{
return false;
}
}
elseif (is_array($value) || is_object($value))
{
// If the value is an array, recursively validate its elements
if ( ! self::validate_asset_urls($value))
{
return false;
}
}
}
return true;
}

/**
* Replace the qset for an instance
* @param int $inst_id
Expand All @@ -398,16 +429,35 @@ static public function widget_instance_update_qset($inst_id, $qset)
if ( ! Util_Validator::is_valid_hash($inst_id)) return new Msg(Msg::ERROR, 'Instance id is invalid');
if ( ! static::has_perms_to_inst($inst_id, [Perm::VISIBLE, Perm::FULL])) return Msg::no_perm();

$inst = Widget_Instance_Manager::get($inst_id, true);
$inst = Widget_Instance_Manager::get($inst_id);
if ( ! $inst) return new Msg(Msg::ERROR, 'Widget instance could not be found.');

if ( ! empty($qset->data) && ! empty($qset->version))
// Validate every single field in the qset
// if any field is invalid, return an error message
// if all fields are valid, update the qset and return the updated instance
if (empty($qset->data) || empty($qset->version))
{
$inst->qset = $qset;
return new Msg(Msg::ERROR, 'Invalid qset');
}
else
{
return new Msg(Msg::ERROR, 'Invalid qset');
$questions = \Materia\Widget_Instance::find_questions($qset->data);
foreach ($questions as $q)
{
if ( ! $q instanceof \Materia\Widget_Question)
{
return new Msg(Msg::ERROR, 'Invalid qset');
}
// Validate whether $q->type (widget name) is same as $inst->widget->id
// (TODO: Might be impossible without changing qset)
}

if ( ! empty($qset->data['items']) && ! self::validate_asset_urls($qset->data['items']))
{
return new Msg(Msg::ERROR, 'Invalid qset');
}

$inst->qset = $qset;
}

try
Expand Down Expand Up @@ -1287,4 +1337,103 @@ static private function _get_instance_for_play_id($play_id)
if ( ! ($inst = Widget_Instance_Manager::get($inst_id))) throw new \HttpNotFoundException;
return $inst;
}

// static public function export_get($inst_id, $timestamp, $all = true)
// {
// // check if instance exists
// if ( ! $inst = Widget_Instance_Manager::get($inst_id)) throw new HttpNotFoundException;
// // check if user has permission to instance
// if ( ! Perm_Manager::user_has_any_perm_to(\Model_User::find_current_id(), $inst_id, Perm::INSTANCE, [Perm::FULL, Perm::VISIBLE]))
// {
// return new Response('You do not have permission to access the requested content.', 403);
// }

// $filename = preg_replace('/[^a-zA-Z0-9]/', '', $inst->name);

// $qset = Api_V1::question_set_get($inst_id, null, $timestamp);
// if ($qset instanceof Msg) return $qset;

// // For all assets, we need to zip them up
// $zip = new \ZipArchive();
// $zip->open('assets_export', \ZipArchive::CREATE);

// $zip->addFromString('qset.json', json_encode($qset));

// $bytes = 0;
// $asset_ids = Api_V1::assets_get_for_instance($inst_id, false, $qset->id);
// $size = 'original';

// if (count($asset_ids) == 0)
// {
// // $zip->addFromString('no_assets.txt', 'No assets found for this widget.');
// $zip->setArchiveComment('zipped on '.date('Y-M-d'));
// $zip->close();

// return new \Response('No assets found for this widget.', 403);
// }

// foreach ($asset_ids as $id)
// {
// $asset = Widget_Asset::fetch_by_id($id);

// if ( ! ($asset instanceof Widget_Asset))
// {
// $zip->close();
// throw new HttpNotFoundException;
// }

// try
// {
// // requested size doesnt exist?
// $driver = \Config::get('materia.asset_storage_driver', 'db');
// $_storage_driver = Widget_Asset::get_storage_driver($driver);
// if ( ! $_storage_driver->exists($asset->id, $size))
// {
// // if size is original, just 404
// if ($size === 'original') throw new \Exception("Missing asset data for asset: {$asset->id} {$size}");

// // rebuild the size (hopefully - we may not )
// $asset_path = $asset->build_size($size);
// }
// else
// {
// $asset_path = $asset->copy_asset_to_temp_file($asset->id, $size);
// }
// } catch (\Throwable $e)
// {
// $zip->close();
// throw new \HttpNotFoundException;
// }

// // register a shutdown function that will render the image
// // allowing all of fuel's other shutdown methods to do their jobs

// \Event::register('fuel-shutdown', function() use($asset_path) {

// if ( ! file_exists($asset_path)) throw new \HttpNotFoundException;
// $bytes = filesize($asset_path);
// // turn off and clean output buffer
// while (ob_get_level() > 0) ob_end_clean();
// });

// // Add asset to zip file
// $zip->addFromString($asset->title, file_get_contents($asset_path));
// }

// $zip->setArchiveComment('zipped on ' . date('Y-M-d'));
// $zip->close();

// $fileSize = filesize('assets_export');

// $response = new \Response(file_get_contents('assets_export'), 200);

// $response->set_header('Content-Type', 'application/zip');
// $response->set_header('Content-Disposition', 'attachment; filename="assets_export.zip"');
// $response->set_header('Content-Length', $fileSize);

// // remove the temporary zip file
// unlink('assets_export');

// return $response;
// }
}
4 changes: 2 additions & 2 deletions fuel/app/classes/materia/widget/instance.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ public function db_get(string $inst_id, bool $load_qset = false, $timestamp = fa
/**
* Load the qset for this instance
*
* @param int $inst_id the id of the widget to load
* @param int $timestamp UnixTimestamp or false, if provided, loads the newest qset before the timestamp
* @param string $inst_id the id of the widget to load
* @param string $timestamp UnixTimestamp or false, if provided, loads the newest qset before the timestamp
*/
public function get_qset(string $inst_id, $timestamp=false)
{
Expand Down
5 changes: 2 additions & 3 deletions fuel/app/config/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@
'scores/semesters?' => 'scores/semesters',
'scores/(:alnum)(/.*)?' => 'scores/show/$1',

'widgets/export/(:alnum)' => 'widgets/export/$1',
'widgets/export/(:alnum)/media' => 'widgets/export/$1/media',
'widgets/export/(:alnum)/qset' => 'widgets/export/$1/qset',
'widgets/export/(:alnum)/all/(:alnum)' => 'widgets/export/$1/all/$2', // export qset + media for specific timestamp
'widgets/export/(:alnum)/media/(:alnum)' => 'widgets/export/$1/media/$2', // export media for specific timestamp

// ================================= MEDIA ======================================

Expand Down
6 changes: 6 additions & 0 deletions fuel/app/tests/api/v1.php
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,12 @@ public function test_widget_instance_update()
$this->assert_validation_error_message($output);
}

public function test_validate_asset_urls()
{
// only here to appease the api coverage
self::assertTrue(true);
}

public function test_widget_instance_update_qset()
{
$this->_as_student();
Expand Down
Loading

0 comments on commit 3980454

Please sign in to comment.