Differences Between: [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]
1 <?php 2 3 namespace Packback\Lti1p3; 4 5 class LtiAssignmentsGradesService extends LtiAbstractService 6 { 7 public const CONTENTTYPE_SCORE = 'application/vnd.ims.lis.v1.score+json'; 8 public const CONTENTTYPE_LINEITEM = 'application/vnd.ims.lis.v2.lineitem+json'; 9 public const CONTENTTYPE_LINEITEMCONTAINER = 'application/vnd.ims.lis.v2.lineitemcontainer+json'; 10 public const CONTENTTYPE_RESULTCONTAINER = 'application/vnd.ims.lis.v2.resultcontainer+json'; 11 12 public function getScope(): array 13 { 14 return $this->getServiceData()['scope']; 15 } 16 17 // https://www.imsglobal.org/spec/lti-ags/v2p0#assignment-and-grade-service-claim 18 // When an LTI message is launching a resource associated to one and only one lineitem, 19 // the claim must include the endpoint URL for accessing the associated line item; 20 // in all other cases, this property must be either blank or not included in the claim. 21 public function getResourceLaunchLineItem(): ?LtiLineitem 22 { 23 $serviceData = $this->getServiceData(); 24 if (empty($serviceData['lineitem'])) { 25 return null; 26 } 27 28 return LtiLineitem::new()->setId($serviceData['lineitem']); 29 } 30 31 public function putGrade(LtiGrade $grade, LtiLineitem $lineitem = null) 32 { 33 if (!in_array(LtiConstants::AGS_SCOPE_SCORE, $this->getScope())) { 34 throw new LtiException('Missing required scope', 1); 35 } 36 37 $lineitem = $this->ensureLineItemExists($lineitem); 38 39 $scoreUrl = $lineitem->getId(); 40 41 // Place '/scores' before url params 42 $pos = strpos($scoreUrl, '?'); 43 $scoreUrl = $pos === false ? $scoreUrl.'/scores' : substr_replace($scoreUrl, '/scores', $pos, 0); 44 45 $request = new ServiceRequest(LtiServiceConnector::METHOD_POST, $scoreUrl); 46 $request->setBody($grade); 47 $request->setContentType(static::CONTENTTYPE_SCORE); 48 49 return $this->makeServiceRequest($request); 50 } 51 52 public function findLineItem(LtiLineitem $newLineItem): ?LtiLineitem 53 { 54 $lineitems = $this->getLineItems(); 55 56 foreach ($lineitems as $lineitem) { 57 if ($this->isMatchingLineitem($lineitem, $newLineItem)) { 58 return new LtiLineitem($lineitem); 59 } 60 } 61 62 return null; 63 } 64 65 public function createLineitem(LtiLineitem $newLineItem): LtiLineitem 66 { 67 $request = new ServiceRequest(LtiServiceConnector::METHOD_POST, $this->getServiceData()['lineitems']); 68 $request->setBody($newLineItem) 69 ->setContentType(static::CONTENTTYPE_LINEITEM) 70 ->setAccept(static::CONTENTTYPE_LINEITEM); 71 $createdLineItems = $this->makeServiceRequest($request); 72 73 return new LtiLineitem($createdLineItems['body']); 74 } 75 76 public function findOrCreateLineitem(LtiLineitem $newLineItem): LtiLineitem 77 { 78 return $this->findLineItem($newLineItem) ?? $this->createLineitem($newLineItem); 79 } 80 81 public function getGrades(LtiLineitem $lineitem = null) 82 { 83 $lineitem = $this->ensureLineItemExists($lineitem); 84 $resultsUrl = $lineitem->getId(); 85 86 // Place '/results' before url params 87 $pos = strpos($resultsUrl, '?'); 88 $resultsUrl = $pos === false ? $resultsUrl.'/results' : substr_replace($resultsUrl, '/results', $pos, 0); 89 90 $request = new ServiceRequest(LtiServiceConnector::METHOD_GET, $resultsUrl); 91 $request->setAccept(static::CONTENTTYPE_RESULTCONTAINER); 92 $scores = $this->makeServiceRequest($request); 93 94 return $scores['body']; 95 } 96 97 public function getLineItems(): array 98 { 99 if (!in_array(LtiConstants::AGS_SCOPE_LINEITEM, $this->getScope())) { 100 throw new LtiException('Missing required scope', 1); 101 } 102 103 $request = new ServiceRequest( 104 LtiServiceConnector::METHOD_GET, 105 $this->getServiceData()['lineitems'] 106 ); 107 $request->setAccept(static::CONTENTTYPE_LINEITEMCONTAINER); 108 109 $lineitems = $this->getAll($request); 110 111 // If there is only one item, then wrap it in an array so the foreach works 112 if (isset($lineitems['body']['id'])) { 113 $lineitems['body'] = [$lineitems['body']]; 114 } 115 116 return $lineitems; 117 } 118 119 public function getLineItem(string $url): LtiLineitem 120 { 121 if (!in_array(LtiConstants::AGS_SCOPE_LINEITEM, $this->getScope())) { 122 throw new LtiException('Missing required scope', 1); 123 } 124 125 $request = new ServiceRequest(LtiServiceConnector::METHOD_GET, $url); 126 $request->setAccept(static::CONTENTTYPE_LINEITEM); 127 128 $response = $this->makeServiceRequest($request)['body']; 129 130 return new LtiLineitem($response); 131 } 132 133 private function ensureLineItemExists(LtiLineitem $lineitem = null): LtiLineitem 134 { 135 // If no line item is passed in, attempt to use the one associated with 136 // this launch. 137 if (!isset($lineitem)) { 138 $lineitem = $this->getResourceLaunchLineItem(); 139 } 140 141 // If none exists still, create a default line item. 142 if (!isset($lineitem)) { 143 $defaultLineitem = LtiLineitem::new() 144 ->setLabel('default') 145 ->setScoreMaximum(100); 146 $lineitem = $this->createLineitem($defaultLineitem); 147 } 148 149 // If the line item does not contain an ID, find or create it. 150 if (empty($lineitem->getId())) { 151 $lineitem = $this->findOrCreateLineitem($lineitem); 152 } 153 154 return $lineitem; 155 } 156 157 private function isMatchingLineitem(array $lineitem, LtiLineitem $newLineItem): bool 158 { 159 return $newLineItem->getTag() == ($lineitem['tag'] ?? null) && 160 $newLineItem->getResourceId() == ($lineitem['resourceId'] ?? null) && 161 $newLineItem->getResourceLinkId() == ($lineitem['resourceLinkId'] ?? null); 162 } 163 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body