Commit af17fa4d authored by mathieso's avatar mathieso

New view for class-level submissions mgt, other things.

parent 675f7e7b
......@@ -595,6 +595,7 @@ views.view.character_grid_header_list.yml so
views.view.classes_administration.yml so
views.view.class_progress.yml so
views.view.class_progress_admin_view_header.yml so
views.view.class_submissions_admin_view_header.yml so
views.view.course_contents.yml so
views.view.design_page_administration.yml so
views.view.enrollments_administration.yml so
......@@ -623,9 +624,13 @@ views.view.rubric_item_categories.yml so
views.view.rubric_items_with_no_categories.yml so
views.view.submissions.yml so
views.view.submissions_administration.yml so
views.view.submissions_administration_for_class.yml so
views.view.suggestions_administration.yml so
views.view.user_details_header_contextual_filter_.yml so
# CUSTOM TIME
core.date_format.trimmed_date.yml co
......
......@@ -317,65 +317,31 @@ skilling.admin.characters.add_character:
description: 'Add a new character.'
weight: 20
# Skilling | Classes and events
skilling.admin.classes_events:
# Skilling | Classes
skilling.admin.classes:
title: 'Classes'
route_name: skilling.admin.classes_events
route_name: skilling.admin.classes
parent: system.admin.skilling
description: 'Manage classes.'
weight: 20
# Skilling | Classes and events | List classes
# Skilling | Classes | List classes
skilling.admin.classes_events.list_classes:
title: 'List classes'
route_name: view.classes_administration.administer
parent: skilling.admin.classes_events
parent: skilling.admin.classes
description: 'Show the current classes.'
weight: 0
# Skilling | Classes and events | Add class
skilling.admin.classes_events.add_class:
# Skilling | Classes | Add class
skilling.admin.classes.add_class:
title: 'Add class'
route_name: node.add
route_parameters: { node_type: 'class' }
parent: skilling.admin.classes_events
parent: skilling.admin.classes
description: 'Add a new class.'
weight: 20
# Skilling | Classes and events | List calendars
#skilling.admin.classes_events.list_calendars:
# title: 'List calendars'
# route_name: view.calendar_administration.administer
# parent: skilling.admin.classes_events
# description: 'Show the current calendars.'
# weight: 40
# Skilling | Classes and events | Add calendar
#skilling.admin.classes_events.add_calendar:
# title: 'Add calendar'
# route_name: node.add
# route_parameters: { node_type: 'calendar' }
# parent: skilling.admin.classes_events
# description: 'Add a new calendar.'
# weight: 60
# Skilling | Classes and events | List events
#skilling.admin.classes_events.list_events:
# title: 'List events'
# route_name: view.events.event_list
# parent: skilling.admin.classes_events
# description: 'Show the current events.'
# weight: 80
# Skilling | Classes and events | Add event
#skilling.admin.classes_events.add_event:
# title: 'Add event'
# route_name: node.add
# route_parameters: { node_type: 'event' }
# parent: skilling.admin.classes_events
# description: 'Add a new event.'
# weight: 100
# Skilling | Enrollments
skilling.admin.enrollments:
title: 'Enrollments'
......
......@@ -250,9 +250,9 @@ skilling.admin.characters:
options:
_admin_route: TRUE
# Skilling | Classes and events
skilling.admin.classes_events:
path: '/admin/skilling/classes_events'
# Skilling | Classes
skilling.admin.classes:
path: '/admin/skilling/classes'
defaults:
_controller: '\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage'
_title: 'Skilling classes'
......
......@@ -452,7 +452,7 @@ class SkillingAccessChecker {
// - Students see and edit their own.
// - Instructors see and edit submissions from students in
// their classes.
// Graders don't have direct access, only through the grading interface.
// Graders can't access.
// Rules apply for view and edit operations.
// Admins can view submissions.
if ($admin) {
......@@ -3125,6 +3125,31 @@ class SkillingAccessChecker {
return $currentTeachesStudent;
}
/**
* Is current user grader of submitter?
*
* Check whether the current user is a grader of the student who
* submitted an exercise submission.
*
* @param \Drupal\node\NodeInterface $submssion
* The submission.
*
* @return bool
* True if current user is submitting student's grader.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\skilling\Exception\SkillingException
*/
protected function isCurrentUserGraderOfSubmissionStudent(NodeInterface $submssion) {
$studentUid = $submssion->get(SkillingConstants::FIELD_USER)->target_id;
$student = $this->skillingUserFactory->makeSkillingUser($studentUid);
$currentTeachesStudent = $this->checkUserRelationshipsService->isUserGraderOfUser(
$this->currentUser, $student
);
return $currentTeachesStudent;
}
/**
* Does the current user own the given node?
*
......
<?php
namespace Drupal\skilling\Plugin\views\access;
use Drupal\skilling\SkillingCurrentUser;
use Drupal\views\Plugin\views\access\AccessPluginBase;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* An access plugin for views.
*
* It inspects a context filter argument,
* interpreting it as a class nid. If the current user is an instructor
* or grader of the class, allows access.
*
* @ingroup views_access_plugins
*
* @ViewsAccess(
* id = "InstructorGraderAccess",
* title = @Translation("Instructor or grader of class"),
* help = @Translation("Access based on whether the current user is an instructor or grader of a class with node id in a contextual filter parameter."),
* )
*/
class ViewsInstructorGraderOfClassAccess extends AccessPluginBase {
/**
* {@inheritdoc}
*/
protected $usesOptions = FALSE;
/**
* Skilling current user service.
*
* @var \Drupal\skilling\SkillingCurrentUser
*/
protected $skillingCurrentUser;
/**
* Constructs an InstructorAccess object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\skilling\SkillingCurrentUser $skillingCurrentUser
* Skilling current user service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, SkillingCurrentUser $skillingCurrentUser) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->skillingCurrentUser = $skillingCurrentUser;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
/* @noinspection PhpParamsInspection */
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('skilling.skilling_current_user')
);
}
/**
* Is current user instructor of class with nid in contextual filter argument?
*
* @param \Drupal\Core\Session\AccountInterface $account
* The user who wants to access this view.
*
* @return bool
* Returns whether the user has access to the view.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function access(AccountInterface $account) {
// This works, but is positional. Is there a better way?
if (!isset($this->view->element['#arguments'][0])) {
return FALSE;
}
$classId = $this->view->element['#arguments'][0];
if (!$classId || !is_numeric($classId)) {
return FALSE;
}
$isClassInstructor = $this->skillingCurrentUser->isInstructorOfClassNid($classId);
$isClassGrader = $this->skillingCurrentUser->isGraderOfClassNid($classId);
$isAdmin = $this->skillingCurrentUser->isAdministrator();
return $isClassInstructor || $isClassGrader || $isAdmin;
}
/**
* Allows access plugins to alter the route definition of a view.
*
* Some requirement has to be added here. Check .services.yml
* for a tag.
*
* @param \Symfony\Component\Routing\Route $route
* The route to change.
*/
public function alterRouteDefinition(Route $route) {
$route->setRequirement('_access', 'TRUE');
}
/**
* {@inheritdoc}
*/
public function getCacheMaxAge() {
return 0;
}
/**
* Determine whether configuration is valid.
*/
protected function isValidConfig() {
return TRUE;
}
}
<?php
namespace Drupal\skilling\Plugin\views\access;
use Drupal\skilling\SkillingConstants;
use Drupal\views\Plugin\views\access\AccessPluginBase;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* An access plugin for views. It inspects a context filter argument,
* interpreting it as a class nid. If the current user is an instructor
* or grader of the class, allows access.
*
* @ingroup views_access_plugins
*
* @ViewsAccess(
* id = "ViewsInstructorGraderOfStudentAccess",
* title = @Translation("Instructor or grader of student"),
* help = @Translation("Access based on whether the current user is an instructor or grader of the user with uid in a contextual filter parameter."),
* )
*/
class ViewsInstructorGraderOfStudentAccess extends AccessPluginBase {
/**
* {@inheritdoc}
*/
protected $usesOptions = FALSE;
/** @var \Drupal\skilling\SkillingCurrentUser */
protected $skillingCurrentUser;
/**
* Constructs an InstructorAccess object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\skilling\SkillingCurrentUser $skillingCurrentUser
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, \Drupal\skilling\SkillingCurrentUser $skillingCurrentUser) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->skillingCurrentUser = $skillingCurrentUser;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
/** @noinspection PhpParamsInspection */
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('skilling.skilling_current_user')
);
}
/**
* Determine if the current user is an instructor of the
* class identified in a contextual filter argument.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The user who wants to access this view.
*
* @return bool
* Returns whether the user has access to the view.
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function access(AccountInterface $account) {
//This works, but is positional. Is there a better way?
if (! isset($this->view->element['#arguments'][0])) {
return FALSE;
}
$possibleStudentId = $this->view->element['#arguments'][0];
if (!$possibleStudentId || !is_numeric($possibleStudentId)) {
return FALSE;
}
$currentUserAsInstructorStudents
= $this->skillingCurrentUser->getStudentsNidsOfSpecialClassRole(
SkillingConstants::CLASS_ROLE_INSTRUCTOR
);
$currentUserAsGraderStudents
= $this->skillingCurrentUser->getStudentsNidsOfSpecialClassRole(
SkillingConstants::CLASS_ROLE_GRADER
);
$isStudentsInstructor = in_array($possibleStudentId, $currentUserAsInstructorStudents);
$isStudentsGrader = in_array($possibleStudentId, $currentUserAsGraderStudents);
$isAdmin = $this->skillingCurrentUser->isAdministrator();
return $isStudentsInstructor || $isStudentsGrader || $isAdmin;
}
/**
* Allows access plugins to alter the route definition of a view.
*
* Some requirement has to be added here. Don't know why. Check .services.yml
* for a tag.
*
* @param \Symfony\Component\Routing\Route $route
* The route to change.
*/
public function alterRouteDefinition(Route $route) {
$route->setRequirement('_access', 'TRUE');
// $route->setRequirement('_instructor_check', 'TRUE');
}
/**
* {@inheritdoc}
*/
public function getCacheMaxAge() {
return 0;
}
}
<?php
namespace Drupal\skilling\Plugin\views\access;
use Drupal\skilling\SkillingConstants;
use Drupal\views\Plugin\views\access\AccessPluginBase;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\Routing\Route;
......@@ -68,6 +69,7 @@ class ViewsInstructorOfStudentAccess extends AccessPluginBase {
* @return bool
* Returns whether the user has access to the view.
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function access(AccountInterface $account) {
//This works, but is positional. Is there a better way?
......@@ -78,7 +80,10 @@ class ViewsInstructorOfStudentAccess extends AccessPluginBase {
if (!$possibleStudentId || !is_numeric($possibleStudentId)) {
return FALSE;
}
$currentUsersStudents = $this->skillingCurrentUser->getStudentsNidsOfInstructor();
$currentUsersStudents
= $this->skillingCurrentUser->getStudentsNidsOfSpecialClassRole(
SkillingConstants::CLASS_ROLE_INSTRUCTOR
);
$isStudentsInstructor = in_array($possibleStudentId, $currentUsersStudents);
$isAdmin = $this->skillingCurrentUser->isAdministrator();
return $isStudentsInstructor || $isAdmin;
......
......@@ -652,13 +652,14 @@ class SkillingUser {
/**
* Get nids of students with published enrollments that have this user
* with the instructor role.
* with the special role given, usually instructor.
*
* @return int[]
* Student nids.
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function getStudentsNidsOfInstructor() {
public function getStudentsNidsOfSpecialClassRole($classRole) {
$enrollmentIds = $this->entityTypeManager
->getStorage('node')
->getQuery()
......@@ -667,7 +668,7 @@ class SkillingUser {
->condition('status', TRUE)
//For this user.
->condition('field_user', $this->id())
->condition('field_class_roles', [SkillingConstants::CLASS_ROLE_INSTRUCTOR], 'IN')
->condition('field_class_roles', [$classRole], 'IN')
->execute();
if (count($enrollmentIds) === 0) {
//No instructor enrollments.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment