Passed
Push — master ( 65060b...2b54a2 )
by Julito
10:15
created

ImsLtiPlugin::getEntityPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/* For license terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\Course;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Course. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
5
use Chamilo\CoreBundle\Entity\CourseRelUser;
6
use Chamilo\CoreBundle\Entity\Session;
7
use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser;
8
use Chamilo\CourseBundle\Entity\CTool;
9
use Chamilo\PluginBundle\Entity\ImsLti\ImsLtiTool;
10
use Chamilo\UserBundle\Entity\User;
11
use Doctrine\DBAL\DBALException;
12
use Doctrine\DBAL\Schema\Schema;
13
use Doctrine\DBAL\Types\Type;
14
use Symfony\Component\Filesystem\Filesystem;
15
16
/**
17
 * Description of MsiLti
18
 *
19
 * @author Angel Fernando Quiroz Campos <[email protected]>
20
 */
21
class ImsLtiPlugin extends Plugin
22
{
23
    const TABLE_TOOL = 'plugin_ims_lti_tool';
24
25
    public $isAdminPlugin = true;
26
27
    /**
28
     * Class constructor
29
     */
30
    protected function __construct()
31
    {
32
        $version = '1.1 (beta)';
33
        $author = 'Angel Fernando Quiroz Campos';
34
35
        parent::__construct($version, $author, ['enabled' => 'boolean']);
36
37
        $this->setCourseSettings();
38
    }
39
40
    /**
41
     * Get the class instance
42
     * @staticvar MsiLtiPlugin $result
43
     * @return ImsLtiPlugin
44
     */
45
    public static function create()
46
    {
47
        static $result = null;
48
49
        return $result ?: $result = new self();
50
    }
51
52
    /**
53
     * Get the plugin directory name
54
     */
55
    public function get_name()
56
    {
57
        return 'ims_lti';
58
    }
59
60
    /**
61
     * Install the plugin. Setup the database
62
     */
63
    public function install()
64
    {
65
        $pluginEntityPath = $this->getEntityPath();
66
67
        if (!is_dir($pluginEntityPath)) {
68
            if (!is_writable(dirname($pluginEntityPath))) {
69
                $message = get_lang('ErrorCreatingDir').': '.$pluginEntityPath;
70
                Display::addFlash(Display::return_message($message, 'error'));
71
72
                return false;
73
            }
74
75
            mkdir($pluginEntityPath, api_get_permissions_for_new_directories());
76
        }
77
78
        $fs = new Filesystem();
79
        $fs->mirror(__DIR__.'/Entity/', $pluginEntityPath, null, ['override']);
80
81
        $this->createPluginTables();
82
    }
83
84
    /**
85
     * Unistall plugin. Clear the database
86
     */
87
    public function uninstall()
88
    {
89
        $pluginEntityPath = $this->getEntityPath();
90
        $fs = new Filesystem();
91
92
        if ($fs->exists($pluginEntityPath)) {
93
            $fs->remove($pluginEntityPath);
94
        }
95
96
        try {
97
            $this->dropPluginTables();
98
            $this->removeTools();
99
        } catch (DBALException $e) {
100
            error_log('Error while uninstalling IMS/LTI plugin: '.$e->getMessage());
101
        }
102
    }
103
104
    /**
105
     * Creates the plugin tables on database
106
     *
107
     * @return boolean
108
     * @throws DBALException
109
     */
110
    private function createPluginTables()
111
    {
112
        $entityManager = Database::getManager();
113
        $connection = $entityManager->getConnection();
114
115
        if ($connection->getSchemaManager()->tablesExist(self::TABLE_TOOL)) {
116
            return true;
117
        }
118
119
        $queries = [
120
            'CREATE TABLE '.self::TABLE_TOOL.' (
121
                id INT AUTO_INCREMENT NOT NULL,
122
                c_id INT DEFAULT NULL,
123
                gradebook_eval_id INT DEFAULT NULL,
124
                parent_id INT DEFAULT NULL,
125
                name VARCHAR(255) NOT NULL,
126
                description LONGTEXT DEFAULT NULL,
127
                launch_url VARCHAR(255) NOT NULL,
128
                consumer_key VARCHAR(255) NOT NULL,
129
                shared_secret VARCHAR(255) NOT NULL,
130
                custom_params LONGTEXT DEFAULT NULL,
131
                active_deep_linking TINYINT(1) DEFAULT \'0\' NOT NULL,
132
                privacy LONGTEXT DEFAULT NULL,
133
                INDEX IDX_C5E47F7C91D79BD3 (c_id),
134
                INDEX IDX_C5E47F7C82F80D8B (gradebook_eval_id),
135
                INDEX IDX_C5E47F7C727ACA70 (parent_id),
136
                PRIMARY KEY(id)
137
            ) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB',
138
            'ALTER TABLE '.self::TABLE_TOOL.' ADD CONSTRAINT FK_C5E47F7C91D79BD3
139
                FOREIGN KEY (c_id) REFERENCES course (id)',
140
            'ALTER TABLE '.self::TABLE_TOOL.' ADD CONSTRAINT FK_C5E47F7C82F80D8B
141
                FOREIGN KEY (gradebook_eval_id) REFERENCES gradebook_evaluation (id) ON DELETE SET NULL',
142
            'ALTER TABLE '.self::TABLE_TOOL.' ADD CONSTRAINT FK_C5E47F7C727ACA70
143
                FOREIGN KEY (parent_id) REFERENCES '.self::TABLE_TOOL.' (id);',
144
        ];
145
146
        foreach ($queries as $query) {
147
            Database::query($query);
148
        }
149
150
        return true;
151
    }
