diff --git a/Courseware.php b/Courseware.php index 700f929b5..fa866cec6 100755 --- a/Courseware.php +++ b/Courseware.php @@ -28,6 +28,8 @@ class Courseware extends StudIPPlugin implements StandardPlugin */ private $container; + public static $registered_blocks = []; + public function __construct() { parent::__construct(); @@ -45,6 +47,8 @@ public function __construct() // set text-domain for translations in this plugin bindtextdomain('courseware', dirname(__FILE__).'/locale'); + + \NotificationCenter::postNotification('CoursewareRegisterBlocks', \Context::getId()); } public function getPluginname() @@ -91,7 +95,7 @@ public function getTabNavigation($courseId) ); $settingsUrl = PluginEngine::getURL($this, compact('cid'), 'courseware/settings', true); $navigation->addSubnavigation( - 'settings', + 'settings', new Navigation(_cw('Einstellungen'), $settingsUrl) ); $navigation->addSubnavigation( @@ -176,7 +180,7 @@ public function getIconNavigation($courseId, $last_visit, $user_id) seminar_id = :cid AND chdate >= :last_visit - AND + AND type NOT IN ('Courseware', 'Chapter', 'Subchapter', 'Section') "); $stmt->bindParam(':cid', $courseId); @@ -189,7 +193,7 @@ public function getIconNavigation($courseId, $last_visit, $user_id) if ($plugin_manager->getPluginInfo('VipsPlugin') == null){ $vips = false; } - if($plugin_manager->getPlugin('VipsPlugin')){ + if($plugin_manager->getPlugin('VipsPlugin')){ $version = $plugin_manager->getPluginManifest($plugin_manager->getPlugin('VipsPlugin')->getPluginPath())['version']; if (version_compare('1.3',$version) > 0) { $vips = false; @@ -517,8 +521,8 @@ public static function deleteUserdata($user_id) $stmt = $db->prepare(' DELETE FROM - mooc_userprogress - WHERE + mooc_userprogress + WHERE user_id = :uid '); $stmt->bindParam(':uid', $user_id); @@ -526,10 +530,10 @@ public static function deleteUserdata($user_id) $stmt = $db->prepare(' DELETE FROM - mooc_fields - WHERE + mooc_fields + WHERE user_id = :uid - AND + AND (name = "visited" OR name = "lastSelected") '); $stmt->bindParam(':uid', $user_id); @@ -538,4 +542,13 @@ public static function deleteUserdata($user_id) return $exec; } + public static function addBlock($plugin_name, $rel_path) + { + $path = PluginEngine::getPlugin($plugin_name)->getPluginPath(); + + self::$registered_blocks[] = [ + 'plugin' => $plugin_name, + 'path' => $path .'/'. $rel_path, + ]; + } } diff --git a/assets/js/courseware.js b/assets/js/courseware.js index b14fa8835..77a053114 100644 --- a/assets/js/courseware.js +++ b/assets/js/courseware.js @@ -3,6 +3,9 @@ import $ from 'jquery' import Backbone from 'backbone' import helper from './url' import BlockModel from './block_model' +import block_types from './block_types'; +import AuthorView from 'js/author_view' +import StudentView from 'js/student_view' import '../less/courseware.less' @@ -31,7 +34,6 @@ import 'ImageMapBlock/js/ImageMapBlock' import 'InteractiveVideoBlock/js/InteractiveVideoBlock' import 'KeyPointBlock/js/KeyPointBlock' import 'LinkBlock/js/LinkBlock' -import 'OpenCastBlock/js/OpenCastBlock' import 'PdfBlock/js/PdfBlock' import 'PostBlock/js/PostBlock' import 'ScrollyBlock/js/ScrollyBlock' @@ -103,3 +105,5 @@ function patchBackbone() { return (xhr !== false) ? xhr : Promise.reject(new Error('ModelError')); }; } + +export {$, Backbone, AuthorView, StudentView, helper, block_types }; diff --git a/blocks/OpenCastBlock/css/opencast_block.less b/blocks/OpenCastBlock/css/opencast_block.less deleted file mode 100644 index 7c5e6d45a..000000000 --- a/blocks/OpenCastBlock/css/opencast_block.less +++ /dev/null @@ -1,19 +0,0 @@ -@import '../../../assets/less/mixins'; - -.OpenCastBlock { - iframe { - border: solid thin #ccc; - } - .cw-opencast-useocplayer-wrapper { - width: 40em; - height: unset; - padding: 0.5em 0; - display: inline-block; - vertical-align: top; - - span.bold{ - font-weight: 600; - } - } - -} diff --git a/blocks/OpenCastBlock/js/OpenCastBlock.js b/blocks/OpenCastBlock/js/OpenCastBlock.js deleted file mode 100644 index add812f27..000000000 --- a/blocks/OpenCastBlock/js/OpenCastBlock.js +++ /dev/null @@ -1,16 +0,0 @@ -import block_types from 'js/block_types' -import StudentView from './student_view' -import AuthorView from './author_view' - -import '../css/opencast_block.less' - -export default block_types.add({ - name: 'OpenCastBlock', - - content_block: true, - - views: { - student: StudentView, - author: AuthorView - } -}); diff --git a/blocks/OpenCastBlock/js/author_view.js b/blocks/OpenCastBlock/js/author_view.js deleted file mode 100644 index 24b75a322..000000000 --- a/blocks/OpenCastBlock/js/author_view.js +++ /dev/null @@ -1,109 +0,0 @@ -import $ from 'jquery' -import Backbone from 'backbone' -import AuthorView from 'js/author_view' -import helper from 'js/url' - -export default AuthorView.extend({ - - events: { - 'click button[name=save]': 'onSave', - 'click button[name=cancel]': 'switchBack' - }, - - initialize() { - Backbone.on('beforemodeswitch', this.onModeSwitch, this); - Backbone.on('beforenavigate', this.onNavigate, this); - }, - - render() { - return this; - }, - - postRender() { - let $opencast_id = this.$('.cw-opencast-stored-id').val(); - let $opencast_player = this.$('.cw-opencast-stored-player').val(); - if ($opencast_id != '') { - this.$('.cw-opencast-content option[data-opencastid="'+$opencast_id+'"]').prop('selected', true); - } - switch($opencast_player){ - case 'treu': - case 'theodul': - default: - this.$('.cw-opencast-useocplayer[value="theodul"]').prop('checked', true); - break; - case 'paella': - this.$('.cw-opencast-useocplayer[value="paella"]').prop('checked', true); - break; - case 'false': - this.$('.cw-opencast-useocplayer[value="false"]').prop('checked', true); - break; - } - - return this; - }, - - onNavigate(event) { - if (!$('section .block-content button[name=save]').length) { - return; - } - if(event.isUserInputHandled) { - return; - } - event.isUserInputHandled = true; - Backbone.trigger('preventnavigateto', !confirm('Es gibt nicht gespeicherte Änderungen. Möchten Sie die Seite trotzdem verlassen?')); - }, - - onModeSwitch(toView, event) { - if (toView != 'student') { - return; - } - // the user already switched back (i.e. the is not visible) - if (!this.$el.is(':visible')) { - return; - } - // another listener already handled the user's feedback - if (event.isUserInputHandled) { - return; - } - event.isUserInputHandled = true; - Backbone.trigger('preventviewswitch', !confirm('Es gibt nicht gespeicherte Änderungen. Möchten Sie trotzdem fortfahren?')); - }, - - onSave(event) { - var $view = this; - let $opencast_content = {}; - $opencast_content.id = this.$(".cw-opencast-content option:selected").attr('data-opencastid'); - $opencast_content.useplayer = this.$('.cw-opencast-useocplayer:checked').val(); - $opencast_content.title = this.$(".cw-opencast-content option:selected").attr('data-episodetitle'); - - switch($opencast_content.useplayer) { - case 'theodul': - default: - $opencast_content.url_opencast_theodul = this.$(".cw-opencast-content option:selected").attr('data-urlopencasttheodul'); - break; - case 'paella': - $opencast_content.url_opencast_paella = this.$(".cw-opencast-content option:selected").attr('data-urlopencastpaella'); - break; - case 'false': - $opencast_content.url_mp4 = this.$(".cw-opencast-content option:selected").attr('data-urlmp4'); - break; - } - $opencast_content = JSON.stringify($opencast_content); - helper - .callHandler(this.model.id, 'save', { - opencast_content : $opencast_content - }) - .then( - // success - function () { - $(event.target).addClass('accept'); - $view.switchBack(); - }, - - // error - function (error) { - console.log(error); - } - ); - } -}); diff --git a/blocks/OpenCastBlock/js/student_view.js b/blocks/OpenCastBlock/js/student_view.js deleted file mode 100644 index 4971c0354..000000000 --- a/blocks/OpenCastBlock/js/student_view.js +++ /dev/null @@ -1,33 +0,0 @@ -import $ from 'jquery' -import StudentView from 'js/student_view' -import helper from 'js/url' - -export default StudentView.extend({ - events: { - }, - - initialize() { }, - - render() { - return this; - }, - - postRender() { - try { - OC.ltiCall(OC_SEARCH_URL, OC_LTI_DATA, function() { - jQuery('iframe.courseware-oc-video').each(function() { - this.src = this.dataset.src; - }); - }); - } catch (e) { - if (e instanceof ReferenceError) { - console.log('OpenCast is not available'); - } else { - console.log(e); - } - } - - return this; - }, - -}); diff --git a/blocks/OpenCastBlock/schema/opencast-1.0.xsd b/blocks/OpenCastBlock/schema/opencast-1.0.xsd deleted file mode 100644 index 558400345..000000000 --- a/blocks/OpenCastBlock/schema/opencast-1.0.xsd +++ /dev/null @@ -1,8 +0,0 @@ - - - - diff --git a/blocks/OpenCastBlock/templates/author_view.mustache b/blocks/OpenCastBlock/templates/author_view.mustache deleted file mode 100644 index d02ed2ef6..000000000 --- a/blocks/OpenCastBlock/templates/author_view.mustache +++ /dev/null @@ -1,55 +0,0 @@ -{{#opencast_installed}} - {{#opencast_active}} - - - - - - -
- -
- - Theodul - einfacher Player der entweder Video 1 oder Video 2 abspielt -
- - Paella - umfangreicher Player der beide Videos parallel abspielt. - -
-
- - -
- {{/opencast_active}} - {{^opencast_active}} -
- {{#i18n}}Das Opencast Plugin ist in dieser Veranstaltung nicht aktiviert. Sie können das Plugin über den Navigationspunkt „mehr…” aktivieren.{{/i18n}} -
-
- -
- {{/opencast_active}} -{{/opencast_installed}} -{{^opencast_installed}} -
- {{#i18n}}Das Opencast Plugin ist nicht installiert. Bitte wenden Sie sich an Ihren Administrator.{{/i18n}} -
-
- -
-{{/opencast_installed}} diff --git a/blocks/OpenCastBlock/templates/preview_view.mustache b/blocks/OpenCastBlock/templates/preview_view.mustache deleted file mode 100644 index 20b39980c..000000000 --- a/blocks/OpenCastBlock/templates/preview_view.mustache +++ /dev/null @@ -1 +0,0 @@ -

{{content}}

diff --git a/blocks/OpenCastBlock/templates/student_view.mustache b/blocks/OpenCastBlock/templates/student_view.mustache deleted file mode 100644 index edcfcf308..000000000 --- a/blocks/OpenCastBlock/templates/student_view.mustache +++ /dev/null @@ -1,23 +0,0 @@ -{{#oc_present}} - - {{#url_isset}} - {{#useplayer}} - - - - {{/useplayer}} - {{^useplayer}} - - {{/useplayer}} - {{/url_isset}} - {{^url_isset}} -
- {{#i18n}}Es wurde kein OpenCast-Video ausgewählt.{{/i18n}} -
- {{/url_isset}} -{{/oc_present}} diff --git a/controllers/courseware.php b/controllers/courseware.php index 4ecce9519..60d792cc4 100755 --- a/controllers/courseware.php +++ b/controllers/courseware.php @@ -57,7 +57,7 @@ public function settings_action() return $this->redirect('courseware/settings'); } } - + public function news_action() { PageLayout::addStylesheet($this->plugin->getPluginURL().'/assets/static/courseware.css'); @@ -72,7 +72,7 @@ public function news_action() seminar_id = :cid AND chdate >= :last_visit - AND + AND type NOT IN ('Courseware', 'Chapter', 'Subchapter', 'Section') "); $stmt->bindParam(":cid", $this->container['cid']); @@ -86,7 +86,7 @@ public function news_action() if ($plugin_manager->getPluginInfo('VipsPlugin') == null){ $vips = false; } - if($plugin_manager->getPlugin('VipsPlugin')){ + if($plugin_manager->getPlugin('VipsPlugin')){ $version = $plugin_manager->getPluginManifest($plugin_manager->getPlugin('VipsPlugin')->getPluginPath())['version']; if (version_compare('1.3',$version) > 0) { $vips = false; @@ -99,20 +99,20 @@ public function news_action() // getting all tests $db = DBManager::get(); $stmt = $db->prepare(" - SELECT - json_data - FROM + SELECT + json_data + FROM mooc_blocks - JOIN - mooc_fields - ON - mooc_blocks.id = mooc_fields.block_id - WHERE + JOIN + mooc_fields + ON + mooc_blocks.id = mooc_fields.block_id + WHERE mooc_blocks.type = 'TestBlock' AND mooc_blocks.seminar_id = :cid - AND - mooc_fields.name = 'test_id' + AND + mooc_fields.name = 'test_id' "); $stmt->bindParam(":cid", $this->container['cid']); $stmt->execute(); @@ -166,7 +166,7 @@ public function news_action() $subchapter = $block->parent->parent->title; $section = $block->parent->title; if (!$block->isVisible()) {continue;} - $class_name = 'Mooc\UI\\'.$block->type.'\\'.$block->type; + $class_name = 'Mooc\UI\\'.$block->type.'\\'.$block->type; $name_constant = $class_name.'::NAME'; if (defined($name_constant)) { @@ -176,13 +176,13 @@ public function news_action() } $ui_block = $this->plugin->getBlockFactory()->makeBlock($block); $this->new_content[$chapter][$subchapter][$section][$block->id] = array( - 'title' => $title, + 'title' => $title, 'type' => $block->type, 'id' => $block->id, - 'ui_block' => $ui_block + 'ui_block' => $ui_block ); } - + return true; } @@ -196,7 +196,36 @@ private function getMustacheTemplates() { $templates = array(); - foreach (glob($this->plugin->getPluginPath() . '/blocks/*/templates/*.mustache') as $file) { + // add templates and load less files from block plugins + $plugin_template_files = array(); + + foreach (\Courseware::$registered_blocks as $block) { + $plugin_template_files = array_merge( + $plugin_template_files, + glob($block['path'] . '/templates/*.mustache') + ); + + // add stylesheets for block to page + foreach (glob($block['path'] . '/css/*.css') as $source) { + PageLayout::addHeadElement('link', [ + 'rel' => 'stylesheet', + 'type' => 'text/css', + 'href' => UrlHelper::getURL($source, ['cid' => null]) + ]); + } + + // add base js for block to page + PageLayout::addHeadElement('script', [ + 'src' => URLHelper::getUrl($block['path'] .'/js/'. basename($block['path']) .'.js'), + 'type' => 'module' + ], ''); + } + + // add base templates, integrating plugin templates + foreach (array_merge( + glob($this->plugin->getPluginPath() . '/blocks/*/templates/*.mustache'), + $plugin_template_files + ) as $file) { preg_match('|blocks/([^/]+)/templates/([^/]+).mustache$|', $file, $matches); list(, $block, $name) = $matches; @@ -263,7 +292,7 @@ private function storeSettings() // Scrollytelling // //////////////////////// $this->storeScrollytelling(isset($courseware_settings['scrollytelling']) ? true : false); - + //////////////////////// // EDITING PERMISSION // //////////////////////// @@ -320,7 +349,7 @@ private function storeDiscussionBlockActivation($active) // TODO: send a message back } } - + private function storeVipsTabVisible($active) { if (!$this->courseware_block->setVipsTabVisible($active)) { diff --git a/models/mooc/ui/BlockFactory.php b/models/mooc/ui/BlockFactory.php index 6baf2bdf3..6a64dc692 100755 --- a/models/mooc/ui/BlockFactory.php +++ b/models/mooc/ui/BlockFactory.php @@ -27,6 +27,8 @@ public function __construct(Container $container) */ public function makeBlock($sorm_block) { + $this->getBlockClasses(); + $class = 'Mooc\\UI\\'.$sorm_block->type.'\\'.$sorm_block->type; // there is no class describing a UI for that kind of block @@ -42,11 +44,23 @@ public function makeBlock($sorm_block) public function getBlockClasses() { static $classes; + if (!isset($classes)) { $classes = array_map("basename", glob($this->getPluginDir() . '/blocks/*')); } - return $classes; + $dyn_classes = []; + + foreach (\Courseware::$registered_blocks as $block) { + $dyn_classes[] = basename($block['path']); + + // load classes in blocks + foreach (glob($block['path'] . '/*.php') as $class) { + require_once($class); + } + } + + return array_merge($classes, $dyn_classes); } // TODO diff --git a/webpack.config.js b/webpack.config.js index 5af718bf7..6d7e30222 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,7 +17,8 @@ module.exports = { chunkFilename: '[name].chunk.js', filename: '[name].js', pathinfo: !isProd, - publicPath: !isProd ? 'http://localhost:8081/' : undefined + publicPath: !isProd ? 'http://localhost:8081/' : undefined, + library: '[name]' }, module: { rules: [