Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[React branch] Fixes and polish part five #1492

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
751336a
Adjusts action button padding in instance admin panels; fixes widget …
clpetersonucf Aug 23, 2023
a5a32a4
Instance admin style cleanup
clpetersonucf Aug 23, 2023
b1607c8
Fixes to url hash matching in my-widgets page
clpetersonucf Aug 24, 2023
8df96fb
b tags are gross
clpetersonucf Aug 24, 2023
747da7b
Implements player support for the legacy LTI assignment route
clpetersonucf Aug 25, 2023
f07b4a1
Cleaned out additional console logs
clpetersonucf Aug 25, 2023
6c149d7
Enables full-height widget players for widgets that do not provide a …
clpetersonucf Aug 28, 2023
9a7f796
Fixed useUpdatedWidget mutation to correctly iterate through widget l…
clpetersonucf Aug 29, 2023
82bb496
Fixes catalog filtering display
clpetersonucf Sep 5, 2023
497bfd7
Major revisions to score screen component, split it into several new …
clpetersonucf Sep 12, 2023
023bdcb
Additional score screen polish, especially related to error states
clpetersonucf Sep 13, 2023
6c7edbe
Fixes & usability improvements to user admin instance history display
clpetersonucf Sep 13, 2023
8a11b5e
Fix to expired score screen preview again href & incomplete score pag…
clpetersonucf Sep 13, 2023
a52196d
Cleaned up some hooks, fixed race conditions, bug fixes, disabled sco…
clpetersonucf Sep 14, 2023
c0d8af1
Fixed creator action button update behavior after saving. Added time …
clpetersonucf Sep 15, 2023
b353306
Small style tweaks to homepage, replaced screenshot assets
clpetersonucf Sep 15, 2023
b879fa8
Reimplements no-activity state for profile page
clpetersonucf Sep 18, 2023
efe9fa2
Fixes to customscorescreen not updating when a new attempt is selected
clpetersonucf Sep 18, 2023
0db3924
Fix to profile page assets prematurely rendering
clpetersonucf Sep 18, 2023
ff87db3
Adds role management interface to user admin panel, only available to…
clpetersonucf Sep 19, 2023
b2c274c
Merge pull request #4 from clpetersonucf/react/add-user-admin-role-ma…
clpetersonucf Sep 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions fuel/app/classes/controller/api/user.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,58 @@ public function post_settings()

return $this->response($reply);
}

public function post_roles()
{
if (\Service_User::verify_session() !== true) return $this->response('Not logged in', 401);
// this endpoint is only available to superusers!
if ( ! \Materia\Perm_Manager::is_super_user()) return $this->response('Not authorized', 403);

$success = false;
$user_id = Input::json('id', null);
$roles = [
'basic_author' => Input::json('author', false),
'support_user' => Input::json('support_user', false)
];

if ( ! $user_id) return $this->response('User ID not provided', 401);

$current_roles = \Materia\Perm_Manager::get_user_roles($user_id);
$current_roles_condensed = array_map( fn($r) => $r->name, $current_roles);

$roles_to_add = [];
$roles_to_revoke = [];

foreach ($roles as $name => $val)
{
if ( ! in_array($name, $current_roles_condensed) && $val == true) array_push($roles_to_add, $name);
else if (in_array($name, $current_roles_condensed) && $val == false) array_push($roles_to_revoke, $name);
}

$message = '';

if (count($roles_to_add) > 0)
{
$success = \Materia\Perm_Manager::add_users_to_roles([$user_id], $roles_to_add);
if ($success != true) return $this->response(['success' => false, 'status' => 'Failed to add roles']);
$message .= count($roles_to_add).' role(s) added.';
}

if (count($roles_to_revoke) > 0)
{
$success = \Materia\Perm_Manager::remove_users_from_roles([$user_id], $roles_to_revoke);
if ($success != true) return $this->response(['success' => false, 'status' => 'Failed to revoke roles']);
$message .= count($roles_to_revoke).' role(s) revoked.';
}

if (strlen($message) == 0)
{
$message .= 'No roles were changed.';
}

return $this->response([
'success' => true,
'status' => $message
]);
}
}
2 changes: 2 additions & 0 deletions fuel/app/classes/controller/widgets.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ public function action_play_widget($inst_id = false)

