Skip to content

Commit

Permalink
Merge branch 'master' into issue-245
Browse files Browse the repository at this point in the history
  • Loading branch information
bluetom authored Dec 1, 2024
2 parents 8e7d299 + fc9e55b commit fd97c8b
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 34 deletions.
2 changes: 1 addition & 1 deletion amd/build/block_manage_series.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion amd/build/block_manage_series.min.js.map

Large diffs are not rendered by default.

51 changes: 46 additions & 5 deletions amd/src/block_manage_series.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,11 @@ function submitFormAjax(e) {
args: {contextid: contextid, ocinstanceid: e.data.ocinstanceid, seriesid: e.data.seriesid, jsonformdata: formData},
done: function(newseries) {
modal.destroy();
var stringkey = 'createseriesforcourse_succeeded';
if (edited) {
let row = seriestable.getRows().find(r => r.getData().series === e.data.seriesid);
row.update({"seriesname": seriestitle});
stringkey = 'editseries_succeeded';
} else {
var s = JSON.parse(newseries);
seriestable.addRow({'seriesname': s.seriestitle, 'series': s.series, 'isdefault': s.isdefault});
Expand All @@ -100,7 +102,17 @@ function submitFormAjax(e) {
$("#createseries")?.hide();
$("#importseries")?.hide();
}

}
// We now notify the user about the successful series creation or edit.
str.get_string(stringkey, 'block_opencast')
.done(function(result) {
Notification.addNotification({
message: result,
type: 'success'
});
})
.fail(Notification.exception);
},
fail: function(er) {
if (er.errorcode === 'metadataseriesupdatefailed') {
Expand Down Expand Up @@ -189,7 +201,12 @@ export const init = (contextid, ocinstanceid, createseries, series, numseriesall
{key: 'importfailed', component: 'block_opencast'},
{key: 'form_seriesid', component: 'block_opencast'},
{key: 'setdefaultseries_heading', component: 'block_opencast'},
{key: 'setdefaultseries', component: 'block_opencast'}
{key: 'setdefaultseries', component: 'block_opencast'},
{key: 'setdefaultseriessucceeded', component: 'block_opencast'},
{key: 'cantdeletedefaultseries_modaltitle', component: 'block_opencast'},
{key: 'cantdeletedefaultseries', component: 'block_opencast'},
{key: 'delete_series_succeeded', component: 'block_opencast'},
{key: 'importseries_succeeded', component: 'block_opencast'},
];
str.get_strings(strings).then(function(jsstrings) {
// Style hidden input.
Expand All @@ -215,8 +232,7 @@ export const init = (contextid, ocinstanceid, createseries, series, numseriesall
input.name = 'defaultseries';
input.checked = cell.getValue();
input.classList.add('ignoredirty');
input.addEventListener('click', function(e) {
e.preventDefault();
input.addEventListener('change', function() {
ModalFactory.create({
type: ModalFactory.types.SAVE_CANCEL,
title: jsstrings[13],
Expand All @@ -241,6 +257,11 @@ export const init = (contextid, ocinstanceid, createseries, series, numseriesall
row.update({'isdefault': 0});
}
});
// We now notify the user about the successful update.
Notification.addNotification({
message: jsstrings[15],
type: 'success'
});
},
fail: function(e) {
modal.destroy();
Expand Down Expand Up @@ -304,6 +325,11 @@ export const init = (contextid, ocinstanceid, createseries, series, numseriesall
return '<i class="icon fa fa-trash fa-fw"></i>';
},
cellClick: function(e, cell) {
// We prevent default series deletion at js level as well.
if (cell.getRow().getData().isdefault === 1) {
Notification.alert(jsstrings[16], jsstrings[17]);
return;
}
ModalFactory.create({
type: ModalFactory.types.SAVE_CANCEL,
title: jsstrings[5],
Expand All @@ -329,6 +355,11 @@ export const init = (contextid, ocinstanceid, createseries, series, numseriesall
createseries?.removeClass('d-none');
importseries?.show();
importseries?.removeClass('d-none');
// We now notify the user about the successful deletion.
Notification.addNotification({
message: jsstrings[18],
type: 'success'
});
}
},
fail: function(e) {
Expand Down Expand Up @@ -434,11 +465,21 @@ export const init = (contextid, ocinstanceid, createseries, series, numseriesall
$("#createseries")?.hide();
$("#importseries")?.hide();
}

// We now notify the user about the successful series import.
Notification.addNotification({
message: jsstrings[19],
type: 'success'
});
}
},
fail: function() {
fail: function(er) {
modal.destroy();
displayError(jsstrings[11]);
var message = jsstrings[11];
if (er.errorcode === 'importseries_alreadyexists') {
message = er.message;
}
displayError(message);
}
}]);
});
Expand Down
52 changes: 42 additions & 10 deletions classes/external.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,16 @@ public static function import_series(int $contextid, int $ocinstanceid, string $
throw new moodle_exception('maxseriesreached', 'block_opencast');
}

// Check if the series id already exists in this course.
$importingseriesid = $params['seriesid'];
$existingseries = array_filter($courseseries, function ($courseserie) use ($importingseriesid) {
return $courseserie->series === $importingseriesid;
});

if (count($existingseries) > 0) {
throw new moodle_exception('importseries_alreadyexists', 'block_opencast');
}

// Perform ACL change.
$apibridge = apibridge::get_instance($params['ocinstanceid']);
$result = $apibridge->import_series_to_course_with_acl_change($course->id, $params['seriesid'], $USER->id);
Expand Down Expand Up @@ -293,20 +303,36 @@ public static function unlink_series(int $contextid, int $ocinstanceid, string $
'seriesid' => $series,
]);

$unlinkall = $params['seriesid'] === 'all';

$context = context::instance_by_id($params['contextid']);
self::validate_context($context);
require_capability('block/opencast:manageseriesforcourse', $context);

list($unused, $course, $cm) = get_context_info_array($context->id);

if ($params['seriesid'] === 'all') {
// In case the request comes from block deletion remove all mappings.
if ($unlinkall) {
$mappings = seriesmapping::get_records(['courseid' => $course->id]);
} else {
// Otherwise, the request comes from normal series deletion page.
$mappings = seriesmapping::get_records(['ocinstanceid' => $params['ocinstanceid'], 'courseid' => $course->id,
'series' => $params['seriesid']]);
}

foreach ($mappings as $mapping) {
$isdefault = $mapping->get('isdefault');
// We need to check the uniqueness of the mapping record when it is a single mapping removal.
if ($isdefault && !$unlinkall) {
// Prevent deletion of default series.
// By checking the number of default series,
// it is still possible to correct the faulty scenario of having multi-default series in a course.
if (seriesmapping::count_records(['ocinstanceid' => $params['ocinstanceid'],
'courseid' => $course->id, 'isdefault' => true, ]) === 1) {
throw new moodle_exception('cantdeletedefaultseries', 'block_opencast');
}
}

if (!$mapping->delete()) {
throw new moodle_exception('delete_series_failed', 'block_opencast');
}
Expand Down Expand Up @@ -349,24 +375,30 @@ public static function set_default_series(int $contextid, int $ocinstanceid, str
'courseid' => $course->id, 'isdefault' => true, ]);

// Series is already set as default.
if ($olddefaultseries->get('series') == $params['seriesid']) {
// We provide an exception here to fix the problem of having a course with no default series, which should not happen,
// by letting it pass through when the old default does not exist.
if (!empty($olddefaultseries) && $olddefaultseries->get('series') == $params['seriesid']) {
return true;
}

// Set new series as default.
$mapping = seriesmapping::get_record(['ocinstanceid' => $params['ocinstanceid'],
'courseid' => $course->id, 'series' => $params['seriesid'], ], true);

if ($mapping) {
// Remove default flag from old series first.
$canbeupdated = empty($olddefaultseries);
if (!empty($olddefaultseries)) {
$olddefaultseries->set('isdefault', false);
if ($olddefaultseries->update()) {
$canbeupdated = true;
}
}

// Now, we go for the actual update.
if ($canbeupdated && $mapping) {
$mapping->set('isdefault', true);
if ($mapping->update()) {
// Remove default flag from old series.
if ($olddefaultseries) {
$olddefaultseries->set('isdefault', false);
if ($olddefaultseries->update()) {
return true;
}
}
return true;
}
}

Expand Down
2 changes: 1 addition & 1 deletion classes/local/ltimodulemanager.php
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ public static function build_lti_modinfo($pluginid, $course, $title, $sectionid,
$moduleinfo->availability = $availability;

// Apply completion defaults.
$module = $DB->get_record('modules', ['name' => 'opencast']);
$module = $DB->get_record('modules', ['name' => 'lti'], '*', MUST_EXIST);
$defaults = manager::get_default_completion($course, $module);
if ($module) {
foreach ($defaults as $key => $value) {
Expand Down
9 changes: 8 additions & 1 deletion lang/en/block_opencast.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@
$string['cachedef_videodata'] = 'Caches the result of the opencast api for the opencast-block.';
$string['cachevalidtime'] = 'Cache valid time';
$string['cachevalidtime_desc'] = 'Time in seconds, before the cache for the video data of each course is refreshed.';
$string['cantdeletedefaultseries'] = 'You cannot delete the default series. Please choose another default series before deleting this series.';
$string['cantdeletedefaultseries'] = 'You cannot delete the default series. Please choose another series as default for this course before deleting this series.';
$string['cantdeletedefaultseries_modaltitle'] = 'Cannot Delete Default Series';
$string['catalog_params_noarray'] = "Parameters have to be either empty or a JSON representation of an array or an object.";
$string['catalog_static_params_empty'] = "Read only fields need to define a text in the parameters field.";
$string['catalogparam'] = 'Parameters in JSON-Format';
Expand Down Expand Up @@ -240,6 +241,7 @@
$string['created'] = 'Created at';
$string['createdby'] = 'Uploaded by';
$string['createseriesforcourse'] = 'Create new series';
$string['createseriesforcourse_succeeded'] = 'A new series has been successfully created.';
$string['creator'] = 'Presenter(s)';
$string['cronsettings'] = 'Settings for upload jobs';
$string['date'] = 'Start Date';
Expand All @@ -255,6 +257,7 @@
$string['delete_role'] = 'Delete role';
$string['delete_series'] = 'Delete series';
$string['delete_series_failed'] = 'Deleting the series failed. Please try again later or contact an administrator.';
$string['delete_series_succeeded'] = 'The series has been successfully deleted.';
$string['deleteaclgroup'] = 'Delete video from this list.';
$string['deletecheck_title_modal'] = 'Remove Opencast Block?';
$string['deletedraft'] = 'Delete a video before transfer to Opencast';
Expand Down Expand Up @@ -308,6 +311,7 @@
$string['editorendpointurl'] = 'Opencast Editor Endpoint';
$string['editorendpointurl_desc'] = 'The editor endpoint to access the editor. The mediapackage ID will be added at the end of the url.';
$string['editseries'] = 'Edit series';
$string['editseries_succeeded'] = 'The series has been successfully updated.';
$string['embeddedasactivity'] = 'Embedded as activity';
$string['embeddedasactivitywolink'] = 'Embedded as activity without linked series';
$string['empty_catalogname'] = 'This field must not be empty';
Expand Down Expand Up @@ -425,6 +429,8 @@
$string['importmode'] = 'Import mode';
$string['importmodedesc'] = 'In order to define an approach to import videos into a course, a mode should be seleted. The default mode is Duplicating Events in which a new series would be created and events will be avaible in the series by using a dupliaction workflow. <br /> ACL Change approach on the other hand will use the same seriesid among courses but series and events\' ACLs are changes to grant access from the course which imports the videos.<br /><strong>NOTE:</strong> At the moment, the selection of events in import wizards (course and manual) for "ACL Change" is NOT possible, due to avoiding further complications in terms of permissions in Opencast.';
$string['importseries'] = 'Import series';
$string['importseries_alreadyexists'] = 'The series you are trying to import is already present. Please choose a different series.';
$string['importseries_succeeded'] = 'The series has been successfully imported.';
$string['importvideos_aclprocessingexplanation'] = 'The seriesid of the selected course will be used in this course and the series\' ACL as well as ACLs of its videos will be changed accordingly.';
$string['importvideos_errornotenabledorworking'] = 'The import videos feature is not enabled or not working';
$string['importvideos_importbuttontitle'] = 'Import videos';
Expand Down Expand Up @@ -747,6 +753,7 @@
$string['setdefaultseries'] = 'Do you really want to use this series as new default series?';
$string['setdefaultseries_heading'] = 'Set default series';
$string['setdefaultseriesfailed'] = 'Changing the default series failed. Please try again later or contact an administrator.';
$string['setdefaultseriessucceeded'] = 'The default series has been successfully changed.';
$string['setting_permanent'] = 'Is permanent';
$string['settings'] = 'Opencast Videos';
$string['shared_settings'] = 'Shared settings';
Expand Down
9 changes: 9 additions & 0 deletions styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,12 @@ table.opencast-videos-table.generaltable thead th.oc-sortable-alignment > i {
float: left;
margin: 4px 0 1px 3px;
}

/* Styles for Series Table */
#seriestable .tabulator-row .tabulator-cell {
cursor: default !important; /* stylelint-disable-line */
}

#seriestable .tabulator-row .tabulator-cell input[type="radio"]:not(:checked) {
cursor: pointer !important; /* stylelint-disable-line */
}
37 changes: 22 additions & 15 deletions tests/behat/block_opencast_manageseries.feature
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@ Feature: Manage series as Teacher
And I set the field "Rights" to "Some user"
And I select "ALLRIGHTS" from the "License" singleselect
And I click on "Create new series" "button" in the ".modal" "css_element"
And I wait "2" seconds
Then I should see "My new series"
And I should see "84bab8de-5688-46a1-9af0-5ce9122eeb6a"
And I wait "1" seconds
Then I should see "A new series has been successfully created."

@javascript
Scenario: Teachers should be able to edit an existing series
Expand All @@ -60,9 +59,8 @@ Feature: Manage series as Teacher
And I set the field "Rights" to "Some user"
And I select "ALLRIGHTS" from the "License" singleselect
And I click on "Edit series" "button"
And I wait "2" seconds
Then I should not see "Updating the series metadata failed"
And I should see "Another series title"
And I wait "1" seconds
Then I should see "The series has been successfully updated."

@javascript
Scenario: Teachers should not be able to create/import series if the maximum number of series is reached
Expand All @@ -75,36 +73,45 @@ Feature: Manage series as Teacher
And I should not see "Import series"

@javascript
Scenario: Teachers should not be able to select a different default series
Scenario: Teachers should be able to select a different default series
Given I create a second series
When I click on "Go to overview..." "link"
And I click on "Manage series" "link"
And I click on ".tabulator-row-even input[name=\"defaultseries\"]" "css_element"
Then I should see "Do you really want to use this series as new default series"
When I click on "Save changes" "button"
And I wait "2" seconds
Then I should not see "Changing the default series failed"
And I wait "1" seconds
Then I should see "The default series has been successfully changed."

@javascript
Scenario: Teachers should be able to delete a series
Scenario: Teachers should be able to delete a series but should not be able to delete the default series
Given I create a second series
When I click on "Go to overview..." "link"
And I click on "Manage series" "link"
And I click on ".tabulator-row-even i.fa-trash" "css_element"
Then I should see "Are you sure you want to delete this series"
When I click on "Delete" "button" in the ".modal" "css_element"
And I wait "2" seconds
Then I should not see "Another series"
And I wait "1" seconds
Then I should see "The series has been successfully deleted."
When I click on ".tabulator-row-odd i.fa-trash" "css_element"
Then I should see "Cannot Delete Default Series"
When I click on "OK" "button" in the ".modal" "css_element"
Then I should see "1234-1234-1234-1234-1234"

@javascript
Scenario: Teachers should be able to import a series
Scenario: Teachers should be able to import a series but should not be able to import a series twice
When I click on "Go to overview..." "link"
And I click on "Manage series" "link"
And I click on "Import series" "button"
And I set the field "Series ID" to "1111-1111-1111-1111-1111"
And I click on "Import series" "button" in the ".modal" "css_element"
And I wait "2" seconds
Then I should not see "The series could not be imported"
And I wait "1" seconds
Then I should see "The series has been successfully imported."
When I click on "Import series" "button"
And I set the field "Series ID" to "1111-1111-1111-1111-1111"
And I click on "Import series" "button" in the ".modal" "css_element"
And I wait "1" seconds
Then I should see "The series you are trying to import is already present. Please choose a different series."

@javascript
Scenario: When manually deleting a block, teacher will be asked to decide whether to delete seriesmapping in a confirmation.
Expand Down

0 comments on commit fd97c8b

Please sign in to comment.