Completed
Branch develop (eb5fc3)
by Seth
02:07
created

Toolbox::flushLogQueue()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.2
c 0
b 0
f 0
cc 4
eloc 5
nc 3
nop 0
1
<?php
2
/** Toolbox class */
3
4
namespace smtech\ReflexiveCanvasLTI;
5
6
use mysqli;
7
use Serializable;
8
9
use Log;
10
11
use Battis\AppMetadata;
12
use Battis\ConfigXML;
13
use Battis\DataUtilities;
14
15
use smtech\CanvasPest\CanvasPest;
16
use smtech\ReflexiveCanvasLTI\LTI\ToolProvider;
17
use smtech\ReflexiveCanvasLTI\Exception\ConfigurationException;
18
use smtech\LTI\Configuration\Generator;
19
use smtech\LTI\Configuration\LaunchPrivacy;
20
use smtech\LTI\Configuration\Exception\ConfigurationException as LTIConfigGeneratorException;
21
22
/**
23
 * A toolbox of tools for quickly constructing LTI tool providers that hook
24
 * back into the Canvas API reflexively.
25
 *
26
 * The basic idea is that you need an XML configuration file of credentials and
27
 * use that to instantiate the Toolbox. The toolbox can then perform LTI
28
 * authentication, handle API requests, generate an LTI Configuration XML file,
29
 * etc. for you.
30
 *
31
 * @author Seth Battis
32
 * @version v1.0
33
 */
