feat: LTI support and Moodle plugin

This commit is contained in:
Markos Gogoulos
2026-05-11 12:47:09 +03:00
committed by GitHub
parent b7427869b6
commit 55ab7ff34f
307 changed files with 19966 additions and 3748 deletions
@@ -513,12 +513,6 @@
}
}
.embedded-app {
.viewer-container,
.viewer-info {
width: 100%;
}
}
.viewer-image-container {
position: relative;
@@ -79,6 +79,10 @@ function EditMediaButton(props) {
link = '/edit-media.html';
}
if (link && inEmbeddedApp()) {
link += '&mode=lms_embed_mode';
}
return (
<a href={link} rel="nofollow" title={translateString('Edit media')} className="edit-media-icon">
<i className="material-icons">edit</i>
@@ -94,11 +98,18 @@ export default function ViewerInfoContent(props) {
!PageStore.get('config-enabled').taxonomies.tags || PageStore.get('config-enabled').taxonomies.tags.enabled
? metafield(MediaPageStore.get('media-tags'))
: [];
let mediaCategories = MediaPageStore.get('media-categories');
// Filter to show only LMS courses when in embed mode
if (inEmbeddedApp()) {
mediaCategories = mediaCategories.filter(cat => cat.is_lms_course === true);
}
const categoriesContent = PageStore.get('config-options').pages.media.categoriesWithTitle
? []
: !PageStore.get('config-enabled').taxonomies.categories ||
PageStore.get('config-enabled').taxonomies.categories.enabled
? metafield(MediaPageStore.get('media-categories'))
? metafield(mediaCategories)
: [];
let summary = MediaPageStore.get('media-summary');
@@ -220,9 +231,13 @@ export default function ViewerInfoContent(props) {
<MediaMetaField
value={categoriesContent}
title={
1 < categoriesContent.length
? translateString('Categories')
: translateString('Category')
inEmbeddedApp()
? (1 < categoriesContent.length
? translateString('Courses')
: translateString('Course'))
: (1 < categoriesContent.length
? translateString('Categories')
: translateString('Category'))
}
id="categories"
/>
@@ -274,7 +289,7 @@ export default function ViewerInfoContent(props) {
</div>
</div>
{!inEmbeddedApp() && <CommentsList />}
<CommentsList />
</div>
);
}
@@ -183,8 +183,7 @@ export default class ViewerInfoTitleBanner extends React.PureComponent {
{MemberContext._currentValue.can.shareMedia ? <MediaShareButton isVideo={false} /> : null}
{!MemberContext._currentValue.is.anonymous &&
MemberContext._currentValue.can.saveMedia &&
-1 < PlaylistsContext._currentValue.mediaTypes.indexOf(MediaPageStore.get('media-type')) ? (
MemberContext._currentValue.can.saveMedia ? (
<MediaSaveButton />
) : null}
@@ -95,10 +95,8 @@ export default class ViewerInfoVideoTitleBanner extends ViewerInfoTitleBanner {
<MediaShareButton isVideo={true} />
) : null}
{!inEmbeddedApp() &&
!MemberContext._currentValue.is.anonymous &&
MemberContext._currentValue.can.saveMedia &&
-1 < PlaylistsContext._currentValue.mediaTypes.indexOf(MediaPageStore.get('media-type')) ? (
{!MemberContext._currentValue.is.anonymous &&
MemberContext._currentValue.can.saveMedia ? (
<MediaSaveButton />
) : null}
@@ -1,7 +1,4 @@
import React from 'react';
import { MediaPageStore } from '../../utils/stores/';
import { AutoPlay } from './AutoPlay';
import { RelatedMedia } from './RelatedMedia';
import PlaylistView from './PlaylistView';
export default class ViewerSidebar extends React.PureComponent {
@@ -12,8 +9,6 @@ export default class ViewerSidebar extends React.PureComponent {
playlistData: props.playlistData,
isPlaylistPage: !!props.playlistData,
activeItem: 0,
mediaType: MediaPageStore.get('media-type'),
chapters: MediaPageStore.get('media-data')?.chapters
};
if (props.playlistData) {
@@ -28,18 +23,6 @@ export default class ViewerSidebar extends React.PureComponent {
}
}
this.onMediaLoad = this.onMediaLoad.bind(this);
}
componentDidMount() {
MediaPageStore.on('loaded_media_data', this.onMediaLoad);
}
onMediaLoad() {
this.setState({
mediaType: MediaPageStore.get('media-type'),
chapters: MediaPageStore.get('media-data')?.chapter_data || []
});
}
render() {
@@ -47,10 +30,7 @@ export default class ViewerSidebar extends React.PureComponent {
<div className="viewer-sidebar">
{this.state.isPlaylistPage ? (
<PlaylistView activeItem={this.state.activeItem} playlistData={this.props.playlistData} />
) : 'video' === this.state.mediaType || 'audio' === this.state.mediaType ? (
<AutoPlay />
) : null}
<RelatedMedia hideFirst={!this.state.isPlaylistPage} />
</div>
);
}