<?php
namespace Products\NotificationsBundle\Subscriber\OneRoster;
use Cms\CoreBundle\Entity\AbstractOneRosterEntity;
use Cms\CoreBundle\Entity\OneRoster\OneRosterUser;
use Cms\CoreBundle\Entity\OneRosterSync;
use Cms\CoreBundle\Events\OneRosterFixEvent;
use Doctrine\Common\Util\ClassUtils;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Class OneRosterUserFixSubscriber
* @package Products\NotificationsBundle\Subscriber\OneRoster
*/
final class OneRosterUserFixSubscriber extends AbstractNotificationsOneRosterSubscriber implements EventSubscriberInterface
{
/**
* {@inheritdoc}
*/
static public function getSubscribedEvents(): array
{
return [
OneRosterFixEvent::EVENT__USER => [
['teacherGrades', 0],
['parentOrgs', 0],
['parentGrades', 0],
],
];
}
/**
* @param OneRosterFixEvent $event
*/
public function teacherGrades(OneRosterFixEvent $event): void
{
// ensure we are meant to process this
if ( ! $this->checkTypes($event->getJob(), [
OneRosterSync::STRATEGIES__NOTIFICATIONS__STAFF,
])) {
return;
}
// get the user
$user = $event->getEntity();
if ( ! $user instanceof OneRosterUser) {
throw new \Exception(sprintf(
'User is not of proper type, got "%s".',
ClassUtils::getClass($user)
));
}
// branch on the role type
switch (true) {
case $user->getRole() === AbstractOneRosterEntity::ENUMS__ROLE_TYPE__TEACHER:
// noop, code will continue after the switch statement
break;
default:
// DEBUGGING
$event->getOutput()->writeln(sprintf(
'User role for #%s is "%s", skipping...',
$user->getSourcedId(),
$user->getRole()
));
// return to prevent further processing
return;
}
// clear out the grades already there
$user->setGrades([]);
// get grades from api and loop over them
foreach ($this->oneroster->api($event->getJob())->getTeacherClasses($user->getSourcedId()) as $course) {
// set the grades for the teacher by reducing down the class data for them
$user->setGrades(array_values(array_filter(array_unique(array_merge(
$user->getGrades(),
$course['grades'] ?? []
)))));
}
// TODO: track dirty flags properly depending on whether or not this entity was changed
// save the changes
$this->em->save($user);
}
/**
* @param OneRosterFixEvent $event
*/
public function parentOrgs(OneRosterFixEvent $event): void
{
// ensure we are meant to process this
if ( ! $this->checkTypes($event->getJob(), [
OneRosterSync::STRATEGIES__NOTIFICATIONS__FAMILY,
])) {
return;
}
// get the user
$user = $event->getEntity();
if ( ! $user instanceof OneRosterUser) {
throw new \Exception(sprintf(
'User is not of proper type, got "%s".',
ClassUtils::getClass($user)
));
}
// branch on the role type
switch (true) {
case $user->isRoleFamily():
// noop, code will continue after the switch statement
break;
default:
// DEBUGGING
$event->getOutput()->writeln(sprintf(
'User role for #%s is "%s", skipping...',
$user->getSourcedId(),
$user->getRole()
));
// return to prevent further processing
return;
}
// obtain the orgs already on the user
// TODO: should it be assumed that the core orgs reset on every stash so as orgs fall off, they are removed properly?
$orgs = [];
foreach ($user->getOrgs() as $org) {
$orgs[$org['sourcedId']] = $org;
}
// loop over the agent data from gg4l, this contains links back to applicable students
$studentSourcedIds = [];
foreach ($user->getAgents() as $agent) {
// make sure the agent association is that of a student
if (array_key_exists('type', $agent) && in_array($agent['type'], [AbstractOneRosterEntity::ENUMS__ROLE_TYPE__USER, AbstractOneRosterEntity::ENUMS__ROLE_TYPE__STUDENT])) {
// attach the current id to the array so that we can pull in one database query
$studentSourcedIds[] = $agent['sourcedId'];
}
}
// query database for the student records we need
$students = $studentSourcedIds ? $this->em->getRepository(OneRosterUser::class)->findBy([
'sourcedId' => $studentSourcedIds
]) : [];
// if the count is off, that's a problem, but one we can get around
// just log the error but continue
if (count($students) != count($studentSourcedIds)) {
// TODO
}
// loop over the found students
$changed = false;
foreach ($students as $student) {
// loop over the orgs the student is tied to
foreach ($student->getOrgs() as $org) {
// if we are not already in the tracked orgs, add it
if ( ! array_key_exists($org['sourcedId'], $orgs)) {
// do the addition
$orgs[$org['sourcedId']] = $org;
// track the change
$changed = true;
}
}
}
// TODO: track dirty flags properly depending on whether or not this entity was changed
// update the orgs on the user only if changes are tracked
if ($changed) {
$this->em->save(
$user->setOrgs($orgs)
);
}
}
/**
* @param OneRosterFixEvent $event
*/
public function parentGrades(OneRosterFixEvent $event): void
{
// ensure we are meant to process this
if ( ! $this->checkTypes($event->getJob(), [
OneRosterSync::STRATEGIES__NOTIFICATIONS__FAMILY,
])) {
return;
}
// get the user
$user = $event->getEntity();
if ( ! $user instanceof OneRosterUser) {
throw new \Exception(sprintf(
'User is not of proper type, got "%s".',
ClassUtils::getClass($user)
));
}
// branch on the role type
switch (true) {
case $user->isRoleFamily():
// noop, code will continue after the switch statement
break;
default:
// DEBUGGING
$event->getOutput()->writeln(sprintf(
'User role for #%s is "%s", skipping...',
$user->getSourcedId(),
$user->getRole()
));
// return to prevent further processing
return;
}
// reset the grades
$user->setGrades([]);
// loop over the agent data from gg4l, this contains links back to applicable students
$studentSourcedIds = [];
foreach ($user->getAgents() as $agent) {
// make sure the agent association is that of a student
if (array_key_exists('type', $agent) && in_array($agent['type'], [AbstractOneRosterEntity::ENUMS__ROLE_TYPE__STUDENT])) {
// attach the current id to the array so that we can pull in one database query
$studentSourcedIds[] = $agent['sourcedId'];
}
}
// query database for the student records we need
$students = $studentSourcedIds ? $this->em->getRepository(OneRosterUser::class)->findBy([
'sourcedId' => $studentSourcedIds
]) : [];
// if the count is off, that's a problem, but one we can get around
// just log the error but continue
if (count($students) != count($studentSourcedIds)) {
// TODO
}
// loop over the found students
foreach ($students as $student) {
// hack to use gg4l custom fields for grades if that exists
if (array_key_exists('gg4l.primaryGrade', $student->getMetadata()) && ! empty($student->getMetadata()['gg4l.primaryGrade'])) {
$grades = [$student->getMetadata()['gg4l.primaryGrade']];
} else {
$grades = $student->getGrades() ?: [];
}
// update the grades array
$user->setGrades(array_values(array_filter(array_unique(array_merge(
$user->getGrades(),
$grades
)))));
}
// TODO: track dirty flags properly depending on whether or not this entity was changed
// save the changes
$this->em->save($user);
}
}