34
class Toolbox implements Serializable
35
{
36
    /** Default level of information-sharing privacy between consumer and provider */
37
    const DEFAULT_LAUNCH_PRIVACY = 'public';
38
39
    /** Name of the database table backing the tool metadata */
40
    const TOOL_METADATA_TABLE = 'tool_metadata';
41
42
    /** The path to the configuration file from which this toolbox was generated */
43
    const TOOL_CONFIG_FILE = 'TOOL_CONFIG_FILE';
44
45
    /**
46
     * The (ideally globally unique) identifier for the LTI tool provider
47
     */
48
    const TOOL_ID = 'TOOL_ID';
49
50
    /** The human-readable name of the tool */
51
    const TOOL_NAME = 'TOOL_NAME';
52
53
    /** The human-readable description of the tool */
54
    const TOOL_DESCRIPTION = 'TOOL_DESCRIPTION';
55
56
    /** The URL of the tool's icon image (if present) */
57
    const TOOL_ICON_URL = 'TOOL_ICON_URL';
58
59
    /** The domain from which Tool Consumer requests may emanate for the tool */
60
    const TOOL_DOMAIN = 'TOOL_DOMAIN';
61
62
    /** The URL of the script that will handle LTI authentication */
63
    const TOOL_LAUNCH_URL = 'TOOL_LAUNCH_URL';
64
65
    /** The level of information sharing between the LMS and the tool */
66
    const TOOL_LAUNCH_PRIVACY = 'TOOL_LAUNCH_PRIVACY';
67
68
    /** The path to the tool's log file */
69
    const TOOL_LOG = 'TOOL_LOG';
70
71
    /** An associative array of LTI request types and the URL that handles that request. */
72
    const TOOL_HANDLER_URLS = 'TOOL_HANDLER_URLS';
73
74
    /** An associative array of Canvas API credentials (`url` and `token`) */
75
    const TOOL_CANVAS_API = 'TOOL_CANVAS_API';
76
77
    /**
78
     * Persistent metadata storage
79
     * @var AppMetadata
80
     * @see Toolbox::config() Toolbox::config()
81
     */
82
    protected $metadata = false;
83
84
    /**
85
     * Object-oriented access to the Canvas API
86
     * @var CanvasPest
87
     */
88
    protected $api = false;
89
90
    /**
91
     * MySQL database connection
92
     * @var mysqli
93
     */
94
    protected $mysql = false;
95
96
    /**
97
     * LTI Tool Provider for handling authentication and consumer/user management
98
     * @var ToolProvider
99
     */
100
    protected $toolProvider;
101
102
    /**
103
     * Generator for LTI Configuration XML files
104
     * @var Generator
105
     */
106
    protected $generator;
107
108
    /**
109
     * Log file manager
110
     * @var Log
111
     */
112
    protected $logger = false;
113
114
    /**
115
     * Queue of delayed log messages (waiting for a logger instance)
116
     * @return array
117
     */
118
    protected $logQueue = [];
119
120
    /**
121
     * Provide serialization support for the Toolbox
122
     *
123
     * This allows a Toolbox to be stored in the `$_SESSION` variables.
124
     *
125
     * Caveat emptor: because `mysqli` objects can not be serialized,
126
     * serialization is limited to storing a reference to the configuration file
127
     * that generated this object, which will be reaccessed (along with cached
128
     * configuration metadata) when the object is unserialized.
129
     *
130
     * @return string
131
     */
132
    public function serialize()
133
    {
134
        return serialize([
135
            'config' => $this->config(static::TOOL_CONFIG_FILE)
136
        ]);
137
    }
138
139
    /**
140
     * Provide serialization support for Toolbox
141
     *
142
     * This allows a Toolbox to be stored in the `$_SESSION` variables.
143
     *
144
     * @see Toolbox::serialize() `Toolbox::serialize()` has more information on the
145
     *      specifics of the serialization approach.
146
     *
147
     * @param  string $serialized A Toolbox object serialized by `Toolbox::serialize()`
148
     * @return Toolbox
149
     */
150
    public function unserialize($serialized)
151
    {
152
        $data = unserialize($serialized);
153
        $this->loadConfiguration($data['config']);
154
    }
155
156
    /**
157
     * Create a Toolbox instance from a configuration file
158
     *
159
     * @param  string $configFilePath Path to the configuration file
160
     * @param  boolean $forceRecache Whether or not to rely on cached
161
     *     configuration metadata or to force a refresh from the configuration
162
     *     file
163
     * @return Toolbox
164
     */
165
    public static function fromConfiguration($configFilePath, $forceRecache = false)
166
    {
167
        return new static($configFilePath, $forceRecache);
168
    }
169
170
    /**
171
     * Construct a Toolbox instance from a configuration file
172
     *
173
     * @see Toolbox::fromConfiguration() Use `Toolbox::fromConfiguration()`
174
     *
175
     * @param string $configFilePath
176
     * @param boolean $forceRecache
177
     */
178
    private function __construct($configFilePath, $forceRecache = false)
179
    {
180
        $this->loadConfiguration($configFilePath, $forceRecache);
181
    }
182
183
    /**
184
     * Update a Toolbox instance from a configuration file
185
     *
186
     * @see Toolbox::fromConfiguration() Use `Toolbox::fromConfiguration()`
187
     *
188
     * @param  string $configFilePath
189
     * @param  boolean $forceRecache
190
     * @return void
191
     */
192
    protected function loadConfiguration($configFilePath, $forceRecache = false)
193
    {
194
        if ($forceRecache) {
195
            $this->log("Resetting LTI configuration from $configFilePath");
196
        }
197
198
        /* load the configuration file */
199
        $config = new ConfigXML($configFilePath);
200
201
        /* configure database connections */
202
        $this->setMySQL($config->newInstanceOf(mysqli::class, '/config/mysql'));
203
204
        /* configure metadata caching */
205
        $id = $config->toString('/config/tool/id');
206
        if (empty($id)) {
207
            $id = basename(dirname($configFilePath)) . '_' . md5(__DIR__ . file_get_contents($configFilePath));
208
            $this->log("    Automatically generated ID $id");
209
        }
210
        $this->setMetadata(new AppMetadata($this->mysql, $id, self::TOOL_METADATA_TABLE));
211
212
        /* update metadata */
213
        if ($forceRecache ||
214
            empty($this->config(static::TOOL_ID)) ||
215
            empty($this->config(static::TOOL_LAUNCH_URL)) ||
216
            empty($this->config(static::TOOL_CONFIG_FILE))) {
217
            $this->configToolMetadata($config, $id);
218
        }
219
        $configPath = dirname($this->config(static::TOOL_CONFIG_FILE));
0 ignored issues
show
Unused Code introduced by
$configPath is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
220
221
        /* configure logging */
222
        if ($forceRecache || empty($this->config(static::TOOL_LOG))) {
223
            $this->configLog($config);
224
        }
225
        $this->setLog(Log::singleton('file', $this->config(static::TOOL_LOG)));
226
227
        /* configure tool provider */
228
        if ($forceRecache || empty($this->config(static::TOOL_HANDLER_URLS))) {
229
            $this->configToolProvider($config);
230
        }
231
232
        /* configure API access */
233
        if ($forceRecache || empty($this->config(static::TOOL_CANVAS_API))) {
234
            $this->configApi($config);
235
        }
236
    }
237
238
    /**
239
     * Configure the tool metadata from a configuration file
240
     * @param ConfigXML $config Configuration file object
241
     * @param string $id Unique, potentially auto-generated tool ID
242
     * @return void
243
     */
244
    protected function configToolMetadata(ConfigXML $config, $id)
245
    {
246
        $tool = $config->toArray('/config/tool')[0];
247
248
        $this->config(static::TOOL_ID, $id);
249
        $this->config(static::TOOL_NAME, (empty($tool['name']) ? $id : $tool['name']));
250
        $this->config(static::TOOL_CONFIG_FILE, realpath($configFilePath));
0 ignored issues
show
Bug introduced by
The variable $configFilePath does not exist. Did you mean $config?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
251
        $configPath = dirname($this->config(static::TOOL_CONFIG_FILE));
252
253 View Code Duplication
        if (!empty($tool['description'])) {
254
            $this->config(static::TOOL_DESCRIPTION, $tool['description']);
255
        } else {
256
            $this->clearConfig(static::TOOL_DESCRIPTION);
257
        }
258
259
        if (!empty($tool['icon'])) {
260
            $this->config(static::TOOL_ICON_URL, (
261
                file_exists("$configPath/{$tool['icon']}") ?
262
                    DataUtilities::URLfromPath("$configPath/{$tool['icon']}") :
263
                    $tool[self::ICON]
264
            ));
265
        } else {
266
            $this->clearConfig(static::TOOL_ICON_URL);
267
        }
268
269
        $this->config(static::TOOL_LAUNCH_PRIVACY, (
270
            empty($tool['launch-privacy']) ?
271
                self::DEFAULT_LAUNCH_PRIVACY :
272
                $tool['launch-privacy']
273
        ));
274
275 View Code Duplication
        if (!empty($tool['domain'])) {
276
            $this->config(static::TOOL_DOMAIN, $tool['domain']);
277
        } else {
278
            $this->clearConfig(static::TOOL_DOMAIN);
279
        }
280
281
        $this->config(static::TOOL_LAUNCH_URL, (
282
            empty($tool['authenticate']) ?
283
                DataUtilities::URLfromPath($_SERVER['SCRIPT_FILENAME']) :
284
                DataUtilities::URLfromPath("$configPath/{$tool['authenticate']}")
285
        ));
286
287
        $this->log("    Tool metadata configured");
288
    }
289
290
    /**
291
     * Configure the logger object from a configuration file
292
     *
293
     * This will also flush any backlog of queued messages that have been
294
     * waiting for a logger object to be ready.
295
     *
296
     * @param ConfigXML $config Configuration file object
297
     * @return void
298
     */
299
    protected function configLog(ConfigXML $config)
300
    {
301
        $configPath = dirname($this->config(static::TOOL_CONFIG_FILE));
302
        $log = "$configPath/" . $config->toString('/config/tool/log');
303
        shell_exec("touch \"$log\"");
304
        $this->config(static::TOOL_LOG, realpath($log));
305
        $this->flushLogQueue();
306
    }
307
308
    /**
309
     * Configure tool provider object from configuration file
310
     * @param ConfigXML $config Configuration file object
311
     * @return void
312
     */
313
    protected function configToolProvider(ConfigXML $config)
314
    {
315
        $handlers = $config->toArray('/config/tool/handlers')[0];
316
        if (empty($handlers) || !is_array($handlers)) {
317
            throw new ConfigurationException(
318
                'At least one handler/URL pair must be specified',
319
                ConfigurationException::TOOL_PROVIDER
320
            );
321
        }
322
        foreach ($handlers as $request => $path) {
323
            $handlers[$request] = DataUtilities::URLfromPath("$configPath/$path");
0 ignored issues
show
Bug introduced by
The variable $configPath does not exist. Did you mean $config?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
324
        }
325
        $this->config(static::TOOL_HANDLER_URLS, $handlers);
326
        $this->log('    Tool provider handler URLs configured');
327
    }
328
329
    /**
330
     * Configure API access object from configuration file
331
     * @param ConfigXML $config Configuration file object
332
     * @return void
333
     */
334
    protected function configApi(ConfigXML $config)
335
    {
336
        $this->config(static::TOOL_CANVAS_API, $config->toArray('/config/canvas')[0]);
337
        if (empty($this->config(static::TOOL_CANVAS_API))) {
338
            throw new ConfigurationException(
339
                'Canvas API credentials must be provided',
340
                ConfigurationException::CANVAS_API_MISSING
341
            );
342
        }
343
        $this->log('    Canvas API credentials configured');
344
    }
345
346
    /**
347
     * Update toolbox configuration metadata object
348
     *
349
     * @param AppMetadata $metadata
350
     */
351
    public function setMetadata(AppMetadata $metadata)
352
    {
353
        $this->metadata = $metadata;
354
    }
355
356
    /**
357
     * Get the toolbox configuration metadata object
358
     *
359
     * @return AppMetadata
360
     */
361
    public function getMetadata()
362
    {
363
        return $this->metadata;
364
    }
365
366
    /**
367
     * Access or update a specific configuration metadata key/value pair
368
     *
369
     * The `TOOL_*` constants refer to keys used by the Toolbox by default.
370
     *
371
     * @param  string $key The metadata key to look up/create/update
372
     * @param  mixed $value (Optional) If not present (or `null`), the current
373
     *     metadata is returned. If present, the metadata is created/updated
374
     * @return mixed If not updating the metadata, the metadata (if any)
375
     *     currently stored
376
     */
377
    public function config($key, $value = null)
378
    {
379
        if ($value !== null) {
380
            $this->metadata[$key] = $value;
381
        } else {
382
            return $this->metadata[$key];
383
        }
384
    }
385
386
    /**
387
     * Wipe a particular configuration key from storage
388
     * @param string $key
389
     * @return boolean `TRUE` if cleared, `FALSE` if not found
390
     */
391
    public function clearConfig($key)
392
    {
393
        if (isset($this->metadata[$key])) {
394
            unset($this->metadata[$key]);
395
            return true;
396
        }
397
        return false;
398
    }
399
400
    /**
401
     * Update the ToolProvider object
402
     *
403
     * @param ToolProvider $toolProvider
404
     */
405
    public function setToolProvider(ToolProvider $toolProvider)
406
    {
407
        $this->toolProvider = $toolProvider;
408
    }
409
410
    /**
411
     * Get the ToolProvider object
412
     *
413
     * This does some just-in-time initialization, so that if the ToolProvider
414
     * has not yet been accessed, it will be instantiated and initialized by this
415
     * method.
416
     *
417
     * @return ToolProvider
418
     */
419
    public function getToolProvider()
420
    {
421
        if (empty($this->toolProvider)) {
422
            $this->setToolProvider(
423
                new ToolProvider(
424
                    $this->mysql,
425
                    $this->metadata['TOOL_HANDLER_URLS']
426
                )
427
            );
428
        }
429
        return $this->toolProvider;
430
    }
431
432
    /**
433
     * Authenticate an LTI launch request
434
     *
435
     * @return void
436
     * @codingStandardsIgnoreStart
437
     */
438
    public function lti_authenticate()
439
    {
440
        /* @codingStandardsIgnoreEnd */
441
        $this->getToolProvider()->handle_request();
442
    }
443
444
    /**
445
     * Are we (or should we be) in the midst of authenticating an LTI launch request?
446
     *
447
     * @return boolean
448
     * @codingStandardsIgnoreStart
449
     */
450
    public function lti_isLaunching()
451
    {
452
        /* @codingStandardsIgnoreEnd */
453
        return !empty($_POST['lti_message_type']);
454
    }
455
456
    /**
457
     * Create a new Tool consumer
458
     *
459
     * @see ToolProvider::createConsumer() Pass-through to `ToolProvider::createConsumer()`
460
     *
461
     * @param  string $name Human-readable name
462
     * @param  string $key (Optional) Consumer key (unique within the tool provider)
463
     * @param  string $secret (Optional) Shared secret
464
     * @return boolean Whether or not the consumer was created
465
     * @codingStandardsIgnoreStart
466
     */
467
    public function lti_createConsumer($name, $key = false, $secret = false)
468
    {
469
        /* @codingStandardsIgnoreEnd */
470
        if ($this->getToolProvider()->createConsumer($name, $key, $secret)) {
471
            $this->log("Created consumer $name");
472
            return true;
473
        } else {
474
            $this->log("Could not recreate consumer '$name', consumer already exists");
475
            return false;
476
        }
477
    }
478
479
    /**
480
     * Get the list of consumers for this tool
481
     *
482
     * @see ToolProvider::getConsumers() Pass-through to `ToolProvider::getConsumers()`
483
     *
484
     * @return LTI_Consumer[]
485
     * @codingStandardsIgnoreStart
486
     */
487
    public function lti_getConsumers()
488
    {
489
        /* @codingStandardsIgnoreEnd */
490
        return $this->getToolProvider()->getConsumers();
491
    }
492
493
    /**
494
     * Update the API interaction object
495
     *
496
     * @param CanvasPest $api
497
     */
498
    public function setAPI(CanvasPest $api)
499
    {
500
        $this->api = $api;
501
    }
502
503
    /**
504
     * Get the API interaction object
505
506
     * @return CanvasPest
507
     */
508
    public function getAPI()
509
    {
510
        if (empty($this->api)) {
511
            if (!empty($this->config(static::TOOL_CANVAS_API)['token'])) {
512
                $this->setAPI(new CanvasPest(
513
                    'https://' . $_SESSION[ToolProvider::class]['canvas']['api_domain'] . '/api/v1',
514
                    $this->config(static::TOOL_CANVAS_API)['token']
515
                ));
516
            } else {
517
                throw new ConfigurationException(
518
                    'Canvas URL and Token required',
519
                    ConfigurationException::CANVAS_API_INCORRECT
520
                );
521
            }
522
        }
523
        return $this->api;
524
    }
525
526
    /**
527
     * Make a GET request to the API
528
     *
529
     * @link https://htmlpreview.github.io/?https://raw.githubusercontent.com/smtech/canvaspest/master/doc/classes/smtech.CanvasPest.CanvasPest.html#method_get Pass-through to CanvasPest::get()
530
     * @param  string $url
531
     * @param  string[] $data (Optional)
532
     * @param  string[] $headers (Optional)
533
     * @return \smtech\CanvasPest\CanvasObject|\smtech\CanvasPest\CanvasArray
534
     * @codingStandardsIgnoreStart
535
     */
536
    public function api_get($url, $data = [], $headers = [])
537
    {
538
        /* @codingStandardsIgnoreEnd */
539
        return $this->getAPI()->get($url, $data, $headers);
540
    }
541
542
    /**
543
     * Make a POST request to the API
544
     *
545
     * @link https://htmlpreview.github.io/?https://raw.githubusercontent.com/smtech/canvaspest/master/doc/classes/smtech.CanvasPest.CanvasPest.html#method_post Pass-through to CanvasPest::post()
546
     * @param  string $url
547
     * @param  string[] $data (Optional)
548
     * @param  string[] $headers (Optional)
549
     * @return \smtech\CanvasPest\CanvasObject|\smtech\CanvasPest\CanvasArray
550
     * @codingStandardsIgnoreStart
551
     */
552
    public function api_post($url, $data = [], $headers = [])
553
    {
554
        /* @codingStandardsIgnoreEnd */
555
        return $this->getAPI()->post($url, $data, $headers);
556
    }
557
558
    /**
559
     * Make a PUT request to the API
560
     *
561
     * @link https://htmlpreview.github.io/?https://raw.githubusercontent.com/smtech/canvaspest/master/doc/classes/smtech.CanvasPest.CanvasPest.html#method_put Pass-through to CanvasPest::put()
562
     * @param  string $url
563
     * @param  string[] $data (Optional)
564
     * @param  string[] $headers (Optional)
565
     * @return \smtech\CanvasPest\CanvasObject|\smtech\CanvasPest\CanvasArray
566
     * @codingStandardsIgnoreStart
567
     */
568
    public function api_put($url, $data = [], $headers = [])
569
    {
570
        /* @codingStandardsIgnoreEnd */
571
        return $this->getAPI()->put($url, $data, $headers);
572
    }
573
574
    /**
575
     * Make a DELETE request to the API
576
     *
577
     * @link https://htmlpreview.github.io/?https://raw.githubusercontent.com/smtech/canvaspest/master/doc/classes/smtech.CanvasPest.CanvasPest.html#method_delete Pass-through to CanvasPest::delete()
578
     * @param  string $url
579
     * @param  string[] $data (Optional)
580
     * @param  string[] $headers (Optional)
581
     * @return \smtech\CanvasPest\CanvasObject|\smtech\CanvasPest\CanvasArray
582
     * @codingStandardsIgnoreStart
583
     */
584
    public function api_delete($url, $data = [], $headers = [])
585
    {
586
        /* @codingStandardsIgnoreEnd */
587
        return $this->getAPI()->delete($url, $data, $headers);
588
    }
589
590
    /**
591
     * Set MySQL connection object
592
     *
593
     * @param mysqli $mysql
594
     */
595
    public function setMySQL(mysqli $mysql)
596
    {
597
        $this->mysql = $mysql;
598
    }
599
600
    /**
601
     * Get MySQL connection object
602
     *
603
     * @return mysqli
604
     */
605
    public function getMySQL()
606
    {
607
        return $this->mysql;
608
    }
609
610
    /**
611
     * Make a MySQL query
612
     *
613
     * @link http://php.net/manual/en/mysqli.query.php Pass-through to `mysqli::query()`
614
     * @param string $query
615
     * @param int $resultMode (Optional, defaults to `MYSQLI_STORE_RESULT`)
616
     * @return mixed
617
     * @codingStandardsIgnoreStart
618
     */
619
    public function mysql_query($query, $resultMode = MYSQLI_STORE_RESULT)
620
    {
621
        /* @codingStandardsIgnoreEnd */
622
        return $this->getMySQL()->query($query, $resultMode);
623
    }
624
625
    /**
626
     * Check if the logger object is ready for use
627
     * @return boolean `TRUE` if ready, `FALSE` otherwise
628
     */
629
    protected function logReady()
630
    {
631
        return is_a($this->log, Log::class);
0 ignored issues
show
Bug introduced by
The property log does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
632
    }
633
634
    /**
635
     * Set log file manager
636
     *
637
     * @param Log $log
638
     */
639
    public function setLog(Log $log)
640
    {
641
        $this->logger = $log;
642
    }
643
644
    /**
645
     * Get log file manager
646
     *
647
     * @return Log
648
     */
649
    public function getLog()
650
    {
651
        return $this->logger;
652
    }
653
654
    /**
655
     * Queue a message for delayed logging
656
     * @param string $message
657
     * @param string $priority
658
     * @return void
659
     */
660
    protected function queueLog($message, $priority = null)
661
    {
662
        $this->logQueue[] = ['message' => $message, 'priority' => $priority];
663
    }
664
665
    /**
666
     * Flush the delayed log queue
667
     * @return void
668
     */
669
    protected function flushLogQueue()
670
    {
671
        if ($this->logReady() && !empty($this->logQueue)) {
672
            foreach ($this->logQueue as $entry) {
673
                $this->getLog()->log($entry['message'], $entry['priority']);
674
            }
675
            $this->logQueue = [];
676
        }
677
    }
678
679
    /**
680
     * Add a message to the tool log file
681
     *
682
     * If no logger object is ready, the message will be queued for delayed
683
     * logging until a logger object is ready.
684
     *
685
     * @link https://pear.php.net/package/Log/docs/1.13.1/Log/Log_file.html#methodlog
686
     *      Pass-throgh to `Log_file::log()`
687
     *
688
     * @param string $message
689
     * @param string $priority (Optional, defaults to `PEAR_LOG_INFO`)
690
     * @return boolean Success
691
     */
692
    public function log($message, $priority = null)
693
    {
694
        if ($this->logReady()) {
695
            $this->flushLogQueue();
696
            return $this->getLog()->log($message, $priority);
697
        } else {
698
            $this->queueLog($message, $priority);
699
        }
700
    }
701
702
    /**
703
     * Set the LTI Configuration generator
704
     *
705
     * @param Generator $generator
706
     */
707
    public function setGenerator(Generator $generator)
708
    {
709
        $this->generator = $generator;
710
    }
711
712
    /**
713
     * Get the LTI Configuration generator
714
     *
715
     * @return Generator
716
     */
717
    public function getGenerator()
718
    {
719
        try {
720
            if (empty($this->generator)) {
721
                $this->setGenerator(
722
                    new Generator(
723
                        $this->config(static::TOOL_NAME),
724
                        $this->config(static::TOOL_ID),
725
                        $this->config(static::TOOL_LAUNCH_URL),
726
                        (empty($this->config(static::TOOL_DESCRIPTION)) ? false : $this->config(static::TOOL_DESCRIPTION)),
727
                        (empty($this->config(static::TOOL_ICON_URL)) ? false : $this->config(static::TOOL_ICON_URL)),
728
                        (empty($this->config(static::TOOL_LAUNCH_PRIVACY)) ? LaunchPrivacy::USER_PROFILE() : $this->config(static::TOOL_LAUNCH_PRIVACY)),
729
                        (empty($this->config(static::TOOL_DOMAIN)) ? false : $this->config(static::TOOL_DOMAIN))
730
                    )
731
                );
732
            }
733
        } catch (LTIConfigGeneratorException $e) {
734
            throw new ConfigurationException(
735
                $e->getMessage(),
736
                ConfigurationException::TOOL_PROVIDER
737
            );
738
        }
739
        return $this->generator;
740
    }
741
742
    /**
743
     * Get the LTI configuration XML
744
     *
745
     * @link https://htmlpreview.github.io/?https://raw.githubusercontent.com/smtech/lti-configuration-xml/master/doc/classes/smtech.LTI.Configuration.Generator.html#method_saveXML Pass-through to `Generator::saveXML()`
746
     *
747
     * @return string
748
     */
749
    public function saveConfigurationXML()
750
    {
751
        try {
752
            return $this->getGenerator()->saveXML();
753
        } catch (LTIConfigGeneratorException $e) {
754
            throw new ConfigurationException(
755
                $e->getMessage(),
756
                ConfigurationException::TOOL_PROVIDER
757
            );
758
        }
759
    }
760
}
761