diff --git a/.gitignore b/.gitignore index 136a5dd..b8e7956 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ dist/ -lib/ +lib/h5p-standalone/ +libraries.h5p diff --git a/README.md b/README.md index e064e5c..2db4cdb 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The `fetch-dependencies` script is responsible for cloning the `bigbluebutton/h5 dataChannels: - name: jsonContent pushPermission: ['moderator','presenter'] - replaceOrDeletePermission: ['moderator', 'crerator'] + replaceOrDeletePermission: ['moderator', 'crerator', 'presenter'] - name: testResult pushPermission: ['all'] replaceOrDeletePermission: ['moderator', 'creator'] @@ -59,7 +59,7 @@ public: dataChannels: - name: jsonContent pushPermission: ['moderator','presenter'] - replaceOrDeletePermission: ['moderator', 'creator'] + replaceOrDeletePermission: ['moderator', 'creator', 'presenter'] - name: testResult pushPermission: ['all'] replaceOrDeletePermission: ['moderator', 'creator'] diff --git a/deploy_package.sh b/deploy_package.sh new file mode 100755 index 0000000..1999a37 --- /dev/null +++ b/deploy_package.sh @@ -0,0 +1,14 @@ +if [ -f libraries.h5p ]; then + if [ -d /var/www/bigbluebutton-default/assets/plugins/h5p ]; then + sudo cp -r /var/www/bigbluebutton-default/assets/plugins/h5p /var/www/bigbluebutton-default/assets/plugins/h5p-backup + sudo rm -rf /var/www/bigbluebutton-default/assets/plugins/h5p + fi + sudo mkdir -p /var/www/bigbluebutton-default/assets/plugins/h5p + sudo cp libraries.h5p /var/www/bigbluebutton-default/assets/plugins/h5p + cd /var/www/bigbluebutton-default/assets/plugins/h5p/ + sudo unzip libraries.h5p + sudo rm libraries.h5p +else + echo "No libraries.h5p found, either download one, or make one via the following command:" + echo " ./pack_dependencies.sh" +fi diff --git a/lib/libraries.json b/lib/libraries.json new file mode 100644 index 0000000..9587471 --- /dev/null +++ b/lib/libraries.json @@ -0,0 +1,380 @@ +{ + "FontAwesome": { + "id": "FontAwesome", + "title": "Font Awesome", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/font-awesome" + }, + "resume": false, + "majorVersion": 4, + "minorVersion": 5, + "patchVersion": 6, + "fullscreen": false, + "author": "Dave Gandy", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5P.AdvancedText": { + "id": "H5P.AdvancedText", + "title": "Text", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-advanced-text" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 1, + "patchVersion": 16, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5P.Crossword": { + "id": "H5P.Crossword", + "title": "Crossword", + "repo": { + "type": "github", + "url": "https://github.com/otacke/h5p-crossword" + }, + "resume": false, + "majorVersion": 0, + "minorVersion": 5, + "patchVersion": 11, + "fullscreen": false, + "author": "Language Training Center GmbH, Oliver Tacke", + "runnable": true, + "xapiVerbs": [ + "attempted" + ] + }, + "H5P.DragNBar": { + "id": "H5P.DragNBar", + "title": "Drag N Bar", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-drag-n-bar" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 5, + "patchVersion": 22, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5P.DragNDrop": { + "id": "H5P.DragNDrop", + "title": "Drag N Drop", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-drag-n-drop" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 1, + "patchVersion": 5, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5P.DragNResize": { + "id": "H5P.DragNResize", + "title": "Drag'N Resize", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-drag-n-resize" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 2, + "patchVersion": 5, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5P.DragQuestion": { + "id": "H5P.DragQuestion", + "title": "Drag and Drop", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-drag-question" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 14, + "patchVersion": 21, + "fullscreen": false, + "author": "H5P Group", + "runnable": true, + "xapiVerbs": [ + "attempted" + ] + }, + "H5P.FontIcons": { + "id": "H5P.FontIcons", + "title": "H5P.FontIcons", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-font-icons" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 0, + "patchVersion": 6, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5P.Image": { + "id": "H5P.Image", + "title": "Image", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-image" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 1, + "patchVersion": 23, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5P.JoubelUI": { + "id": "H5P.JoubelUI", + "title": "Joubel UI", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-joubel-ui" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 3, + "patchVersion": 19, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5P.MaterialDesignIcons": { + "id": "H5P.MaterialDesignIcons", + "title": "Material Design Icons", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-material-design-icons" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 0, + "patchVersion": 0, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5P.Question": { + "id": "H5P.Question", + "title": "Question", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-question" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 5, + "patchVersion": 15, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5P.Transition": { + "id": "H5P.Transition", + "title": "Transition", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-transition" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 0, + "patchVersion": 4, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5PEditor.ColorSelector": { + "id": "H5PEditor.ColorSelector", + "title": "H5PEditor.ColorSelector", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-editor-color-selector" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 3, + "patchVersion": 1, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5PEditor.DragQuestion": { + "id": "H5PEditor.DragQuestion", + "title": "Drag Question Editor", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-editor-drag-question" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 10, + "patchVersion": 21, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5PEditor.RangeList": { + "id": "H5PEditor.RangeList", + "title": "H5P Editor Range List", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-editor-range-list" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 0, + "patchVersion": 13, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5PEditor.ShowWhen": { + "id": "H5PEditor.ShowWhen", + "title": "Toggle visibility of a field based on rules", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-editor-show-when" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 0, + "patchVersion": 9, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5PEditor.TableList": { + "id": "H5PEditor.TableList", + "title": "H5P Editor Table List", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-editor-table-list" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 0, + "patchVersion": 4, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5PEditor.VerticalTabs": { + "id": "H5PEditor.VerticalTabs", + "title": "H5P Editor Vertical Tabs", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-editor-vertical-tabs" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 3, + "patchVersion": 8, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "H5PEditor.Wizard": { + "id": "H5PEditor.Wizard", + "title": "H5PEditor.Wizard", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/h5p-editor-wizard" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 2, + "patchVersion": 17, + "fullscreen": false, + "author": "H5P Group", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + }, + "jQuery.ui": { + "id": "jQuery.ui", + "title": "UI", + "repo": { + "type": "github", + "url": "https://github.com/bigbluebutton/jquery-ui" + }, + "resume": false, + "majorVersion": 1, + "minorVersion": 10, + "patchVersion": 22, + "fullscreen": false, + "author": "Jupiter", + "runnable": false, + "xapiVerbs": [ + "attempted" + ] + } +} \ No newline at end of file diff --git a/pack_dependencies.sh b/pack_dependencies.sh new file mode 100755 index 0000000..e58d0f4 --- /dev/null +++ b/pack_dependencies.sh @@ -0,0 +1,47 @@ +declare -a listOfLibraries=$(python3 scripts/list_libraries.py lib/libraries.json) + +h5pCommand=$(which h5p) +if [ -f "$h5pCommand" ]; then + mkdir temp + + cd temp + # Fetching each h5p libraries + for libraryUrl in $listOfLibraries; do + libName=$(echo ${libraryUrl##*/}) + git clone $libraryUrl $libName + cd $libName + if [ -f "package.json" ]; then + npm i + npm run build + fi + cd .. + h5p utils pack $libName "$libName.h5p" + unzip "$libName.h5p" + rm -rf $libName + rm "$libName.h5p" + done + cd .. + + # Fetching h5p-standalone + cd lib + if [ ! -d h5p-standalone ]; then + git clone https://github.com/bigbluebutton/h5p-standalone + fi + cd h5p-standalone + npm install + npm run build + cd ../../ + cp -r lib/h5p-standalone/dist/* temp/ + + # Now, packing everything + echo "Packing libraries..." + cd temp + zip -r libraries.h5p . + cd .. + mv temp/libraries.h5p ./ + rm -rf temp +else + echo "Please install the h5p command with (It might need super user permissions):" + echo " npm install -g h5p-cli" + echo "" +fi diff --git a/package.json b/package.json index b4aa7bf..5fc9461 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "build-bundle": "webpack --mode production", "start": "webpack serve --mode development", "build:watch": "rm -rf dist && tsc -w --module CommonJS", - "fetch-dependencies": "git clone https://github.com/bigbluebutton/h5p-standalone.git ../src/lib/", + "fetch-standalone": "git clone https://github.com/bigbluebutton/h5p-standalone.git lib/h5p-standalone", "lint": "eslint ./src/*", "lint:fix": "npm run lint -- --fix", "lint:watch": "watch 'yarn lint'" diff --git a/scripts/list_libraries.py b/scripts/list_libraries.py new file mode 100644 index 0000000..1cdd1bc --- /dev/null +++ b/scripts/list_libraries.py @@ -0,0 +1,14 @@ +import sys +import json + +libraries_json_path = sys.argv[1] + +libraries_list = "" +with open(libraries_json_path, 'r') as json_data: + d = json.load(json_data) + json_data.close() + for key, value in d.items(): + library_github_url = value["repo"]["url"] + libraries_list += library_github_url + " " + +print(libraries_list) \ No newline at end of file diff --git a/src/components/generic-component/h5p-renderer/utils.js b/src/components/generic-component/h5p-renderer/utils.js index 1109fed..51e078a 100644 --- a/src/components/generic-component/h5p-renderer/utils.js +++ b/src/components/generic-component/h5p-renderer/utils.js @@ -33,12 +33,12 @@ export const renderH5p = ( { state: JSON.stringify(previousState), }], - contentAsJson: contentAsJson.replace(/(\r\n|\n|\r)/gm, ''), - h5pAsJson: h5pAsJson.replace(/(\r\n|\n|\r)/gm, ''), + contentAsJson, + h5pAsJson, customCss, - h5pJsonPath: 'https://bigbluebutton.nyc3.digitaloceanspaces.com/plugins/h5p/assets', - frameJs: 'https://bigbluebutton.nyc3.digitaloceanspaces.com/plugins/h5p/assets/frame.bundle.js', - frameCss: 'https://bigbluebutton.nyc3.digitaloceanspaces.com/plugins/h5p/assets/styles/h5p.css', + h5pJsonPath: '/plugins/h5p/', + frameJs: '/plugins/h5p/frame.bundle.js', + frameCss: '/plugins/h5p/styles/h5p.css', assetsRequestFetchOptions: {}, }; await new H5P(el, options) diff --git a/src/components/generic-component/non-presenter-view/component.tsx b/src/components/generic-component/non-presenter-view/component.tsx index 5d12e9b..3c7cd06 100644 --- a/src/components/generic-component/non-presenter-view/component.tsx +++ b/src/components/generic-component/non-presenter-view/component.tsx @@ -33,7 +33,7 @@ function NonPresenterViewComponent(props: NonPresenterViewComponentProps) { if (verb === 'answered') { pluginApi.sendGenericDataForLearningAnalyticsDashboard({ columnTitle: h5pAsJsonObject.title, - value: `${(parseFloat(score) / parseFloat(maxScore)) * 10.0} / 10`, + value: `${parseFloat(score)} / ${parseFloat(maxScore)}`, cardTitle: 'H5P', }); pushEntryTestResult({ diff --git a/src/components/generic-component/presenter-view/component.tsx b/src/components/generic-component/presenter-view/component.tsx index 436a279..cae73a7 100644 --- a/src/components/generic-component/presenter-view/component.tsx +++ b/src/components/generic-component/presenter-view/component.tsx @@ -1,3 +1,4 @@ +import { pluginLogger } from 'bigbluebutton-html-plugin-sdk'; import { DataChannelEntryResponseType } from 'bigbluebutton-html-plugin-sdk/dist/cjs/data-channel/types'; import * as React from 'react'; import { PresenterViewComponentProps } from './types'; @@ -25,11 +26,15 @@ const mapObject = ( function PresenterViewComponent(props: PresenterViewComponentProps) { const { + currentUserId, h5pLatestStateUpdate, contentAsJson, h5pAsJson, } = props; - const dataToBeRendered = h5pLatestStateUpdate?.data; + const dataToBeRendered = h5pLatestStateUpdate?.data?.filter( + (item) => item.payloadJson.userId !== currentUserId, + ); + pluginLogger.debug(`Debug log from Presenter View Component (Showing dataToBeRendered): ${dataToBeRendered}`); return (
( usersList?.map((item) => { const userResult = results?.filter((r) => (r.userId === item.userId))[0]; return item.userId !== presenterId ? ( - - - Name: - {' '} - {item.name} - - - Score: - {' '} + + {item.name} + {userResult?.testResultObject} {userResult ? '/' : null} {userResult?.testResultMaximumScore} - - + + ) : null; }) ); function PresenterViewerSidekickRenderResultFunction(props: PresenterViewerRenderFunctionProps) { const { - usersList, currentUserId, pluginUuid, h5pContentText, + currentUserId, pluginUuid, h5pContentText, } = props; const pluginApi = BbbPluginSdk.getPluginApi(pluginUuid); const { data: testResultResponse } = pluginApi.useDataChannel('testResult', DataChannelTypes.All_ITEMS); + const allUsersInfo = pluginApi.useCustomSubscription( + USERS_MORE_INFORMATION, + ); + const usersList = allUsersInfo?.data?.user; const restults = testResultResponse.data?.map((data) => ({ userId: data.payloadJson.userId, testResultObject: data.payloadJson.testResultObject, testResultMaximumScore: data.payloadJson.testResultMaximumScore, } as TestResult)); - let sidekickAreaTitle: string = 'No h5p content is playing'; + let sidekickAreaTitle: string = 'No H5P content is playing'; if (h5pContentText) { const { h5pAsJson } = extractH5pContents(h5pContentText); @@ -64,7 +61,15 @@ function PresenterViewerSidekickRenderResultFunction(props: PresenterViewerRende } >

{sidekickAreaTitle}

- {mapObject(restults, usersList, currentUserId)} + + + + Users + Result + + {mapObject(restults, usersList, currentUserId)} + +
); } diff --git a/src/components/generic-component/presenter-view/sidekick-content/styles.ts b/src/components/generic-component/presenter-view/sidekick-content/styles.ts index 833af5e..b3ab2f8 100644 --- a/src/components/generic-component/presenter-view/sidekick-content/styles.ts +++ b/src/components/generic-component/presenter-view/sidekick-content/styles.ts @@ -1,5 +1,7 @@ import styled from 'styled-components'; +const colorGrayLightest = 'var(--color-gray-lightest, #D4D9DF)'; + const ListItemRender = styled.div` display: flex; flex-direction: column; @@ -11,4 +13,30 @@ const ListItemRender = styled.div` } `; -export { ListItemRender }; +const THeading = styled.th` + text-align: left; + + [dir="rtl"] & { + text-align: right; + } +`; + +const ResultLeft = styled.td` + padding: 0 .5rem 0 0; + border-bottom: 1px solid ${colorGrayLightest}; + + [dir="rtl"] & { + padding: 0 0 0 .5rem; + } + padding-bottom: .25rem; + word-break: break-all; +`; + +const ResultRight = styled.td` + padding-bottom: .25rem; + word-break: break-all; +`; + +export { + ListItemRender, ResultRight, ResultLeft, THeading, +}; diff --git a/src/components/generic-component/presenter-view/sidekick-content/types.ts b/src/components/generic-component/presenter-view/sidekick-content/types.ts index d3b9c10..bdec403 100644 --- a/src/components/generic-component/presenter-view/sidekick-content/types.ts +++ b/src/components/generic-component/presenter-view/sidekick-content/types.ts @@ -1,8 +1,5 @@ -import { MoreInfoUser } from '../../types'; - export interface PresenterViewerRenderFunctionProps { pluginUuid: string; currentUserId: string; - usersList: MoreInfoUser[]; h5pContentText: string; } diff --git a/src/components/h5p-plugin/component.tsx b/src/components/h5p-plugin/component.tsx index f670ae9..6d997af 100644 --- a/src/components/h5p-plugin/component.tsx +++ b/src/components/h5p-plugin/component.tsx @@ -23,8 +23,7 @@ import { GenericContentRenderFunction } from '../generic-component/component'; import { H5pPluginProps } from './types'; import { isValidJSON } from './utils'; import PresenterViewerSidekickRenderResultFunction from '../generic-component/presenter-view/sidekick-content/component'; -import { TestResult, UsersMoreInformationGraphqlResponse } from '../generic-component/types'; -import { USERS_MORE_INFORMATION } from '../generic-component/subscriptions'; +import { TestResult } from '../generic-component/types'; interface DataToGenericLink { contentJson?: string, @@ -48,10 +47,6 @@ function H5pPlugin( const { data: testResultResponse, deleteEntry: testResultDeleteEntry } = pluginApi.useDataChannel('testResult', DataChannelTypes.All_ITEMS); const { deleteEntry: deleteUserH5pCurrentStateList } = pluginApi.useDataChannel('testResult', DataChannelTypes.All_ITEMS, 'userH5pCurrentState'); const [genericContentId, setGenericContentId] = useState(''); - const allUsersInfo = pluginApi.useCustomSubscription( - USERS_MORE_INFORMATION, - ); - const usersList = allUsersInfo?.data?.user; const currentLayout = pluginApi.useUiData(LayoutPresentatioAreaUiDataNames.CURRENT_ELEMENT, [{ isOpen: true, @@ -91,7 +86,10 @@ function H5pPlugin( if (match && match.length > 0) { const longest = match.sort((a, b) => b.length - a.length); const indexOfH5P = longest[0].indexOf('H5P'); - const jsonContent = longest[0].substring(indexOfH5P + 3).replace(/(\r\n|\n|\r)/gm, ''); + const jsonContentWithlinebreaks = longest[0].substring(indexOfH5P + 3); + const jsonContent = jsonContentWithlinebreaks.replace(/-(\r\n|\n|\r)/gm, '-') + .replace(/(\r\n|\n|\r)\./gm, '.').replace(/(\r\n|\n|\r)/gm, ' '); + pluginLogger.debug(`Debug log to see what comes from the slide text (jsonContentWithlinebreaks): ${jsonContentWithlinebreaks} \n\n (JSON.parse(jsonContent)): `, JSON.parse(jsonContent)); if (isValidJSON(jsonContent)) { setLinkThatGeneratedJsonContent(currentTxtUri); setCurrentText(jsonContent); @@ -201,13 +199,12 @@ function H5pPlugin( currentUserId={currentUser.userId} h5pContentText={contentJson} pluginUuid={uuid} - usersList={usersList} /> , ); }, buttonIcon: 'user', - open: false, + open: contentJson && contentJson !== '', })); } const genericContentIdList = pluginApi.setGenericContentItems(genericContentList);