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

Issue/settings dialog #1540

Merged
merged 17 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 8 additions & 3 deletions fuel/app/classes/materia/api/v1.php
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@ static public function widget_instance_update($inst_id=null, $name=null, $qset=n
// student made widgets are locked forever
if ($inst->is_student_made)
{
if ($guest_access === false)
{
return new Msg('Student-made widgets must stay in guest access mode.', 'Student Made', 'error', false);
}
$attempts = -1;
$guest_access = true;
}
Expand Down Expand Up @@ -346,7 +350,7 @@ static public function widget_instance_update($inst_id=null, $name=null, $qset=n
$access = Perm_Manager::get_all_users_explicit_perms($inst_id, Perm::INSTANCE)['widget_user_perms'];
foreach ($access as $user_id => $user_perms)
{
if (Perm_Manager::is_student($user_id))
if (Perm_Manager::is_student($user_id) && $user_id != $inst->user_id)
{
\Model_Notification::send_item_notification(\Model_user::find_current_id(), $user_id, Perm::INSTANCE, $inst_id, 'disabled', null);
Perm_Manager::clear_user_object_perms($inst_id, Perm::INSTANCE, $user_id);
Expand Down Expand Up @@ -662,11 +666,12 @@ static public function guest_widget_instance_scores_get($inst_id, $play_id)
*/
static public function play_logs_get($inst_id, $semester = 'all', $year = 'all', $page_number=1)
{
if ( ! Util_Validator::is_valid_hash($inst_id)) return Msg::invalid_input($inst_id);
if ( ! Util_Validator::is_valid_hash($inst_id)) return Msg::invalid_input($inst_id);
if (\Service_User::verify_session() !== true) return Msg::no_login();
if ( ! static::has_perms_to_inst($inst_id, [Perm::VISIBLE, Perm::FULL])) return Msg::no_perm();
$is_student = ! \Service_User::verify_session(['basic_author', 'super_user']);

$data = Session_Play::get_by_inst_id_paginated($inst_id, $semester, $year, $page_number);
$data = Session_Play::get_by_inst_id_paginated($inst_id, $semester, $year, $page_number, $is_student);
return $data;
}

Expand Down
4 changes: 2 additions & 2 deletions fuel/app/classes/materia/perm/manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ static public function is_super_user()
// The session caching has been removed due to issues related to the cache when the role is added or revoked
// Ideally we can still find a way to cache this and make it more performant!!
return (\Fuel::$is_cli === true && ! \Fuel::$is_test) || self::does_user_have_role([\Materia\Perm_Role::SU]);

}

/**
Expand Down Expand Up @@ -351,10 +351,10 @@ static public function remove_users_from_roles_system_only(Array $user_ids = [],
->execute();
}
}

return $success;
}


/*
********************** User to Object Rights ***************************************
*/
Expand Down
39 changes: 35 additions & 4 deletions fuel/app/classes/materia/session/play.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public function resume($play_id)
* Must be fast because it can be asked to retrieve large data sets
*
*/
public static function get_by_inst_id($inst_id, $semester='all', $year='all')
public static function get_by_inst_id($inst_id, $semester='all', $year='all', $is_student=false)
{
if ($semester != 'all' && $year != 'all')
{
Expand All @@ -230,7 +230,23 @@ public static function get_by_inst_id($inst_id, $semester='all', $year='all')

if (is_null($plays))
{
$query = \DB::select(
// if user is student, do not query user information
if ($is_student)
{
$query = \DB::select(
's.id',
['s.created_at', 'time'],
['s.is_complete', 'done'],
['s.percent', 'perc'],
['s.elapsed', 'elapsed'],
['s.qset_id', 'qset_id']
)
->from(['log_play', 's'])
->where('s.inst_id', $inst_id);
}
else
{
$query = \DB::select(
's.id',
['s.created_at', 'time'],
['s.is_complete', 'done'],
Expand All @@ -246,6 +262,7 @@ public static function get_by_inst_id($inst_id, $semester='all', $year='all')
->join(['users', 'u'], 'LEFT OUTER')
->on('u.id', '=', 's.user_id')
->where('s.inst_id', $inst_id);
}

if (isset($date))
{
Expand All @@ -256,14 +273,28 @@ public static function get_by_inst_id($inst_id, $semester='all', $year='all')

\Cache::set('play-logs.'.$inst_id.'.'.$cache_id, $plays);
}
else
{
// if user is student, do not show user information
if ($is_student)
{
foreach ($plays as &$play)
{
$play['user_id'] = 0;
unset($play['first']);
unset($play['last']);
unset($play['username']);
}
}
}

return $plays;
}

public static function get_by_inst_id_paginated($inst_id, $semester='all', $year='all', $page_number=1)
public static function get_by_inst_id_paginated($inst_id, $semester='all', $year='all', $page_number=1, $is_student=false)
{
$items_per_page = 100;
$data = self::get_by_inst_id($inst_id, $semester, $year);
$data = self::get_by_inst_id($inst_id, $semester, $year, $is_student);
$total_num_pages = ceil(sizeof($data) / $items_per_page);
$offset = $items_per_page * ($page_number - 1);
$page = array_slice($data, $offset, $items_per_page);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"d3": "^7.2.0",
"fs-extra": "^8.0.1",
"js-base64": "^3.7.2",
"react-datepicker": "^4.8.0",
"react-datepicker": "^5.0.0",
"react-overlays": "^5.2.1",
"react-query": "^3.39.2",
"uuid": "^9.0.1"
Expand Down
33 changes: 29 additions & 4 deletions src/components/attempts-slider.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react'
import './my-widgets-settings-dialog.scss'

const AttemptsSlider = ({inst, parentState, setParentState}) => {
const AttemptsSlider = ({inst, is_student, parentState, setParentState, currentAttemptsVal}) => {

const [rawSliderVal, setRawSliderVal] = useState(parseInt(parentState.sliderVal))
const [sliderStopped, setSliderStopped] = useState(false)
Expand All @@ -16,12 +16,19 @@ const AttemptsSlider = ({inst, parentState, setParentState}) => {
const sliderStop = e => {
setSliderStopped(true)
}

// now that the slider value isn't actively changing, round the raw value to the nearest stop
// pass that rounded value up to the parent component
useEffect(() => {
if (sliderStopped && parentState.formData.changes.access != 'guest') {
const sliderInfo = getSliderInfo(rawSliderVal)
// students cannot change attempts to anything other than
// the original number of attempts or unlimited
if (is_student && sliderInfo.val != currentAttemptsVal && sliderInfo.val != '100') {
setSliderStopped(false)
setRawSliderVal(parseInt(parentState.sliderVal))
return
}
setParentState({...parentState, sliderVal: sliderInfo.val, lastActive: sliderInfo.last})
setSliderStopped(false)
}
Expand Down Expand Up @@ -66,21 +73,26 @@ const AttemptsSlider = ({inst, parentState, setParentState}) => {
const updateSliderNum = (val, index) => {
// Attempts always unlimited when guest access is true
if (parentState.formData.changes.access === 'guest') return
if (is_student && val != currentAttemptsVal && val != '100') return

setParentState({...parentState, sliderVal: val.toString(), lastActive: index})
}

const generateStopSpan = (stopId, sliderPosition, display) => {
const spanClass = parentState.lastActive === stopId ? 'active' : ''
const stopClickHandler = () => updateSliderNum(sliderPosition, stopId)
return (
<span key={stopId}
className={spanClass}
className={`${parentState.lastActive === stopId ? 'active' : ''}`}
onClick={stopClickHandler}>
{display}
</span>
)
}
const selectChange = e => {
if (parentState.formData.changes.access === 'guest') return
let sliderInfo = getSliderInfo(parseInt(e.target.value))
setParentState({...parentState, sliderVal: sliderInfo.val, lastActive: sliderInfo.last})
}

let guestModeRender = null
if (parentState.formData.changes.access === 'guest') {
Expand All @@ -93,6 +105,19 @@ const AttemptsSlider = ({inst, parentState, setParentState}) => {

return (
<div className='data-holder'>
<div className ={`mobile selector ${parentState.formData.changes.access === 'guest' ? 'disabled' : ''}`}>
<select onChange={selectChange} value={parentState.sliderVal}>
<option value='1'>1</option>
<option value='5'>2</option>
<option value='9'>3</option>
<option value='13'>4</option>
<option value='17'>5</option>
<option value='39'>10</option>
<option value='59'>15</option>
<option value='79'>20</option>
<option value='100'>Unlimited</option>
</select>
</div>
<div className={`selector ${parentState.formData.changes.access === 'guest' ? 'disabled' : ''}`}>
<input id='ui-slider'
aria-label='attempts-input'
Expand Down
2 changes: 1 addition & 1 deletion src/components/hooks/useSupportUpdateWidget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function useSupportUpdateWidget() {
},
onError: (err, newWidget, context) => {
queryClient.setQueryData('widgets', context.previousValue)

variables.errorFunc()
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/components/hooks/useUpdateWidget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export default function useUpdateWidget() {
return { ...widgetList }
},
onSuccess: (updatedInst, variables) => {

// update successful - insert new values into our local copy of widgetList
for (const page of widgetList?.pages) {
for (const inst of page?.pagination) {
Expand All @@ -33,7 +32,7 @@ export default function useUpdateWidget() {
}
}
}

// update query cache for widgets. This does NOT invalidate the cache, forcing a re-fetch!!
queryClient.setQueryData('widgets', previous => {
return {
Expand Down
7 changes: 4 additions & 3 deletions src/components/modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import './modal.scss'
const Modal = (props) => {

const innerModalRef = useRef(null)
const innerModalOverlayRef = useRef(null)

const clickOutsideListener = (event) => {
// Do nothing if clicking ref's element or descendent elements
if (!innerModalRef.current || innerModalRef.current.contains(event.target)) return
if (!innerModalOverlayRef.current.contains(event.target)) return
if (props.ignoreClose != true) props.onClose()
}

Expand All @@ -25,9 +26,9 @@ const Modal = (props) => {

},[])

const modal =
const modal =
(<>
<div className={`modal-overlay ${props.alert ? 'alert' : ''}`} id='modal-overlay'></div>
<div ref={innerModalOverlayRef} className={`modal-overlay ${props.alert ? 'alert' : ''}`} id='modal-overlay'></div>
<div ref={innerModalRef} className={`modal ${props.smaller ? 'small' : ''} ${props.noGutter ? 'no-gutter' : ''}`} id='inner-modal'>
<span className='close-button'
id='close-button'
Expand Down
12 changes: 8 additions & 4 deletions src/components/modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
/* This way it could be display flex or grid or whatever also. */
display: flex;

max-width: 100%;
max-height: 100%;
min-width: 500px;
width: 100%;
max-width: 600px;
max-height: 95%;
min-height: 300px;

@media screen and (min-width: 620px) {
min-width: 620px;
}

position: fixed;

z-index: 1001;
Expand Down Expand Up @@ -61,7 +65,7 @@
top: 0;
left: 0;
width: 100%;
height: 100%;
max-height: 100%;
padding: 10px 10px 10px 10px;
box-sizing: border-box;

Expand Down
6 changes: 6 additions & 0 deletions src/components/my-widgets-collaborate-dialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,12 @@ const MyWidgetsCollaborateDialog = ({onClose, inst, myPerms, otherUserPerms, set
return <div key={userId}></div>
}

if (user.id == inst.user_id) {
user.is_owner = true;
} else {
user.is_owner = false;
}

return <CollaborateUserRow
key={user.id}
user={user}
Expand Down
17 changes: 13 additions & 4 deletions src/components/my-widgets-collaborate-dialog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
position: relative;
text-align: left;
display: block;

font-weight: bold;
}

Expand Down Expand Up @@ -72,10 +72,10 @@
width: 447px;
padding-bottom: 5px;
overflow: auto;

background-color: #ffffff;
border: #bfbfbf 1px solid;

text-align: left;

.collab-search-match {
Expand All @@ -101,7 +101,7 @@
margin: 5px 0 0 5px;
font-size: 14px;
text-align: left;

font-family: 'Lucida Grande', sans-serif;
}
}
Expand Down Expand Up @@ -296,6 +296,15 @@
font-size: 11px;
color: gray;
}

&.user-match-owner:after {
content: 'Owner';
position: absolute;
top: -12px;
left: 0;
font-size: 11px;
color: gray;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/my-widgets-collaborate-user-row.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ const CollaborateUserRow = ({user, perms, myPerms, isCurrentUser, onChange, read
<div className='about'>
<img className='avatar' src={user.avatar} />

<span className={`name ${user.is_student ? 'user-match-student' : ''}`}>
<span className={`name ${user.is_owner ? 'user-match-owner' : user.is_student ? 'user-match-student' : ''}`}>
{`${user.first} ${user.last}`}
</span>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/components/my-widgets-scores.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ const MyWidgetsScores = ({inst, beardMode}) => {
<div className='scores'>
<h2>Student Activity</h2>
<span id='export_scores_button'
className={`aux_button ${inst.is_draft ? 'disabled' : ''}`}
onClick={openExport}>
className={`aux_button ${inst.is_draft ? 'disabled' : ''}`}
onClick={openExport}>
<span className='arrow_down'></span>
Export Options
</span>
Expand Down
Loading
Loading