public function action_play_embedded($inst_id = false)
{
// if routed from the legacy LTI URL, the instance id is available as a GET parameter
if ( ! $inst_id && \Input::get('widget') ) $inst_id = \Input::get('widget');
// context_id isolates attempt count for an class so a user's attempt limit is reset per course
Session::set('context_id', \Input::post('context_id'));
return $this->_play_widget($inst_id, false, true);
Expand Down
3 changes: 1 addition & 2 deletions fuel/app/classes/materia/perm/manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -280,12 +280,11 @@ static public function get_user_roles($user_id = 0)
->as_object();

$current_id = \Model_User::find_current_id();
$roles = [];

// return logged in user's roles if id is 0 or less, non su users can only use this method
if ($user_id <= 0 || $user_id == $current_id)
{
$roles = [];

$results = $q->where('m.user_id', $current_id)
->execute();

Expand Down
10 changes: 5 additions & 5 deletions fuel/app/classes/model/notification.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,23 +94,23 @@ public static function send_item_notification(int $from_user_id, int $to_user_id
switch ($mode)
{
case 'disabled':
$subject = "<b>$user_link is no longer sharing \"$widget_name\" with you.</b>";
$subject = "$user_link is no longer sharing \"$widget_name\" with you.";
break;

case 'changed':
$subject = "<b>$user_link changed your access to widget \"$widget_link\".</b><br/> You now have $perm_string access.";
$subject = "$user_link changed your access to widget \"$widget_link\".<br/> You now have $perm_string access.";
break;

case 'expired':
$subject = "<b>Your access to \"$widget_name\" has automatically expired.</b>";
$subject = "Your access to \"$widget_name\" has automatically expired.";
break;

case 'deleted':
$subject = "<b>$user_link deleted $widget_type widget \"$widget_name\".</b>";
$subject = "$user_link deleted $widget_type widget \"$widget_name\".";
break;

case 'access_request':
$subject = "<b>$user_link is requesting access to your widget \"$widget_name\".</b><br /> The widget is currently being used within a course in your LMS.";
$subject = "$user_link is requesting access to your widget \"$widget_name\".<br /> The widget is currently being used within a course in your LMS.";
$action = 'access_request';
break;

Expand Down
1 change: 1 addition & 0 deletions fuel/app/classes/model/user.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ public function to_array($custom = false, $recurse = false, $eav = false)
$array['avatar'] = $avatar;
$array['is_student'] = \Materia\Perm_Manager::is_student($this->id);
$array['is_support_user'] = \Materia\Perm_Manager::does_user_have_role([\Materia\Perm_Role::SUPPORT], $this->id);
if (\Materia\Perm_Manager::does_user_have_role([\Materia\Perm_Role::SU], $this->id)) $array['is_super_user'] = true;
return $array;
}

Expand Down
Binary file modified public/img/front1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/img/front2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/img/front3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/retina/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/retina/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/retina/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions src/components/catalog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ const Catalog = ({widgets = [], isLoading = true}) => {
const [filteredWidgets, isFiltered] = useMemo(() => {
let isFiltered = false

// widgets with the in_catalog flag set to false should not be included by default
// in_catalog widgets are already being rendered via featured widgets
// append remaining widgets that are playable but not in_catalog
let results = widgets.filter(w => {
return parseInt(w.in_catalog)
return parseInt(w.is_playable) == 1 && parseInt(w.in_catalog) == 0
})
// filters are active, only match active filters
if(state.activeFilters.length){
Expand Down
2 changes: 1 addition & 1 deletion src/components/header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ const Header = ({
// Not being used
profileMenuRender = (
<nav className={`profile-menu ${optionsOpen ? 'show' : ''}`}>
<span class="arrow-top"></span>
<span className="arrow-top"></span>
<ul>
<li>
<span>{`${user.first} ${user.last}`}</span>
Expand Down
8 changes: 4 additions & 4 deletions src/components/homepage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const Homepage = () => (
<section className='front_bottom'>
<section className='wrapper'>
<div className="wrapper_first">
<div>
<div className='wrapper_content'>
<h2>Create Quickly and Easily</h2>
<p className="front_bottom_desc">
Materia's design philosophy is to be incredibly easy to use.
Expand All @@ -55,18 +55,18 @@ const Homepage = () => (
<img src='/img/front2.png' alt='screen shot of creating a crossword widget'/>
</div>
<div className="wrapper_second">
<div>
<div className='wrapper_content'>
<h2>Engage Your Students</h2>
<p className="front_bottom_desc">
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.
</p>
</div>
<img src='/img/front1.png' alt='screen shot of a labeling widget' />
<img src='/img/front1.png' alt='screen shot of a sort it out widget' />
</div>
<div className="wrapper_third">
<div>
<div className='wrapper_content'>
<h2>Integrate with Your Course</h2>
<p className="front_bottom_desc">
Materia integrates into Canvas seamlessly.
Expand Down
43 changes: 34 additions & 9 deletions src/components/homepage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
margin-right: 40px;
height: 400px;

p {
text-align: left;
}

h1 span.engage {
color: #0093e7;
}
Expand Down Expand Up @@ -108,6 +112,7 @@
padding-top: 65px;

.wrapper {
max-width: 1400px;
margin: 0 auto;
}
div {
Expand All @@ -120,26 +125,45 @@
text-align: center;
display: grid;

div.wrapper_content {
margin: 0 0 20px 0;
}

h2 {
margin-bottom: 6px;
font-size: 30px;
font-weight: 500;
color: #000;
border-bottom: dotted 1px #666;
}

&.wrapper_first, &.wrapper_second, &.wrapper_third {
margin: 0 10%;
margin: 0 10% 20px 10%;
justify-items: center;
}

&.wrapper_first, &.wrapper_third {
p {
margin-right: 25px;
}
}

&.wrapper_second {
p {
margin-left: 25px;
}
}

p {
font-size: 16px;
line-height: 150%;
}
}

img {
border: solid 2px #ffffff;
box-shadow: 1px 2px 4px #888;
max-width: 290px;
padding: 5px;
border: dotted 1px #666;
margin-top: 20px;
}
img:nth-of-type(2) {
Expand All @@ -155,21 +179,21 @@
}
.front_bottom {
div {
margin: 0px 110px 20px 50px !important;
text-align: left !important;
margin: 0 0 20px 0;
// margin: 0px 110px 20px 50px;
text-align: left;

&.wrapper_first, &.wrapper_second, &.wrapper_third {
display: inline-flex;
align-items: center;
}

&.wrapper_second {
margin: 0px 50px 20px 110px !important;
// margin: 0px 50px 20px 110px;
flex-direction: row-reverse;

div {
text-align: right !important;
margin-right: 50px !important;
text-align: right;
}

img {
Expand All @@ -180,8 +204,9 @@
}
}
.p_s {
max-width: 1400px;
text-align: center;
margin: 30px 40px;
margin: 30px 10%;

h2 {
font-size: 30px;
Expand Down
30 changes: 30 additions & 0 deletions src/components/hooks/useUpdateUserRoles.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useMutation, useQueryClient } from 'react-query'
import { apiUpdateUserRoles } from '../../util/api'

export default function useUpdateUserRoles() {

const queryClient = useQueryClient()

return useMutation(
apiUpdateUserRoles,
{
onMutate: async roles => {
await queryClient.cancelQueries('search-users')
const val = {...queryClient.getQueryData('search-users')}
const prior = queryClient.getQueryData('search-users')

queryClient.setQueryData('search-users', () => val)

return { prior }
},
onSuccess: (data, variables, context) => {
queryClient.invalidateQueries('search-users')
variables.successFunc(data)
},
onError: (err, newRoles, context) => {
queryClient.setQueryData('search-users', context.previousValue)
return err
}
}
)
}
18 changes: 11 additions & 7 deletions src/components/hooks/useUpdateWidget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,19 @@ export default function useUpdateWidget() {
onSuccess: (updatedInst, variables) => {

// update successful - insert new values into our local copy of widgetList
for (const inst of widgetList?.pagination) {
if (inst.id === variables.args[0]) {
inst.open_at = `${variables.args[4]}`
inst.close_at = `${variables.args[5]}`
inst.attempts = `${variables.args[6]}`
inst.guest_access = variables.args[7]
inst.embedded_only = variables.args[8]
for (const page of widgetList?.pages) {
for (const inst of page?.pagination) {
if (inst.id === variables.args[0]) {
inst.open_at = `${variables.args[4]}`
inst.close_at = `${variables.args[5]}`
inst.attempts = `${variables.args[6]}`
inst.guest_access = variables.args[7]
inst.embedded_only = variables.args[8]
break
}
}
}


// update query cache for widgets. This does NOT invalidate the cache, forcing a re-fetch!!
queryClient.setQueryData('widgets', widgetList)
Expand Down
9 changes: 9 additions & 0 deletions src/components/include.scss
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,15 @@ header {
display: flex;
flex-direction: column;
gap: 5px;

.subject {
line-height: 1.5em;

a {
font-weight: bold;
text-decoration: underline;
}
}
}
.grantAccessTitle
{
Expand Down
Loading
Loading