From 3d9059cfce6da6fbea0b4fe7008075444f270702 Mon Sep 17 00:00:00 2001 From: Cay Henning Date: Tue, 3 Oct 2023 14:41:09 -0400 Subject: [PATCH 01/19] Add null checks --- src/components/my-widgets-collaborate-dialog.jsx | 4 ++-- src/components/my-widgets-page.jsx | 8 ++++---- src/components/my-widgets-selected-instance.jsx | 4 ++-- src/components/my-widgets-settings-dialog.jsx | 4 ++-- src/components/notifications.jsx | 2 +- src/components/widget-creator.jsx | 16 ++++++++-------- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/components/my-widgets-collaborate-dialog.jsx b/src/components/my-widgets-collaborate-dialog.jsx index ea0c0d28d..083f7d984 100644 --- a/src/components/my-widgets-collaborate-dialog.jsx +++ b/src/components/my-widgets-collaborate-dialog.jsx @@ -41,10 +41,10 @@ const MyWidgetsCollaborateDialog = ({onClose, inst, myPerms, otherUserPerms, set placeholderData: [], retry: false, onSuccess: (data) => { - if (!data || (data.type == 'error')) + if (data && data.type == 'error') { console.error(`User search failed with error: ${data.msg}`); - if (data.title =="Invalid Login") + if (data.title == "Invalid Login") { setInvalidLogin(true) } diff --git a/src/components/my-widgets-page.jsx b/src/components/my-widgets-page.jsx index 50cb2bdcd..7c26234e4 100644 --- a/src/components/my-widgets-page.jsx +++ b/src/components/my-widgets-page.jsx @@ -215,7 +215,7 @@ const MyWidgetsPage = () => { widgetName: inst.widget.name, dir: inst.widget.dir, successFunc: (data) => { - if (!data || (data.type == 'error')) + if (data && (data.type == 'error')) { console.error(`Failed to copy widget with error: ${data.msg}`); if (data.title == "Invalid Login") @@ -245,10 +245,10 @@ const MyWidgetsPage = () => { { instId: inst.id, successFunc: (data) => { - if (!data || (data.type == 'error')) + if (data && data.type == 'error') { - console.error(`Deletion failed with error: ${data.msg}`); - if (data.title =="Invalid Login") + console.error(`Error: ${data.msg}`); + if (data.title == "Invalid Login") { setInvalidLogin(true) } diff --git a/src/components/my-widgets-selected-instance.jsx b/src/components/my-widgets-selected-instance.jsx index b4d8b96f1..2ed12efe2 100644 --- a/src/components/my-widgets-selected-instance.jsx +++ b/src/components/my-widgets-selected-instance.jsx @@ -80,10 +80,10 @@ const MyWidgetSelectedInstance = ({ enabled: !!inst.id, staleTime: Infinity, onSuccess: (data) => { - if (!data || data.type == 'error') + if (data && data.type == 'error') { console.error(`Error: ${data.msg}`); - if (data.title =="Invalid Login") + if (data.title == "Invalid Login") { setInvalidLogin(true) } diff --git a/src/components/my-widgets-settings-dialog.jsx b/src/components/my-widgets-settings-dialog.jsx index a82f7987c..3310f4d74 100644 --- a/src/components/my-widgets-settings-dialog.jsx +++ b/src/components/my-widgets-settings-dialog.jsx @@ -85,10 +85,10 @@ const MyWidgetsSettingsDialog = ({ onClose, inst, currentUser, otherUserPerms, o enabled: !!otherUserPerms && Array.from(otherUserPerms.keys())?.length > 0, staleTime: Infinity, onSuccess: (data) => { - if (!data || (data.type == 'error')) + if (data && data.type == 'error') { console.error(`Error: ${data.msg}`); - if (data.title =="Invalid Login") + if (data.title == "Invalid Login") { setInvalidLogin(true) } diff --git a/src/components/notifications.jsx b/src/components/notifications.jsx index 1357abe8f..0d3a657d1 100644 --- a/src/components/notifications.jsx +++ b/src/components/notifications.jsx @@ -121,7 +121,7 @@ const Notifications = (user) => { instId: notif.item_id, permsObj: userPerms, successFunc: (data) => { - if (data.status == 200) + if (data && data.status == 200) { // Redirect to widget if (!window.location.pathname.includes('my-widgets')) diff --git a/src/components/widget-creator.jsx b/src/components/widget-creator.jsx index 160c278f9..34cd8a91e 100644 --- a/src/components/widget-creator.jsx +++ b/src/components/widget-creator.jsx @@ -103,14 +103,14 @@ const WidgetCreator = ({instId, widgetId, minHeight='', minWidth=''}) => { placeholderData: null, enabled: !!instance.id, // requires instance state object to be prepopulated onSettled: (data) => { - if ( (data != null ? data.title : undefined) === 'Permission Denied' ||data.title === 'error') { - setCreatorState({...creatorState, invalid: true}) - onInitFail('Permission Denied') - } else { - setCreatorState({...creatorState, invalid: false}) - setInstance({ ...instance, qset: data }) - } + if ( (data != null ? data.title : undefined) === 'Permission Denied' || (data && data.title === 'error')) { + setCreatorState({...creatorState, invalid: true}) + onInitFail('Permission Denied') + } else { + setCreatorState({...creatorState, invalid: false}) + setInstance({ ...instance, qset: data }) } + } }) // verify user can publish a given instance @@ -659,7 +659,7 @@ const WidgetCreator = ({instId, widgetId, minHeight='', minWidth=''}) => { } let lastSavedRender = null - if (sinceLastSave.lastSave) { + if (sinceLastSave.lastSave) { lastSavedRender = ( {sinceLastSave.elapsed < 1 ? ' Last saved < 1m ago' : `Last saved ${sinceLastSave.elapsed}m ago`} From 6c17714413659f847d4646a9692b2cc0318611fd Mon Sep 17 00:00:00 2001 From: Cay Henning Date: Tue, 3 Oct 2023 15:31:39 -0400 Subject: [PATCH 02/19] Add console.errors for null values in previous changes --- src/components/my-widgets-collaborate-dialog.jsx | 2 ++ src/components/my-widgets-page.jsx | 2 ++ src/components/my-widgets-selected-instance.jsx | 2 ++ src/components/my-widgets-settings-dialog.jsx | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/components/my-widgets-collaborate-dialog.jsx b/src/components/my-widgets-collaborate-dialog.jsx index 083f7d984..c32e059ed 100644 --- a/src/components/my-widgets-collaborate-dialog.jsx +++ b/src/components/my-widgets-collaborate-dialog.jsx @@ -48,6 +48,8 @@ const MyWidgetsCollaborateDialog = ({onClose, inst, myPerms, otherUserPerms, set { setInvalidLogin(true) } + } else if (!data) { + console.error(`User search failed.`); } } }) diff --git a/src/components/my-widgets-page.jsx b/src/components/my-widgets-page.jsx index 7c26234e4..dd8ee8a20 100644 --- a/src/components/my-widgets-page.jsx +++ b/src/components/my-widgets-page.jsx @@ -252,6 +252,8 @@ const MyWidgetsPage = () => { { setInvalidLogin(true) } + } else if (!data) { + console.error(`Delete widget failed.`); } } }, diff --git a/src/components/my-widgets-selected-instance.jsx b/src/components/my-widgets-selected-instance.jsx index 2ed12efe2..9c0eb80c8 100644 --- a/src/components/my-widgets-selected-instance.jsx +++ b/src/components/my-widgets-selected-instance.jsx @@ -87,6 +87,8 @@ const MyWidgetSelectedInstance = ({ { setInvalidLogin(true) } + } else if (!data) { + console.error(`Failed to fetch permissions.`); } } }) diff --git a/src/components/my-widgets-settings-dialog.jsx b/src/components/my-widgets-settings-dialog.jsx index 3310f4d74..7c16fb14e 100644 --- a/src/components/my-widgets-settings-dialog.jsx +++ b/src/components/my-widgets-settings-dialog.jsx @@ -92,6 +92,8 @@ const MyWidgetsSettingsDialog = ({ onClose, inst, currentUser, otherUserPerms, o { setInvalidLogin(true) } + } else if (!data) { + console.error('Failed to fetch users.') } } }) From d52ecc498b4631b6bb5e451f20770fbedd634292 Mon Sep 17 00:00:00 2001 From: Cay Henning Date: Tue, 3 Oct 2023 16:22:04 -0400 Subject: [PATCH 03/19] Add current instance ID to new user in extra attempts dialog --- src/components/extra-attempts-dialog.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/extra-attempts-dialog.jsx b/src/components/extra-attempts-dialog.jsx index 28f8c2034..16c1bc1f3 100644 --- a/src/components/extra-attempts-dialog.jsx +++ b/src/components/extra-attempts-dialog.jsx @@ -78,7 +78,7 @@ const ExtraAttemptsDialog = ({onClose, inst}) => { state.newIdCount, { id: parseInt(state.newIdCount), - context_id: '', + context_id: inst?.id || "", extra_attempts: 1, user_id: parseInt(match.id) } From 65853c3b91c553eb1fba954d0e8dae284ac452dd Mon Sep 17 00:00:00 2001 From: Cay Henning Date: Thu, 5 Oct 2023 14:25:34 -0400 Subject: [PATCH 04/19] misc fixes to notifications and styling --- fuel/app/classes/controller/widgets.php | 4 +- fuel/app/classes/model/notification.php | 2 +- src/components/include.scss | 2 +- src/components/notifications.jsx | 11 +- .../user-admin-instance-available.jsx | 4 +- src/components/user-admin-page.scss | 178 ++++++++++-------- 6 files changed, 108 insertions(+), 93 deletions(-) diff --git a/fuel/app/classes/controller/widgets.php b/fuel/app/classes/controller/widgets.php index 31ccf4e59..f3283db64 100644 --- a/fuel/app/classes/controller/widgets.php +++ b/fuel/app/classes/controller/widgets.php @@ -508,12 +508,12 @@ protected function build_widget_login_messages($inst) { // $start_string = ''.date($format, (int) $inst->open_at).''; $start_string = date($format, (int) $inst->open_at); - $start_sec = date('h:i A', (int) $inst->open_at * 1000); + $start_sec = date('h:i A', (int) $inst->open_at); } if ($status['closes']) { $end_string = date($format, (int) $inst->close_at); - $end_sec = date('h:i A', (int) $inst->close_at * 1000); + $end_sec = date('h:i A', (int) $inst->close_at); } // finish the actual messages to the user diff --git a/fuel/app/classes/model/notification.php b/fuel/app/classes/model/notification.php index 1448caf52..b8fce1c3e 100644 --- a/fuel/app/classes/model/notification.php +++ b/fuel/app/classes/model/notification.php @@ -110,7 +110,7 @@ public static function send_item_notification(int $from_user_id, int $to_user_id break; case 'access_request': - $subject = "$user_link is requesting access to your widget \"$widget_name\".
The widget is currently being used within a course in your LMS."; + $subject = "$user_link is requesting access to your widget \"$widget_link\".
The widget is currently being used within a course in your LMS."; $action = 'access_request'; break; diff --git a/src/components/include.scss b/src/components/include.scss index 1f1539917..5f8cc04b0 100644 --- a/src/components/include.scss +++ b/src/components/include.scss @@ -417,7 +417,7 @@ header { .subject { line-height: 1.5em; - + a { font-weight: bold; text-decoration: underline; diff --git a/src/components/notifications.jsx b/src/components/notifications.jsx index 0d3a657d1..43ae0404b 100644 --- a/src/components/notifications.jsx +++ b/src/components/notifications.jsx @@ -11,7 +11,10 @@ const Notifications = (user) => { const queryClient = useQueryClient() const setUserPerms = setUserInstancePerms() const numNotifications = useRef(0); - const [errorMsg, setErrorMsg] = useState(''); + const [errorMsg, setErrorMsg] = useState({ + notif_id: '', + msg: '' + }); let modalRef = useRef(); const { data: notifications} = useQuery({ @@ -137,7 +140,7 @@ const Notifications = (user) => { window.location.hash = notif.item_id + '-collab'; } - setErrorMsg(''); + setErrorMsg({notif_id: notif.id, msg: ''}); removeNotification(-1, notif.id); @@ -147,7 +150,7 @@ const Notifications = (user) => { } else { - setErrorMsg('Action failed.'); + setErrorMsg({notif_id: notif.id, msg: 'Action failed.'}); } } }) @@ -199,7 +202,7 @@ const Notifications = (user) => { className={`noticeClose ${showDeleteBtn == index ? 'show' : ''}`} onClick={() => {removeNotification(index)}} /> -

{errorMsg}

+

{errorMsg.notif_id == notification.id ? errorMsg.msg : ''}

notificationIcon = diff --git a/src/components/user-admin-instance-available.jsx b/src/components/user-admin-instance-available.jsx index 54d9e4657..472e4bab4 100644 --- a/src/components/user-admin-instance-available.jsx +++ b/src/components/user-admin-instance-available.jsx @@ -39,7 +39,7 @@ const UserAdminInstanceAvailable = ({instance, index, currentUser}) => { return (
  • -
    setInstanceState(instanceState => ({...instanceState, expanded: !instanceState.expanded, manager: false}))}> @@ -53,7 +53,7 @@ const UserAdminInstanceAvailable = ({instance, index, currentUser}) => {
    - { !instanceState.manager ? + { !instanceState.manager ?
    diff --git a/src/components/user-admin-page.scss b/src/components/user-admin-page.scss index 93d56368f..dd626469b 100644 --- a/src/components/user-admin-page.scss +++ b/src/components/user-admin-page.scss @@ -23,15 +23,15 @@ border-radius: 3px; font-size: 12px; border: 1px solid #ccc; - + .breadcrumb { display: inline-block; - + a { color: black; } } - + svg { position: relative; height: 15px; @@ -84,12 +84,12 @@ margin: 5px; padding: 10px; border: 1px solid #d3d3d3; - + > div { display: inline-block; } } - + .searching { vertical-align: bottom; margin-top: 10px; @@ -172,7 +172,7 @@ .admin-subsection { padding: 15px; - + .top { width: calc(100% + 10px); left: -15px; @@ -181,73 +181,22 @@ &.overview { width: calc(100% - 20px); padding: 10px; - + div { display: block; margin: 0; } - + span { display: block; margin: 5px 0; } - + label { display: inline-block; vertical-align: top; width: 160px; } - - .radio { - display: inline-block; - // width: 200px; - - input { - margin: 0px 5px; - } - - label { - width: auto; - } - } - - .url { - display: inline-block; - vertical-align: middle; - // width: 500px; - font-size: 70%; - margin: 0px 0px; - } - - .right-justify { - display: flex; - justify-content: flex-end; - margin-top: 20px; - - .apply-changes { - display: flex; - flex-direction: column; - justify-content: flex-end; - width: 220px; - margin-right: 20px; - - .apply { - padding: 6px 10px 6px 10px; - } - - .error-text { - color: red; - font-size: 0.8em; - text-align: center; - } - - .success-text { - color: green; - font-size: 0.8em; - text-align: center; - } - } - } } &.role-manager { @@ -286,16 +235,16 @@ padding: 0; flex-direction: row; list-style-type: none; - + li.instance { margin: 0 0 10px 0; - + border-radius: 3px; - + .img-holder { display: inline-block; vertical-align: middle; - + img { width: 50px; height: 50px; @@ -305,30 +254,30 @@ margin-right: 10px; } } - + div.widget-title { display: flex; justify-content: flex-start; align-items: center; gap: 10px; padding: 10px 15px; - + background: $very-light-gray; border: none; border-radius: 3px; transition: background-color 0.5s; - + span.img-holder { flex-basis: 0; } - + span.incomplete-status-holder { color: #555; } - + span.title-holder { flex-grow: 1; - + .title { margin: 0; padding: 0; @@ -337,42 +286,105 @@ font-size: 20px; } } - + span.date-holder { font-size: 12px; font-weight: bold; } } - + .info-holder { display: none; } - + &.expanded { - + div.widget-title { background: $light-gray; border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; } - + .manage-btn-container { display: flex; justify-content: end; margin-top: 20px; } - + .info-holder { + position: relative; display: block; - padding: 15px; - background: $very-light-gray; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; - - line-height: 1.5em; + + padding: 10px; + + span:not(.long), + label { + display: inline-block; + vertical-align: top; + } + + label:not(.normal) { + width: 145px; + } + + .normal input { + margin: 0; + padding: 0; + } + } + .overview { + padding: 10px; + + div { + margin: 5px 0px; + } + + label { + display: inline-block; + vertical-align: top; + width: 150px; + } + + .url { + display: inline-block; + vertical-align: middle; + width: 500px; + font-size: 70%; + margin: 0px 0px; + } + + .right-justify { + margin-top: 20px; + display: flex; + justify-content: flex-end; + + .apply-changes { + display: flex; + flex-direction: column; + justify-content: flex-end; + width: 220px; + + .apply { + padding: 6px 10px 6px 10px; + } + + .error-text { + color: red; + font-size: 0.8em; + text-align: center; + } + + .success-text { + color: green; + font-size: 0.8em; + text-align: center; + } + } + } } } - } + + } } } } From e0b9bcba79e19b4bc8db03b0e6f662c2498114b4 Mon Sep 17 00:00:00 2001 From: Cay Henning Date: Thu, 5 Oct 2023 15:02:06 -0400 Subject: [PATCH 05/19] Encode apostrophes in widget search --- src/util/api.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/api.js b/src/util/api.js index 1008d1b4d..0df320f34 100644 --- a/src/util/api.js +++ b/src/util/api.js @@ -373,13 +373,14 @@ export const apiGetWidgetLock = (id = null) => { /** * It searches for widgets by name or ID - * @param {string} input (letters only) + * @param {string} input (must contain letters) * @returns {array} if matches were found * @returns {bool} if input does not match pattern */ export const apiSearchWidgets = input => { let pattern = /[A-Za-z]+/g if (!input.match(pattern).length) return false + input = input.replace("'","%27") return fetch(`/api/admin/widget_search/${input}`) .then(resp => { if (resp.status === 204 || resp.status === 502) return [] From e679de8893ef219d8084abe8c81e599a788a26a7 Mon Sep 17 00:00:00 2001 From: Cay Henning Date: Thu, 5 Oct 2023 15:42:21 -0400 Subject: [PATCH 06/19] Adds warning when trying to add a student collaborator to non-guest widget --- fuel/app/classes/materia/api/v1.php | 4 +++- fuel/app/classes/materia/msg.php | 5 +++++ src/components/my-widgets-collaborate-dialog.jsx | 11 +++++++++-- src/components/notifications.jsx | 11 +++++------ src/util/api.js | 4 ++++ 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/fuel/app/classes/materia/api/v1.php b/fuel/app/classes/materia/api/v1.php index b173b7be3..7c7ccc351 100644 --- a/fuel/app/classes/materia/api/v1.php +++ b/fuel/app/classes/materia/api/v1.php @@ -1058,7 +1058,9 @@ static public function permissions_set($item_type, $item_id, $perms_array) if ($is_enabled && Perm_Manager::is_student($new_perms->user_id)) { // guest mode isn't enabled - don't give this student access - if ( ! $inst->allows_guest_players()) continue; + if ( ! $inst->allows_guest_players()) { + return Msg::student_collab(); + } Perm_Manager::set_user_game_asset_perms($item_id, $new_perms->user_id, [Perm::VISIBLE => $is_enabled], $new_perms->expiration); } } diff --git a/fuel/app/classes/materia/msg.php b/fuel/app/classes/materia/msg.php index ede1cf363..2ef6a4a54 100644 --- a/fuel/app/classes/materia/msg.php +++ b/fuel/app/classes/materia/msg.php @@ -60,6 +60,11 @@ static public function no_perm() return new Msg('You do not have permission to access the requested content', 'Permission Denied', Msg::WARN); } + static public function student_collab() + { + return new Msg('Students cannot be added as collaborators to widgets that have guest access disabled.', 'Share Not Allowed', Msg::ERROR); + } + static public function student() { return new Msg('Students are unable to receive notifications via Materia', 'No Notifications', Msg::NOTICE); diff --git a/src/components/my-widgets-collaborate-dialog.jsx b/src/components/my-widgets-collaborate-dialog.jsx index ea0c0d28d..02115a08c 100644 --- a/src/components/my-widgets-collaborate-dialog.jsx +++ b/src/components/my-widgets-collaborate-dialog.jsx @@ -167,8 +167,15 @@ const MyWidgetsCollaborateDialog = ({onClose, inst, myPerms, otherUserPerms, set setUserPerms.mutate({ instId: inst.id, permsObj: permsObj, - successFunc: () => { - if (mounted.current) { + successFunc: (data) => { + if (data && data.type == 'error') + { + if (data.title == "Share Not Allowed") + { + setState({...state, shareNotAllowed: true}) + } + } + else if (mounted.current) { if (delCurrUser) { queryClient.invalidateQueries('widgets') } diff --git a/src/components/notifications.jsx b/src/components/notifications.jsx index 1357abe8f..a09cd2b83 100644 --- a/src/components/notifications.jsx +++ b/src/components/notifications.jsx @@ -121,7 +121,11 @@ const Notifications = (user) => { instId: notif.item_id, permsObj: userPerms, successFunc: (data) => { - if (data.status == 200) + if (!data || data.title == "error") + { + setErrorMsg('Action failed.'); + } + else if (data) { // Redirect to widget if (!window.location.pathname.includes('my-widgets')) @@ -143,11 +147,6 @@ const Notifications = (user) => { // Close notifications setNavOpen(false) - - } - else - { - setErrorMsg('Action failed.'); } } }) diff --git a/src/util/api.js b/src/util/api.js index 1008d1b4d..91231e41f 100644 --- a/src/util/api.js +++ b/src/util/api.js @@ -337,6 +337,10 @@ export const apiGetUserPermsForInstance = instId => { export const apiSetUserInstancePerms = ({ instId, permsObj }) => { return fetch('/api/json/permissions_set', fetchOptions({ body: `data=${formatFetchBody([objectTypes.WIDGET_INSTANCE, instId, permsObj])}` })) + .then(resp => { + if (resp.status === 204 || resp.status === 502) return null + return resp.json() + }) } export const apiCanEditWidgets = arrayOfWidgetIds => { From 1b8e0575cf6c7218bf08b9c5f4186d9d0b3f36d5 Mon Sep 17 00:00:00 2001 From: Cay Henning Date: Thu, 5 Oct 2023 15:53:48 -0400 Subject: [PATCH 07/19] PHP syntax error fix --- fuel/app/classes/materia/api/v1.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fuel/app/classes/materia/api/v1.php b/fuel/app/classes/materia/api/v1.php index 7c7ccc351..48278c7ca 100644 --- a/fuel/app/classes/materia/api/v1.php +++ b/fuel/app/classes/materia/api/v1.php @@ -1058,7 +1058,8 @@ static public function permissions_set($item_type, $item_id, $perms_array) if ($is_enabled && Perm_Manager::is_student($new_perms->user_id)) { // guest mode isn't enabled - don't give this student access - if ( ! $inst->allows_guest_players()) { + if ( ! $inst->allows_guest_players()) + { return Msg::student_collab(); } Perm_Manager::set_user_game_asset_perms($item_id, $new_perms->user_id, [Perm::VISIBLE => $is_enabled], $new_perms->expiration); From df75e7270720c43e55207b18130aee46e24f6016 Mon Sep 17 00:00:00 2001 From: Corey Peterson Date: Fri, 6 Oct 2023 16:45:12 -0400 Subject: [PATCH 08/19] Slightly increased requested pixel sizes for gravatars & rounded profile pic corners because it looks good --- fuel/app/classes/materia/utils.php | 2 +- fuel/app/classes/model/notification.php | 2 +- fuel/app/classes/model/user.php | 2 +- src/components/extra-attempts-dialog.scss | 1 + src/components/my-widgets-collaborate-dialog.scss | 1 + src/components/profile-page.scss | 1 + 6 files changed, 6 insertions(+), 3 deletions(-) diff --git a/fuel/app/classes/materia/utils.php b/fuel/app/classes/materia/utils.php index e7f002cfa..d811bcd82 100644 --- a/fuel/app/classes/materia/utils.php +++ b/fuel/app/classes/materia/utils.php @@ -23,7 +23,7 @@ public static function safeTrim($value) return is_string($value) ? trim($value) : $value; } - public static function get_avatar($size=35, $user=false) + public static function get_avatar($size=128, $user=false) { $default = \Config::get('materia.urls.static').'/img/default-avatar.jpg'; if ( ! $user) $user = \Model_User::find_current(); diff --git a/fuel/app/classes/model/notification.php b/fuel/app/classes/model/notification.php index b8fce1c3e..9c02fc572 100644 --- a/fuel/app/classes/model/notification.php +++ b/fuel/app/classes/model/notification.php @@ -133,7 +133,7 @@ public static function send_item_notification(int $from_user_id, int $to_user_id 'is_email_sent' => ($send_email ? '0' : '1'), 'is_read' => '0', 'subject' => $subject, - 'avatar' => \Materia\Utils::get_avatar(50), + 'avatar' => \Materia\Utils::get_avatar(128), 'action' => $action, 'created_at' => time() ]); diff --git a/fuel/app/classes/model/user.php b/fuel/app/classes/model/user.php index 69d12fe28..c29874777 100644 --- a/fuel/app/classes/model/user.php +++ b/fuel/app/classes/model/user.php @@ -155,7 +155,7 @@ static protected function forge_guest() public function to_array($custom = false, $recurse = false, $eav = false) { - $avatar = \Materia\Utils::get_avatar(50, $this); + $avatar = \Materia\Utils::get_avatar(256, $this); $array = parent::to_array($custom, $recurse, $eav); $array['avatar'] = $avatar; $array['is_student'] = \Materia\Perm_Manager::is_student($this->id); diff --git a/src/components/extra-attempts-dialog.scss b/src/components/extra-attempts-dialog.scss index 291a4b8d7..28fc47225 100644 --- a/src/components/extra-attempts-dialog.scss +++ b/src/components/extra-attempts-dialog.scss @@ -185,6 +185,7 @@ height: 40px; width: 40px; margin-right: 10px; + border-radius: 5px; } .user_name { diff --git a/src/components/my-widgets-collaborate-dialog.scss b/src/components/my-widgets-collaborate-dialog.scss index 1b1356d3c..49c664af7 100644 --- a/src/components/my-widgets-collaborate-dialog.scss +++ b/src/components/my-widgets-collaborate-dialog.scss @@ -279,6 +279,7 @@ height: 50px; width: 50px; margin-right: 10px; + border-radius: 5px; } .name { display: inline-block; diff --git a/src/components/profile-page.scss b/src/components/profile-page.scss index 40d5faf67..33fa08eaa 100644 --- a/src/components/profile-page.scss +++ b/src/components/profile-page.scss @@ -103,6 +103,7 @@ img { width: 90px; height: 90px; + border-radius: 10px; } a:link, From ebdc241717a407c912e34934bc83f6046976f42b Mon Sep 17 00:00:00 2001 From: Corey Peterson Date: Mon, 9 Oct 2023 11:17:44 -0400 Subject: [PATCH 09/19] Explicitly adding path.js to MWD package.json --- public/dist/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/dist/package.json b/public/dist/package.json index d9ebc93c8..4dad46c01 100644 --- a/public/dist/package.json +++ b/public/dist/package.json @@ -4,6 +4,7 @@ "author": "University of Central Florida, Center for Distributed Learning", "license": "AGPL-3.0", "files": [ + "path.js", "js/materia.js", "js/materia.enginecore.js", "js/materia.creatorcore.js", @@ -21,7 +22,7 @@ "js/scores.js", "css/scores.css" ], - "version": "0.1.0", + "version": "0.2.0", "repository": { "type": "git", "url": "https://github.com/ucfopen/Materia" From 7f1e1e923551a9b170fede8c7278725a72da8a61 Mon Sep 17 00:00:00 2001 From: Cay Henning Date: Tue, 10 Oct 2023 16:25:33 -0400 Subject: [PATCH 10/19] Update condition check to account for undefined response --- src/components/hooks/useSupportDeleteWidget.jsx | 2 +- src/components/hooks/useSupportUnDeleteWidget.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/hooks/useSupportDeleteWidget.jsx b/src/components/hooks/useSupportDeleteWidget.jsx index 07e76fbec..20d05c04f 100644 --- a/src/components/hooks/useSupportDeleteWidget.jsx +++ b/src/components/hooks/useSupportDeleteWidget.jsx @@ -8,7 +8,7 @@ export default function useSupportDeleteWidget() { apiDeleteWidget, { onSuccess: (data, variables) => { - if (data !== null) { + if (!!data) { variables.successFunc() queryClient.invalidateQueries('widgets') } diff --git a/src/components/hooks/useSupportUnDeleteWidget.jsx b/src/components/hooks/useSupportUnDeleteWidget.jsx index 5d481d76e..1f600f26f 100644 --- a/src/components/hooks/useSupportUnDeleteWidget.jsx +++ b/src/components/hooks/useSupportUnDeleteWidget.jsx @@ -8,7 +8,7 @@ export default function useSupportUnDeleteWidget() { apiUnDeleteWidget, { onSuccess: (data, variables) => { - if (data !== null) { + if (!!data) { variables.successFunc() queryClient.removeQueries('search-widgets', { exact: false From 3fd8e4ac9b48b47cceaca80b38ef9a4048b17211 Mon Sep 17 00:00:00 2001 From: Cay Henning Date: Tue, 10 Oct 2023 16:39:29 -0400 Subject: [PATCH 11/19] Continues instead of returning when attempting to add student --- fuel/app/classes/materia/api/v1.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fuel/app/classes/materia/api/v1.php b/fuel/app/classes/materia/api/v1.php index 48278c7ca..509e66ab8 100644 --- a/fuel/app/classes/materia/api/v1.php +++ b/fuel/app/classes/materia/api/v1.php @@ -1060,7 +1060,8 @@ static public function permissions_set($item_type, $item_id, $perms_array) // guest mode isn't enabled - don't give this student access if ( ! $inst->allows_guest_players()) { - return Msg::student_collab(); + $refused[] = $new_perms->user_id; + continue; } Perm_Manager::set_user_game_asset_perms($item_id, $new_perms->user_id, [Perm::VISIBLE => $is_enabled], $new_perms->expiration); } @@ -1081,6 +1082,11 @@ static public function permissions_set($item_type, $item_id, $perms_array) \Model_Notification::send_item_notification($cur_user_id, $new_perms->user_id, $item_type, $item_id, $notification_mode, $new_perm); } + if (count($refused) > 0) + { + return Msg::student_collab(); + } + return true; } /** From ba57bc1963f70c0a6b7bf1610de8cacff2568989 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 18:31:48 +0000 Subject: [PATCH 12/19] Bump semver from 5.7.1 to 5.7.2 Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2) --- updated-dependencies: - dependency-name: semver dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 334ab4e82..f58cb4a1a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7041,19 +7041,19 @@ semver-regex@^3.1.2: integrity sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA== "semver@2 || 3 || 4 || 5", semver@^5.7.0, semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.3.4, semver@^7.3.5, semver@^7.3.8: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" From bb4273c286aa59a841a8d228539b1c13e93805f2 Mon Sep 17 00:00:00 2001 From: Corey Peterson Date: Thu, 12 Oct 2023 15:23:44 -0400 Subject: [PATCH 13/19] Expanded help section to include Students and Instructors sections. Stylesheet cleanup, still WIP. --- src/components/help-for-instructors.jsx | 89 +++++++ src/components/help-for-students.jsx | 65 +++++ src/components/help-home.jsx | 54 +++++ src/components/help-page.jsx | 89 +++---- src/components/help-page.scss | 308 +++++++++++++----------- 5 files changed, 426 insertions(+), 179 deletions(-) create mode 100644 src/components/help-for-instructors.jsx create mode 100644 src/components/help-for-students.jsx create mode 100644 src/components/help-home.jsx diff --git a/src/components/help-for-instructors.jsx b/src/components/help-for-instructors.jsx new file mode 100644 index 000000000..afb48423e --- /dev/null +++ b/src/components/help-for-instructors.jsx @@ -0,0 +1,89 @@ +import React from 'react'; + +const HelpForInstructors = () => { + + return ( + <> +
    +

    For Instructors

    +

    Check out the Materia Quickstart Guide for a brief introduction to creating a new widget.

    +
    +
    +

    Common Questions & Issues

    +

    General

    +
    +
    Is Materia part of the LMS?
    +
    + No. Materia is a platform that operates independently to the LMS, but integrates with it through a protocol called LTI. + You can authenticate to Materia through LTI, or directly by visiting the Login button. +
    +
    Are there login credentials specific to Materia?
    +
    That depends on how your institution has configured Materia. If you visit Materia through your LMS, authentication is handled for you. If you login directly to Materia, + you may need credentials specifically set up for Materia. If logging in transitions you to a an external login, use the credentials associated with that external login instead. +
    +
    I'm an instructor, why does Materia think I'm suddenly a student?
    +
    + Materia defers to your LMS when receiving login information that may include your role in the course you last accessed Materia from. Depending on your course role, + your role in Materia may update to match. For example, if you are instructor in Course A but a student in Course B, Materia will flag you as a student if you visit + Materia content embedded in Course B. Visiting Materia content in Course A will once again grant you the author role. +
    +
    Can my students author widgets?
    +
    + Yes they can, but student-authored widgets have several limitations. Primarily, the widget is forced into Guest Mode, which removes the login requirement, but all scores + recorded by Guest Mode widgets are anonymous. Student-authored widgets cannot be embedded in your LMS, and if a student grants you access, you cannot disable Guest Mode. + Students can freely share their widgets with other users using the Play URL or Embed Code. +
    +
    +

    Scores & Grades

    +
    +
    Does Materia save student grades?
    +
    + Materia does not save grade information, but it does save student performance data. Student scores are stored and can be reviewed any time by + visiting the Student Activity section of a widget in My Widgets. When embedded in your LMS, Materia sends a score percentage to the LMS that's + then applied as a point value or grade by the LMS itself for an assignment. +
    +
    How does scoring work?
    +
    + By default, a student must authenticate to Materia - either automatically, when accessed through the LMS, or directly, by clicking the Login button - + so the system knows who they are and who to associate a score with. All score data for a widget is accessible under the Student Activity section in My Widgets. + Additionally, student performance data can be exported at any time by clicking the Export Options button in My Widgets. +
    +
    My widget is embedded in the LMS but the gradebook did not update! What happened?
    +
    This can happen for multiple reasons. First, ensure the widget is properly embedded in your LMS by reviewing our documentation for embedding widgets in Canvas. + Next, verify the score the student recieved by reviewing the widget's Student Activity section in Materia under My Widgets. If a score is present in Materia but not synced with the gradebook, contact Support. Often the "handshake" between Materia and the LMS can + fail or be invalidated for several reasons that are not nefarious.
    +
    +

    Widget Ownership

    +
    +
    Can I give other instructors access to a widget?
    +
    + Yes. Select Collaborate in My Widgets to share widgets with other users. You can search for any other Materia user in the search field, + provided they've interacted with Materia at least once in the past. You can grant other users one of two access levels: Full, which + effectively makes a user a co-owner, or View Scores, which allows a user to share and read score information related to a widget but they cannot edit it. +
    +
    Can I make students co-owners of a widget?
    +
    + You cannot share widgets with students unless they have been set to Guest Mode, under a widget's access settings. As a general rule, Materia will not + allow students ownership access to widgets unless the scores associated with the widgets are anonymized. Conversely, widgets authored by students are forced into Guest Mode, and changing a student widget + you co-own to Normal Access will remove the student as an owner. +
    +
    I see a widget embedded in my course in the LMS, but I don't own it. What do I do?
    +
    Don't panic! Even if you don't own the widget, it will continue to function normally, and sync scores with the gradebook if configured to do so. If you're an author of the course, + you will be provided with a list of owners of the widget, and can select Request Access from any owner. The owner will receive a notification in Materia + and be provided with an option to add you as a collaborator. Note that this does not guarantee you will receive access! In cases where the original owner is no longer associated with your institution, feel free to + contact Support. +
    +
    +

    Miscellaneous

    +
    +
    How do I activate Beard Mode?
    +
    + What? We don't know what you're talking about. +
    +
    +
    + + ) +} + +export default HelpForInstructors diff --git a/src/components/help-for-students.jsx b/src/components/help-for-students.jsx new file mode 100644 index 000000000..630739269 --- /dev/null +++ b/src/components/help-for-students.jsx @@ -0,0 +1,65 @@ +import React from 'react'; + +const HelpForStudents = () => { + + return ( + <> +
    +

    For Students

    +

    + Materia is an open-source educational platform hosted alongside your LMS to enhance course content with interactive tools + called widgets. If you run into a problem playing a widget or receiving a score, we're here to help. +

    +
    +
    +

    Get Support

    +

    If you need help beyond what has been provided here, please contact support using one of the following:

    +
    +
    Support
    +
    http://website/support/
    +
    Email
    +
    support@website
    +
    Phone
    +
    PHONE NUMBER HERE
    +
    +
    +
    +

    Common Questions & Issues

    +
    +
    I'm not seeing a widget, only an error message. What do I do?
    +
    For one reason or a another Materia encountered a server issue or an issue authenticating you. Contact support using the info above.
    +
    +
    +
    I completed a widget, but my grade didn't update. What happened?
    +
    +

    Widgets should automatically update the gradebook with your score when embedded correctly in an assignment. If a widget is embedded as a module item or directly in a regular course page, + the gradebook sync will not occur. Additionally, ensure you have advanced the widget all the way to the score screen, + which is when the play session is formally concluded. Leaving the play session early will prevent the play from being marked as complete.

    +

    If all items above check out, contact support. Support can verify the score and ensure your grade is properly updated.

    +
    +
    How many times can I play a widget?
    +
    + By default, widgets can be played as many times as you want. When embedded as assignments, your highest score is the one synced with the gradebook. If your instructor has set an attempt limit, + the additional attempt count will be displayed on the score screen alongside the Play Again button. +
    +
    Can I see scores from widgets I've previously played in Materia?
    +
    + You can! Log in to Materia directly, if you're not already authenticated. Then, visit your profile by clicking your name at the top right, or the + My Profile link the header. Your profile page will list your complete play history for every widget you've previously interacted with. +
    +
    Can I make my own widget?
    +
    + Students can make widgets, but there's one important limitation: the widget will be set to Guest Mode, which means it won't know who is playing it, and consequently, score + data will be anonymous. +
    +
    What's the answer to life, the universe, and everything?
    +
    + That's a really big question, but we're pretty sure the answer is 42. +
    +
    +
    + + ) +} + +export default HelpForStudents diff --git a/src/components/help-home.jsx b/src/components/help-home.jsx new file mode 100644 index 000000000..322191548 --- /dev/null +++ b/src/components/help-home.jsx @@ -0,0 +1,54 @@ +import React from 'react'; + +const HelpHome = () => { + + return ( + <> +
    + + lorem ipsum +
    +
    +

    Requirements

    +

    Materia requires that you use an up-to-date browser with javascript and cookies enabled.

    +
    + +
    +

    Login Issues

    +

    In many cases, problems logging in are a result of one of the following:

    + +

    Incorrect Password

    +

    You may need to reset your password.

    + +

    Expired Password

    +

    You may need to reset your password.

    + +

    User Account Doesn't exist

    +

    Your user account may not have been created yet.

    +
    + +
    +

    Support

    +

    If you need help beyond what has been provided here, please contact support using one of the following:

    +
    +
    Support
    +
    http://website/support/
    +
    Email
    +
    support@website
    +
    Phone
    +
    PHONE NUMBER HERE
    +
    +
    + +
    + +

    View the docs for guides on using Materia.

    + +

    Player/Student Guide

    +

    Author/Instructor Guide

    +
    + + ) +} + +export default HelpHome diff --git a/src/components/help-page.jsx b/src/components/help-page.jsx index de45170a7..412325eb7 100644 --- a/src/components/help-page.jsx +++ b/src/components/help-page.jsx @@ -1,58 +1,59 @@ -import React from 'react' +import React, { useState, useEffect } from 'react' import Header from './header' +import HelpHome from './help-home' +import HelpForStudents from './help-for-students' +import HelpForInstructors from './help-for-instructors' + import './help-page.scss'; const HelpPage = () => { + + const [page, setPage] = useState(window.location.hash.match(/#(home|students|instructors){1}$/)?.[1]) + + const listenToHashChange = () => { + const match = window.location.hash.match(/#(home|students|instructors){1}$/) + if (match != null && match[1] != null) setPage(match[1]) + else setPage('home') + } + + useEffect(() => { + window.addEventListener('hashchange', listenToHashChange) + + return () => { + window.removeEventListener('hashchange', listenToHashChange) + } + }, []) + + let helpContentRender = null + switch (page) { + case 'students': + helpContentRender = + break + case 'instructors': + helpContentRender = + break + default: + helpContentRender = + } + return ( <>

    Help & Support

    -
    - -

    Check out the Materia Quickstart Guide.

    -
    - -
    -

    Requirements

    -

    Materia requires that you use an up-to-date browser with javascript and cookies enabled.

    -
    - -
    -

    Login Issues

    -

    In many cases, problems logging in are a result of one of the following:

    - -

    Incorrect Password

    -

    You may need to reset your password.

    - -

    Expired Password

    -

    You may need to reset your password.

    - -

    User Account Doesn't exist

    -

    Your user account may not have been created yet.

    -
    - -
    - -

    View the docs for guides on using Materia.

    - -

    Player/Student Guide

    -

    Author/Instructor Guide

    -
    - -
    -

    Support

    -

    If you need help beyond what has been provided here, please contact support using one of the following:

    -
    -
    Support
    -
    http://website/support/
    -
    Email
    -
    support@website
    -
    Phone
    -
    PHONE NUMBER HERE
    -
    +
    + +
    + {helpContentRender} +
    diff --git a/src/components/help-page.scss b/src/components/help-page.scss index 83529ad15..3cbd015a7 100644 --- a/src/components/help-page.scss +++ b/src/components/help-page.scss @@ -1,145 +1,183 @@ @import './include.scss'; -@media (max-width: 850px) { - .docs .container .page { - padding: 40px 20px 20px 20px !important; - margin: 15px 30px !important; - } - - .docs .container .page section.float.right { - float: none !important; - width: 100% !important; +.docs { + .container { + display: flex; + justify-content: center; + position: relative; + + font-family: 'Lato', arial, serif; + + .page { + z-index: 100; + max-width: 800px; + min-height: 400px; + margin: 15px 60px 60px 60px; + padding: 40px; + overflow: auto; + + border-radius: 4px; + border: #e4e4e4 1px solid; + + box-shadow: 1px 3px 10px #dcdcdc; + background-color: #fff; + + text-align: left; + font-size: 14px; + + .content { + display: flex; + align-items: stretch; + + nav { + flex-basis:30%; + min-width: 150px; + min-height: 100%; + margin-right: 20px; + + border-right: solid 0.5px #dcdcdc; + + ul { + margin: 0; + padding: 0; + + li { + display: block; + padding: 0.5em 0 0.25em 0; + margin-bottom: 0.5em; + + border-bottom: solid 0.5px #dcdcdc; + + a { + color: #0093e7; + text-decoration: none; + } + } + } + } + + main { + flex-grow: 1; + display: flex; + flex-direction: row; + align-items: flex-start; + flex-wrap: wrap; + + section { + &.full-width { + flex-basis: 100%; + flex-grow: 2; + } + + &.half-width { + flex-basis: 45%; + flex-grow: 1; + margin-right: 2.5%; + } + } + } + } + } + + h1 { + margin: 0 0 30px 0; + padding: 0 0 20px 0; + + border-bottom: solid #dcdcdc .5px; + + font-weight: 700; + font-size: 38px; + } + + h2 { + margin: 20px 0 0 0; + + font-weight: 400; + font-size: 20px; + } + + h3 { + margin: 30px 0 0 0; + + font-size: 16px; + } + + h3 + p { + margin-top: 0.5em; + } + + a { + text-decoration: underline; + } + + span.bold { + font-weight: bold; + } + + dl { + dt { + font-weight: bold; + font-size: 12px; + } + + dd { + margin: 0.5em 0 0 0; + padding-bottom: 10px; + } + } + + section { + margin-bottom: 0px; + padding-bottom: 15px; + clear: both; + + &.float { + width: 25%; + clear: none; + float: left; + + &.right { + width: 40%; + float: right; + } + + &.left { + width: 53%; + float: left; + } + } + + &.bordered { + border-bottom: solid #dcdcdc .5px + } + + &.bordered-top { + border-top: solid #dcdcdc .5px + } + + } } - - .docs .container .page section.float.left { - width: 100% !important; - } - - .docs .container .page section.float.left { - margin-bottom: 20px !important; - } -} - -.docs .container { - display: flex; - justify-content: center; - // margin: 0 auto; - // width: 970px; - position: relative; - - font-family: 'Lato', arial, serif; -} - -.docs .container .page { - border-radius: 4px; - border: #e4e4e4 1px solid; - box-shadow: 1px 3px 10px #dcdcdc; - - margin: 15px 50px; - z-index: 100; - min-height: 400px; - text-align: left; - font-size: 14px; - - overflow: auto; - - width: 685px; - // background: url('/img/question_mark_circle.png') 780px 30px no-repeat; - - background-color: #fff; - margin-bottom: 60px; - - padding: 40px 215px 20px 60px; -} - -.docs .container h1 { - - font-weight: 700; - font-size: 38px; - padding: 0 0 5px 0; - padding-bottom: 20px; - margin: 0 0 30px 0; - border-bottom: solid #dcdcdc .5px; -} - -.docs .container h2 { - font-weight: 400; - font-size: 20px; - margin: 20px 0 0 0; -} - -.docs .container .page section { - margin-bottom: 0px; - padding-bottom: 15px; - clear: both; -} - -.docs .container .page section.float { - width: 25%; - clear: none; - float: left; -} - -.docs .container .page section.float.right { - width: 40%; - float: right; -} - -.docs .container .page section.float.left { - width: 53%; - float: left; -} - -.docs .container .page section.bordered { - border-bottom: solid #dcdcdc .5px -} - -.docs .container .page section.bordered-top { - border-top: solid #dcdcdc .5px } -.docs .container h3 { - margin: 30px 0 0 0; - font-size: 12px; -} +@media (max-width: 850px) { -.docs .container h3 + p { - margin-top: 0.5em; -} + .docs { -.docs .container a { - text-decoration: underline; -} + .container { + .page { + padding: 40px 20px 20px 20px !important; + margin: 15px 30px !important; -.get_flash { - background: url('/img/get_flashplayer.gif'); - width: 160px; - display: block; - margin: 0 auto; - text-indent: -9999px; - background-repeat: no-repeat; - height: 41px; -} + section.float.right { + float: none !important; + width: 100% !important; + } -.get_flash:active { - background-position: 0 2px; -} - -.docs .container dl dt { - font-weight: bold; - font-size: 12px; -} -.docs .container dl dd { - padding-bottom: 10px; - margin: 0.5em 0 0 0; -} - -.docs .no_flash, -.docs .flash_installed { - background: #f7f7f7; - border-radius: 5px; - padding: 15px 20px; - text-align: center; - font-size: 15px; - color: #4a4a4a; + section.float.left { + width: 100% !important; + margin-bottom: 20px !important; + } + } + } + } } From 828c8de60eede3805da1e7ffe81d65d2c54e535f Mon Sep 17 00:00:00 2001 From: Corey Peterson Date: Thu, 12 Oct 2023 15:32:19 -0400 Subject: [PATCH 14/19] Moved things around slightly on the help-home page --- src/components/help-home.jsx | 14 ++++++-------- src/components/help-page.scss | 4 ++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/help-home.jsx b/src/components/help-home.jsx index 322191548..60cc3f8a1 100644 --- a/src/components/help-home.jsx +++ b/src/components/help-home.jsx @@ -5,8 +5,10 @@ const HelpHome = () => { return ( <>
    - - lorem ipsum + +

    Having issues with Materia or don't know where to get started? We're here to help. For first-time users, get more information about Materia from the links below.

    +

    Player/Student Guide

    +

    Author/Instructor Guide

    Requirements

    @@ -39,13 +41,9 @@ const HelpHome = () => {
    PHONE NUMBER HERE
    - -
    +
    -

    View the docs for guides on using Materia.

    - -

    Player/Student Guide

    -

    Author/Instructor Guide

    +

    Our docs site has extensive documentation for students, authors, support users, and developers.

    ) diff --git a/src/components/help-page.scss b/src/components/help-page.scss index 3cbd015a7..7fac242fc 100644 --- a/src/components/help-page.scss +++ b/src/components/help-page.scss @@ -114,6 +114,10 @@ font-weight: bold; } + span.italic { + font-style: italic; + } + dl { dt { font-weight: bold; From 7c913fc8453428dd05ec8b470673c3d2f20d148a Mon Sep 17 00:00:00 2001 From: Corey Peterson Date: Thu, 12 Oct 2023 15:54:02 -0400 Subject: [PATCH 15/19] Adds responsive layout to help page --- src/components/help-for-instructors.jsx | 48 +++++++++-------- src/components/help-for-students.jsx | 20 +++---- src/components/help-page.scss | 70 +++++++++++-------------- 3 files changed, 67 insertions(+), 71 deletions(-) diff --git a/src/components/help-for-instructors.jsx b/src/components/help-for-instructors.jsx index afb48423e..0207a8530 100644 --- a/src/components/help-for-instructors.jsx +++ b/src/components/help-for-instructors.jsx @@ -14,71 +14,75 @@ const HelpForInstructors = () => {
    Is Materia part of the LMS?
    - No. Materia is a platform that operates independently to the LMS, but integrates with it through a protocol called LTI. - You can authenticate to Materia through LTI, or directly by visiting the Login button. +

    No. Materia is a platform that operates independently to the LMS, but integrates with it through a protocol called LTI. + You can authenticate to Materia through LTI, or directly by visiting the Login button.

    Are there login credentials specific to Materia?
    -
    That depends on how your institution has configured Materia. If you visit Materia through your LMS, authentication is handled for you. If you login directly to Materia, - you may need credentials specifically set up for Materia. If logging in transitions you to a an external login, use the credentials associated with that external login instead. +
    +

    That depends on how your institution has configured Materia. If you visit Materia through your LMS, authentication is handled for you. If you login directly to Materia, + you may need credentials specifically set up for Materia. If logging in transitions you to a an external login, use the credentials associated with that external login instead.

    I'm an instructor, why does Materia think I'm suddenly a student?
    - Materia defers to your LMS when receiving login information that may include your role in the course you last accessed Materia from. Depending on your course role, +

    Materia defers to your LMS when receiving login information that may include your role in the course you last accessed Materia from. Depending on your course role, your role in Materia may update to match. For example, if you are instructor in Course A but a student in Course B, Materia will flag you as a student if you visit - Materia content embedded in Course B. Visiting Materia content in Course A will once again grant you the author role. + Materia content embedded in Course B. Visiting Materia content in Course A will once again grant you the author role.

    Can my students author widgets?
    - Yes they can, but student-authored widgets have several limitations. Primarily, the widget is forced into Guest Mode, which removes the login requirement, but all scores +

    Yes they can, but student-authored widgets have several limitations. Primarily, the widget is forced into Guest Mode, which removes the login requirement, but all scores recorded by Guest Mode widgets are anonymous. Student-authored widgets cannot be embedded in your LMS, and if a student grants you access, you cannot disable Guest Mode. - Students can freely share their widgets with other users using the Play URL or Embed Code. + Students can freely share their widgets with other users using the Play URL or Embed Code.

    Scores & Grades

    Does Materia save student grades?
    - Materia does not save grade information, but it does save student performance data. Student scores are stored and can be reviewed any time by +

    Materia does not save grade information, but it does save student performance data. Student scores are stored and can be reviewed any time by visiting the Student Activity section of a widget in My Widgets. When embedded in your LMS, Materia sends a score percentage to the LMS that's - then applied as a point value or grade by the LMS itself for an assignment. + then applied as a point value or grade by the LMS itself for an assignment.

    How does scoring work?
    - By default, a student must authenticate to Materia - either automatically, when accessed through the LMS, or directly, by clicking the Login button - +

    By default, a student must authenticate to Materia - either automatically, when accessed through the LMS, or directly, by clicking the Login button - so the system knows who they are and who to associate a score with. All score data for a widget is accessible under the Student Activity section in My Widgets. - Additionally, student performance data can be exported at any time by clicking the Export Options button in My Widgets. + Additionally, student performance data can be exported at any time by clicking the Export Options button in My Widgets.

    My widget is embedded in the LMS but the gradebook did not update! What happened?
    -
    This can happen for multiple reasons. First, ensure the widget is properly embedded in your LMS by reviewing our documentation for embedding widgets in Canvas. - Next, verify the score the student recieved by reviewing the widget's Student Activity section in Materia under My Widgets. If a score is present in Materia but not synced with the gradebook, contact Support. Often the "handshake" between Materia and the LMS can - fail or be invalidated for several reasons that are not nefarious.
    +
    +

    This can happen for multiple reasons. First, ensure the widget is properly embedded in your LMS by reviewing our documentation for embedding widgets in Canvas. + Next, verify the score the student recieved by reviewing the widget's Student Activity section in Materia under My Widgets. If a score is present in Materia but not synced with the gradebook, contact Support. Often the "handshake" between Materia and the LMS can + fail or be invalidated for several reasons that are not nefarious.

    +

    Widget Ownership

    Can I give other instructors access to a widget?
    - Yes. Select Collaborate in My Widgets to share widgets with other users. You can search for any other Materia user in the search field, +

    Yes. Select Collaborate in My Widgets to share widgets with other users. You can search for any other Materia user in the search field, provided they've interacted with Materia at least once in the past. You can grant other users one of two access levels: Full, which - effectively makes a user a co-owner, or View Scores, which allows a user to share and read score information related to a widget but they cannot edit it. + effectively makes a user a co-owner, or View Scores, which allows a user to share and read score information related to a widget but they cannot edit it.

    Can I make students co-owners of a widget?
    - You cannot share widgets with students unless they have been set to Guest Mode, under a widget's access settings. As a general rule, Materia will not +

    You cannot share widgets with students unless they have been set to Guest Mode, under a widget's access settings. As a general rule, Materia will not allow students ownership access to widgets unless the scores associated with the widgets are anonymized. Conversely, widgets authored by students are forced into Guest Mode, and changing a student widget - you co-own to Normal Access will remove the student as an owner. + you co-own to Normal Access will remove the student as an owner.

    I see a widget embedded in my course in the LMS, but I don't own it. What do I do?
    -
    Don't panic! Even if you don't own the widget, it will continue to function normally, and sync scores with the gradebook if configured to do so. If you're an author of the course, +
    +

    Don't panic! Even if you don't own the widget, it will continue to function normally, and sync scores with the gradebook if configured to do so. If you're an author of the course, you will be provided with a list of owners of the widget, and can select Request Access from any owner. The owner will receive a notification in Materia and be provided with an option to add you as a collaborator. Note that this does not guarantee you will receive access! In cases where the original owner is no longer associated with your institution, feel free to - contact Support. + contact Support.

    Miscellaneous

    How do I activate Beard Mode?
    - What? We don't know what you're talking about. +

    What? We don't know what you're talking about.

    diff --git a/src/components/help-for-students.jsx b/src/components/help-for-students.jsx index 630739269..6420efaef 100644 --- a/src/components/help-for-students.jsx +++ b/src/components/help-for-students.jsx @@ -27,9 +27,9 @@ const HelpForStudents = () => {

    Common Questions & Issues

    I'm not seeing a widget, only an error message. What do I do?
    -
    For one reason or a another Materia encountered a server issue or an issue authenticating you. Contact support using the info above.
    -
    -
    +
    +

    For one reason or a another Materia encountered a server issue or an issue authenticating you. Contact support using the info above.

    +
    I completed a widget, but my grade didn't update. What happened?

    Widgets should automatically update the gradebook with your score when embedded correctly in an assignment. If a widget is embedded as a module item or directly in a regular course page, @@ -39,22 +39,22 @@ const HelpForStudents = () => {

    How many times can I play a widget?
    - By default, widgets can be played as many times as you want. When embedded as assignments, your highest score is the one synced with the gradebook. If your instructor has set an attempt limit, - the additional attempt count will be displayed on the score screen alongside the Play Again button. +

    By default, widgets can be played as many times as you want. When embedded as assignments, your highest score is the one synced with the gradebook. If your instructor has set an attempt limit, + the additional attempt count will be displayed on the score screen alongside the Play Again button.

    Can I see scores from widgets I've previously played in Materia?
    - You can! Log in to Materia directly, if you're not already authenticated. Then, visit your profile by clicking your name at the top right, or the - My Profile link the header. Your profile page will list your complete play history for every widget you've previously interacted with. +

    You can! Log in to Materia directly, if you're not already authenticated. Then, visit your profile by clicking your name at the top right, or the + My Profile link the header. Your profile page will list your complete play history for every widget you've previously interacted with.

    Can I make my own widget?
    - Students can make widgets, but there's one important limitation: the widget will be set to Guest Mode, which means it won't know who is playing it, and consequently, score - data will be anonymous. +

    Students can make widgets, but there's one important limitation: the widget will be set to Guest Mode, which means it won't know who is playing it, and consequently, score + data will be anonymous.

    What's the answer to life, the universe, and everything?
    - That's a really big question, but we're pretty sure the answer is 42. +

    That's a really big question, but we're pretty sure the answer is 42.

    diff --git a/src/components/help-page.scss b/src/components/help-page.scss index 7fac242fc..91a2b7421 100644 --- a/src/components/help-page.scss +++ b/src/components/help-page.scss @@ -64,6 +64,9 @@ flex-wrap: wrap; section { + margin-bottom: 0px; + padding-bottom: 15px; + &.full-width { flex-basis: 100%; flex-grow: 2; @@ -129,57 +132,46 @@ padding-bottom: 10px; } } + } +} - section { - margin-bottom: 0px; - padding-bottom: 15px; - clear: both; - - &.float { - width: 25%; - clear: none; - float: left; - - &.right { - width: 40%; - float: right; - } - - &.left { - width: 53%; - float: left; - } - } - - &.bordered { - border-bottom: solid #dcdcdc .5px - } +@media (max-width: 850px) { - &.bordered-top { - border-top: solid #dcdcdc .5px + .docs { + .container { + .page { + padding: 40px 20px 20px 20px; + margin: 15px 30px; } - } } } -@media (max-width: 850px) { - +@media (max-width: 720px) { .docs { - .container { .page { - padding: 40px 20px 20px 20px !important; - margin: 15px 30px !important; + .content { + flex-direction: column; - section.float.right { - float: none !important; - width: 100% !important; - } + nav { + flex-basis: 100%; + min-width: 100%; + + border-right: none; - section.float.left { - width: 100% !important; - margin-bottom: 20px !important; + ul { + max-width: 90%; + + li { + padding-left: 5%; + } + } + } + + main { + flex-basis: 100%; + } } } } From 4029f125e311e713c75dce73dc6ab1a9815f1af5 Mon Sep 17 00:00:00 2001 From: Corey Peterson Date: Mon, 16 Oct 2023 16:16:14 -0400 Subject: [PATCH 16/19] Fixes score merging in playscoresget query when handling paginated score results --- src/components/my-widgets-score-semester-individual.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/my-widgets-score-semester-individual.jsx b/src/components/my-widgets-score-semester-individual.jsx index ce6994872..5c20c0dca 100644 --- a/src/components/my-widgets-score-semester-individual.jsx +++ b/src/components/my-widgets-score-semester-individual.jsx @@ -41,7 +41,7 @@ const MyWidgetScoreSemesterIndividual = ({ semester, instId }) => { let newLogs = state.logs result.pagination.forEach((record) => { - if (newLogs[record.userId]) newLogs[record.userId].scores.push({...record.scores}) + if (newLogs[record.userId]) newLogs[record.userId].scores.push(...record.scores) else newLogs[record.userId] = { userId: record.userId, name: record.name, searchableName: record.searchableName, scores: record.scores } newLogs[record.userId].scores.sort(_compareScores) }) From 33a8535ffcd3c1762bbb5d457c64abc933cf6701 Mon Sep 17 00:00:00 2001 From: Corey Peterson Date: Tue, 17 Oct 2023 10:26:15 -0400 Subject: [PATCH 17/19] Adjusts font scaling of dts. Slight fix to text in homepage (unrelated). --- src/components/help-page.scss | 4 ++-- src/components/homepage.jsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/help-page.scss b/src/components/help-page.scss index 91a2b7421..a611dfb63 100644 --- a/src/components/help-page.scss +++ b/src/components/help-page.scss @@ -43,7 +43,7 @@ li { display: block; - padding: 0.5em 0 0.25em 0; + padding: 0.5em 0 0.3em 0; margin-bottom: 0.5em; border-bottom: solid 0.5px #dcdcdc; @@ -124,7 +124,7 @@ dl { dt { font-weight: bold; - font-size: 12px; + font-size: 14px; } dd { diff --git a/src/components/homepage.jsx b/src/components/homepage.jsx index 4d5e0b5ed..bc594b455 100644 --- a/src/components/homepage.jsx +++ b/src/components/homepage.jsx @@ -59,8 +59,8 @@ const Homepage = () => (

    Engage Your Students

    Re-imagine your course filled with diverse and interesting experiences. - It can bring life to content modules, practice, study activities, and even assessments. - Engage students with game mechanics like: story-telling, competition, instant feedback, and instant reward systems. + Materia can bring life to content modules, practice, study activities, and even assessments. + Engage students with game mechanics like story-telling, competition, instant feedback, and instant reward systems.

    screen shot of a sort it out widget @@ -70,7 +70,7 @@ const Homepage = () => (

    Integrate with Your Course

    Materia integrates into Canvas seamlessly. - As an assignment, student's scores can automatically sync to the grade book. + As an assignment, students' scores can automatically sync to the grade book. Thanks to the magic of LTI, Students are logged in automatically!

    From b5c7bf26878f81de31a5f8df0f04d70bf062bfa7 Mon Sep 17 00:00:00 2001 From: Corey Peterson Date: Wed, 18 Oct 2023 11:26:47 -0400 Subject: [PATCH 18/19] Updates materia-theme-ucf to 2.0.2 in composer --- composer.json | 6 +++--- composer.lock | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 6232c425f..bda1f1286 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "eher/oauth": "1.0.7", "aws/aws-sdk-php": "3.67.17", "symfony/dotenv": "^5.1", - "ucfopen/materia-theme-ucf": "2.0.1" + "ucfopen/materia-theme-ucf": "2.0.2" }, "suggest": { "ext-memcached": "*" @@ -97,9 +97,9 @@ "package": { "name": "ucfopen/materia-theme-ucf", "type": "fuel-package", - "version": "2.0.1", + "version": "2.0.2", "dist": { - "url": "https://github.com/ucfopen/Materia-Theme-UCF/archive/refs/tags/v2.0.1.zip", + "url": "https://github.com/ucfopen/Materia-Theme-UCF/archive/refs/tags/v2.0.2.zip", "type": "zip" }, "source": { diff --git a/composer.lock b/composer.lock index dd0ada3be..904ea0ff7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6cd12e58a2eadc64a08ed0a8f5d0cf24", + "content-hash": "f25597c889d3990ca0401225ac22f8a2", "packages": [ { "name": "aws/aws-sdk-php", @@ -1972,7 +1972,7 @@ }, { "name": "ucfopen/materia-theme-ucf", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/ucfopen/Materia-Theme-UCF.git", @@ -1980,7 +1980,7 @@ }, "dist": { "type": "zip", - "url": "https://github.com/ucfopen/Materia-Theme-UCF/archive/refs/tags/v2.0.1.zip" + "url": "https://github.com/ucfopen/Materia-Theme-UCF/archive/refs/tags/v2.0.2.zip" }, "type": "fuel-package" } From 082ad93b21e5be466077d42830f767ff4f916402 Mon Sep 17 00:00:00 2001 From: Corey Peterson Date: Tue, 24 Oct 2023 09:15:52 -0400 Subject: [PATCH 19/19] Bumps base ruby image for fakes3 to 2.6 to address deprecation. Fix avatar scaling in user admin search. --- materia-fake-s3.Dockerfile | 2 +- src/components/user-admin-page.scss | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/materia-fake-s3.Dockerfile b/materia-fake-s3.Dockerfile index 73a821cef..8e2a2d169 100644 --- a/materia-fake-s3.Dockerfile +++ b/materia-fake-s3.Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:2.5-alpine +FROM ruby:2.6-alpine RUN gem install fakes3 -v 2.0.0 --no-document diff --git a/src/components/user-admin-page.scss b/src/components/user-admin-page.scss index dd626469b..2082ee035 100644 --- a/src/components/user-admin-page.scss +++ b/src/components/user-admin-page.scss @@ -87,6 +87,18 @@ > div { display: inline-block; + + &.img-holder { + display: inline-block; + + img { + width: 50px; + height: 50px; + border-radius: 3px; + display: inline-block; + margin-right: 10px; + } + } } }