152
153
    /**
154
     * Drops the plugin tables on database
155
     *
156
     * @return boolean
157
     */
158
    private function dropPluginTables()
159
    {
160
        $entityManager = Database::getManager();
161
        $connection = $entityManager->getConnection();
162
        $chamiloSchema = $connection->getSchemaManager();
163
164
        if (!$chamiloSchema->tablesExist([self::TABLE_TOOL])) {
165
            return false;
166
        }
167
168
        $sql = 'DROP TABLE IF EXISTS '.self::TABLE_TOOL;
169
        Database::query($sql);
170
171
        return true;
172
    }
173
174
    /**
175
     *
176
     */
177
    private function removeTools()
178
    {
179
        $sql = "DELETE FROM c_tool WHERE link LIKE 'ims_lti/start.php%' AND category = 'plugin'";
180
        Database::query($sql);
181
    }
182
183
    /**
184
     * Set the course settings
185
     */
186
    private function setCourseSettings()
187
    {
188
        $button = Display::toolbarButton(
189
            $this->get_lang('ConfigureExternalTool'),
190
            api_get_path(WEB_PLUGIN_PATH).'ims_lti/configure.php?'.api_get_cidreq(),
191
            'cog',
192
            'primary'
193
        );
194
195
        $this->course_settings = [
0 ignored issues
show
Bug Best Practice introduced by
The property course_settings does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
196
            [
197
                'name' => $this->get_lang('ImsLtiDescription').$button.'<hr>',
198
                'type' => 'html',
199
            ],
200
        ];
201
    }
202
203
    /**
204
     * @param Course     $course
205
     * @param ImsLtiTool $ltiTool
206
     *
207
     * @return CTool
208
     */
209
    public function findCourseToolByLink(Course $course, ImsLtiTool $ltiTool)
210
    {
211
        $em = Database::getManager();
212
        $toolRepo = $em->getRepository('ChamiloCourseBundle:CTool');
213
214
        /** @var CTool $cTool */
215
        $cTool = $toolRepo->findOneBy(
216
            [
217
                'cId' => $course,
218
                'link' => self::generateToolLink($ltiTool),
219
            ]
220
        );
221
222
        return $cTool;
223
    }
224
225
    /**
226
     * @param CTool      $courseTool
227
     * @param ImsLtiTool $ltiTool
228
     *
229
     * @throws \Doctrine\ORM\OptimisticLockException
230
     */
231
    public function updateCourseTool(CTool $courseTool, ImsLtiTool $ltiTool)
232
    {
233
        $em = Database::getManager();
234
235
        $courseTool->setName($ltiTool->getName());
236
237
        $em->persist($courseTool);
238
        $em->flush();
239
    }
240
241
    /**
242
     * @param ImsLtiTool $tool
243
     *
244
     * @return string
245
     */
246
    private static function generateToolLink(ImsLtiTool $tool)
247
    {
248
        return  'ims_lti/start.php?id='.$tool->getId();
249
    }
250
251
    /**
252
     * Add the course tool
253
     *
254
     * @param Course     $course
255
     * @param ImsLtiTool $tool
256
     */
257
    public function addCourseTool(Course $course, ImsLtiTool $tool)
258
    {
259
        $this->createLinkToCourseTool(
260
            $tool->getName(),
261
            $course->getId(),
262
            null,
263
            self::generateToolLink($tool)
264
        );
265
    }
266
267
    /**
268
     * @return string
269
     */
270
    protected function getConfigExtraText()
271
    {
272
        $text = $this->get_lang('ImsLtiDescription');
273
        $text .= sprintf(
274
            $this->get_lang('ManageToolButton'),
275
            api_get_path(WEB_PLUGIN_PATH).'ims_lti/admin.php'
276
        );
277
278
        return $text;
279
    }
280
281
    /**
282
     * @return string
283
     */
284
    public function getEntityPath()
285
    {
286
        return api_get_path(SYS_PATH).'src/Chamilo/PluginBundle/Entity/'.$this->getCamelCaseName();
287
    }
288
289
    public static function isInstructor()
290
    {
291
        api_is_allowed_to_edit(false, true);
292
    }
293
294
    /**
295
     * @param User         $user
296
     *
297
     * @return string
298
     */
299
    public static function getUserRoles(User $user)
300
    {
301
        if ($user->getStatus() === INVITEE) {
302
            return 'Learner/GuestLearner,Learner';
303
        }
304
305
        if (!api_is_allowed_to_edit(false, true)) {
306
            return 'Learner,Learner/Learner';
307
        }
308
309
        $roles = ['Instructor'];
310
311
        if (api_is_platform_admin_by_id($user->getId())) {
312
            $roles[] = 'Administrator/SystemAdministrator';
313
        }
314
315
        return implode(',', $roles);
316
    }
317
318
    /**
319
     * @param int $userId
320
     *
321
     * @return string
322
     */
323
    public static function generateToolUserId($userId)
324
    {
325
        $siteName = api_get_setting('siteName');
326
        $institution = api_get_setting('Institution');
327
        $toolUserId = "$siteName - $institution - $userId";
328
        $toolUserId = api_replace_dangerous_char($toolUserId);
329
330
        return $toolUserId;
331
    }
332
333
    /**
334
     * @param Course       $course
335
     * @param Session|null $session
336
     *
337
     * @return string
338
     */
339
    public static function getRoleScopeMentor(Course $course, Session $session = null)
340
    {
341
        $scope = [];
342
343
        if ($session) {
344
            $students = $session->getUserCourseSubscriptionsByStatus($course, Session::STUDENT);
345
        } else {
346
            $students = $course->getStudents();
347
        }
348
349
        /** @var SessionRelCourseRelUser|CourseRelUser $subscription */
350
        foreach ($students as $subscription) {
351
            $scope[] = self::generateToolUserId($subscription->getUser()->getId());
352
        }
353
354
        return implode(',', $scope);
355
    }
356
357
    /**
358
     * @param array      $contentItem
359
     * @param ImsLtiTool $baseLtiTool
360
     * @param Course     $course
361
     *
362
     * @throws \Doctrine\ORM\OptimisticLockException
363
     */
364
    public function saveItemAsLtiLink(array $contentItem, ImsLtiTool $baseLtiTool, Course $course)
365
    {
366
        $em = Database::getManager();
367
        $ltiToolRepo = $em->getRepository('ChamiloPluginBundle:ImsLti\ImsLtiTool');
368
369
        $url = empty($contentItem['url']) ? $baseLtiTool->getLaunchUrl() : $contentItem['url'];
370
371
        /** @var ImsLtiTool $newLtiTool */
372
        $newLtiTool = $ltiToolRepo->findOneBy(['launchUrl' => $url, 'parent' => $baseLtiTool, 'course' => $course]);
373
374
        if (null === $newLtiTool) {
375
            $newLtiTool = new ImsLtiTool();
376
            $newLtiTool
377
                ->setLaunchUrl($url)
378
                ->setParent(
379
                    $baseLtiTool
380
                )
381
                ->setPrivacy(
382
                    $baseLtiTool->isSharingName(),
383
                    $baseLtiTool->isSharingEmail(),
384
                    $baseLtiTool->isSharingPicture()
385
                )
386
                ->setCourse($course);
387
        }
388
389
        $newLtiTool
390
            ->setName(
391
                !empty($contentItem['title']) ? $contentItem['title'] : $baseLtiTool->getName()
392
            )
393
            ->setDescription(
394
                !empty($contentItem['text']) ? $contentItem['text'] : null
395
            );
396
397
        $em->persist($newLtiTool);
398
        $em->flush();
399
400
        $courseTool = $this->findCourseToolByLink($course, $newLtiTool);
401
402
        if ($courseTool) {
0 ignored issues
show
introduced by
$courseTool is of type Chamilo\CourseBundle\Entity\CTool, thus it always evaluated to true.
Loading history...
403
            $this->updateCourseTool($courseTool, $newLtiTool);
404
405
            return;
406
        }
407
408
        $this->addCourseTool($course, $newLtiTool);
409
    }
410
411
    /**
412
     * @return null|SimpleXMLElement
413
     */
414
    private function getRequestXmlElement()
415
    {
416
        $request = file_get_contents("php://input");
417
418
        if (empty($request)) {
419
            return null;
420
        }
421
422
        $xml = new SimpleXMLElement($request);
423
424
        return $xml;
425
    }
426
427
    /**
428
     * @return ImsLtiServiceResponse|null
429
     */
430
    public function processServiceRequest()
431
    {
432
        $xml = $this->getRequestXmlElement();
433
434
        if (empty($xml)) {
435
            return null;
436
        }
437
438
        $request = ImsLtiServiceRequestFactory::create($xml);
439
        $response = $request->process();
440
441
        return $response;
442
    }
443
444
    /**
445
     * @param int    $toolId
446
     * @param Course $course
447
     *
448
     * @return bool
449
     */
450
    public static function existsToolInCourse($toolId, Course $course)
451
    {
452
        $em = Database::getManager();
453
        $toolRepo = $em->getRepository('ChamiloPluginBundle:ImsLti\ImsLtiTool');
454
455
        /** @var ImsLtiTool $tool */
456
        $tool = $toolRepo->findOneBy(['id' => $toolId, 'course' => $course]);
457
458
        return !empty($tool);
459
    }
460
}
461