/** * @inheritDoc * * @param string $template * @param string $cache_id * @param string $compile_id * @param string $parent * @return void */ public function display($template = 'page.tpl', $cache_id = null, $compile_id = null, $parent = null) { if ($this->isFramed()) { $this->addStylesheet(DataUtilities::URLfromPath(__DIR__ . '/../css/StMarksSmarty.css') . '?isFramed=true', self::KEY); } $this->assign('isFramed', $this->isFramed()); parent::display($template, $cache_id, $compile_id, $parent); }
<?php require_once __DIR__ . '/vendor/autoload.php'; require_once __DIR__ . '/constants.inc.php'; use smtech\CanvasManagement\Toolbox; use smtech\ReflexiveCanvasLTI\LTI\ToolProvider; use Battis\DataUtilities; @session_start(); // TODO suppressing warnings is wrong /* prepare the toolbox */ if (empty($_SESSION[Toolbox::class])) { $_SESSION[Toolbox::class] = Toolbox::fromConfiguration(CONFIG_FILE); } $toolbox =& $_SESSION[Toolbox::class]; /* set the Tool Consumer's instance URL, if present */ if (empty($_SESSION[CANVAS_INSTANCE_URL])) { if (!empty($_SESSION[ToolProvider::class]['canvas']['api_domain'])) { $_SESSION[CANVAS_INSTANCE_URL] = 'https://' . $_SESSION[ToolProvider::class]['canvas']['api_domain']; } else { $_SESSION[CANVAS_INSTANCE_URL] = $toolbox->config('TOOL_CANVAS_API')['url']; } } /* cache per-instance */ $toolbox->cache_pushKey(parse_url($_SESSION[CANVAS_INSTANCE_URL], PHP_URL_HOST)); /* Configure smarty templating */ /* FIXME this is sometimes superfluous overhead (e.g. action=config) */ $toolbox->smarty_prependTemplateDir(__DIR__ . '/templates', basename(__DIR__)); $toolbox->getSmarty()->addStylesheet(DataUtilities::URLfromPath(__DIR__ . '/css/canvas-management.css'), basename(__DIR__)); $toolbox->smarty_assign(['title' => $toolbox->config('TOOL_NAME'), 'category' => DataUtilities::titleCase(preg_replace('/[\\-_]+/', ' ', basename(__DIR__))), 'APP_URL' => $toolbox->config('APP_URL'), 'CANVAS_INSTANCE_URL' => $_SESSION[CANVAS_INSTANCE_URL], 'navbarActive' => basename(dirname($_SERVER['REQUEST_URI']))]);
/** * Update a Toolbox instance from a configuration file * * @see Toolbox::fromConfiguration() Use `Toolbox::fromConfiguration()` * * @param string $configFilePath * @param boolean $forceRecache * @return void */ protected function loadConfiguration($configFilePath, $forceRecache = false) { $logQueue = []; /* load the configuration file */ $config = new ConfigXML($configFilePath); /* configure database connections */ $this->setMySQL($config->newInstanceOf(mysqli::class, '/config/mysql')); /* configure metadata caching */ $id = $config->toString('/config/tool/id'); if (empty($id)) { $id = basename(dirname($configFilePath)) . '_' . md5(__DIR__ . file_get_contents($configFilePath)); $logQueue[] = " Automatically generated ID {$id}"; } $this->setMetadata(new AppMetadata($this->mysql, $id, self::TOOL_METADATA_TABLE)); /* update metadata */ if ($forceRecache || empty($this->metadata['TOOL_ID']) || empty($this->metadata['TOOL_LAUNCH_URL']) || empty($this->metadata['TOOL_CONFIG_FILE'])) { $tool = $config->toArray('/config/tool')[0]; $this->metadata['TOOL_ID'] = $id; $this->metadata['TOOL_NAME'] = empty($tool['name']) ? $id : $tool['name']; $this->metadata['TOOL_CONFIG_FILE'] = realpath($configFilePath); $configPath = dirname($this->metadata['TOOL_CONFIG_FILE']); if (!empty($tool['description'])) { $this->metadata['TOOL_DESCRIPTION'] = $tool['description']; } elseif (isset($this->metadata['TOOL_DESCRIPTION'])) { unset($this->metadata['TOOL_DESCRIPTION']); } if (!empty($tool['icon'])) { $this->metadata['TOOL_ICON_URL'] = file_exists("{$configPath}/{$tool['icon']}") ? DataUtilities::URLfromPath("{$configPath}/{$tool['icon']}") : $tool[self::ICON]; } elseif (isset($this->metadata['TOOL_ICON_URL'])) { unset($this->metadata['TOOL_ICON_URL']); } $this->metadata['TOOL_LAUNCH_PRIVACY'] = empty($tool['launch-privacy']) ? self::DEFAULT_LAUNCH_PRIVACY : $tool['launch-privacy']; if (!empty($tool['domain'])) { $this->metadata['TOOL_DOMAIN'] = $tool['domain']; } elseif (isset($this->metadata['TOOL_DOMAIN'])) { unset($this->metadata['TOOL_DOMAIN']); } $this->metadata['TOOL_LAUNCH_URL'] = empty($tool['authenticate']) ? DataUtilities::URLfromPath($_SERVER['SCRIPT_FILENAME']) : DataUtilities::URLfromPath("{$configPath}/{$tool['authenticate']}"); $logQueue[] = " Tool metadata configured"; } $configPath = dirname($this->metadata['TOOL_CONFIG_FILE']); /* configure logging */ if ($forceRecache || empty($this->metadata['TOOL_LOG'])) { $log = "{$configPath}/" . $config->toString('/config/tool/log'); shell_exec("touch \"{$log}\""); $this->metadata['TOOL_LOG'] = realpath($log); } $this->setLog(Log::singleton('file', $this->metadata['TOOL_LOG'])); if ($forceRecache) { $this->log("Resetting LTI configuration from {$configFilePath}"); } if (!empty($logQueue)) { foreach ($logQueue as $message) { $this->log($message); } unset($logQueue); } /* configure tool provider */ if ($forceRecache || empty($this->metadata['TOOL_HANDLER_URLS'])) { $handlers = $config->toArray('/config/tool/handlers')[0]; if (empty($handlers) || !is_array($handlers)) { throw new ConfigurationException('At least one handler/URL pair must be specified', ConfigurationException::TOOL_PROVIDER); } foreach ($handlers as $request => $path) { $handlers[$request] = DataUtilities::URLfromPath("{$configPath}/{$path}"); } $this->metadata['TOOL_HANDLER_URLS'] = $handlers; $this->log(' Tool provider handler URLs configured'); } /* configure API access */ if ($forceRecache || empty($this->metadata['TOOL_CANVAS_API'])) { $this->metadata['TOOL_CANVAS_API'] = $config->toArray('/config/canvas')[0]; if (empty($this->metadata['TOOL_CANVAS_API'])) { throw new ConfigurationException('Canvas API credentials must be provided', ConfigurationException::CANVAS_API_MISSING); } $this->log(' Canvas API credentials configured'); } }
/** * Construct the singleton instance of BootstrapSmarty * * @deprecated Use singleton pattern BootstrapSmarty::getSmarty() * * @param string|string[] $template (Optional) Additional Smarty template * directories * @param string|string[] $config (Optional) Additional Smarty config * directories * @param string $compile (Optional) Alternative Smarty compiled template * directory * @param string $cache (Optional) Alternative Smarty cache directory * * @return void * * @throws BootstrapSmarty_Exception SINGLETON If an instance of BootstrapSmarty already exists * * @see BootstrapSmarty::getSmarty() BootstrapSmarty::getSmarty() * @see http://www.phptherightway.com/pages/Design-Patterns.html#singleton Singleton Design Pattern **/ public function __construct($template = null, $config = null, $compile = null, $cache = null) { if (static::$singleton !== null) { throw new BootstrapSmarty_Exception('BootstrapSmarty is a singleton class, use the factory method ' . 'BootstrapSmarty::getSmarty() instead of ' . __METHOD__, BootstrapSmarty_Exception::SINGLETON); } else { parent::__construct(); static::$singleton = $this; } /* Default to local directories for use by Smarty */ $this->setTemplateDir([static::UI_KEY => realpath(__DIR__ . '/../templates')]); $this->setConfigDir([static::UI_KEY => realpath(__DIR__ . '/../configs')]); /* Apply user additions and alternates */ if (!empty($template)) { $this->prependTemplateDir($template); } if (!empty($config)) { $this->addConfigDir($config); } $this->setCompileDir(empty($compile) ? realpath(__DIR__ . '/../templates_c') : $compile); $this->setCacheDir(empty($cache) ? realpath(__DIR__ . '/../cache') : $cache); /* Test all directories for use by Smarty */ foreach ($this->getTemplateDir() as $key => $dir) { static::testAccess($dir); } foreach ($this->getConfigDir() as $key => $dir) { static::testAccess($dir); } static::testAccess($this->getCompileDir(), true); static::testAccess($this->getCacheDir(), true); /* set some reasonable defaults */ $this->url = DataUtilities::URLfromPath(dirname(__DIR__)); $this->assign('BOOTSTRAPSMARTY_URL', $this->url); $this->addStylesheet("{$this->url}/css/BootstrapSmarty.css", static::UI_KEY); $this->assign(['name' => DataUtilities::titleCase(preg_replace('/[\\-_]+/', ' ', urldecode(basename($_SERVER['REQUEST_URI'], '.php')))), 'category' => DataUtilities::titleCase(preg_replace('/[\\-_]+/', ' ', urldecode(basename(dirname($_SERVER['REQUEST_URI']))))), 'navbarActive' => false, 'MODULE_COLORPICKER' => static::MODULE_COLORPICKER, 'MODULE_DATEPICKER' => static::MODULE_DATEPICKER, 'MODULE_SORTABLE' => static::MODULE_SORTABLE]); }
if (!empty($_REQUEST['oauth-return'])) { $_SESSION['oauth']['return'] = $_REQUEST['oauth-return']; } /* have we been given a specific error URL? */ if (!empty($_REQUEST['oauth-error'])) { $_SESSION['oauth']['error'] = $_REQUEST['oauth-error']; } /* do we have a Canvas instance URL yet? */ if (empty($_SESSION['oauth']['instance']) && empty($_REQUEST['url'])) { $smarty->assign(['formAction' => $_SERVER['PHP_SELF'], 'reason' => empty($_REQUEST['reason']) ? false : $_REQUEST['reason']]); $smarty->display('oauth.tpl'); exit; } elseif (empty($_SESSION['oauth']['instance']) && !empty($_REQUEST['url'])) { $_SESSION['oauth']['instance'] = $_REQUEST['url']; } $provider = new CanvasLMS(['clientId' => $_SESSION['oauth']['key'], 'clientSecret' => $_SESSION['oauth']['secret'], 'purpose' => $_SESSION['oauth']['purpose'], 'redirectUri' => DataUtilities::URLfromPath(__FILE__), 'canvasInstanceUrl' => $_SESSION['oauth']['instance']]); /* if we don't already have an authorization code, let's get one! */ if (!isset($_GET['code'])) { $authorizationUrl = $provider->getAuthorizationUrl(); $_SESSION['oauth']['state'] = $provider->getState(); header("Location: {$authorizationUrl}"); exit; /* check that the passed state matches the stored state to mitigate cross-site request forgery attacks */ } elseif (empty($_GET['state']) || $_GET['state'] !== $_SESSION['oauth']['state']) { unset($_SESSION['oauth']); header("Location: {$_SESSION['oauth']['error']}?error[title]=Invalid State&" . 'error[message]=Mismatch between stored and received OAuth states, ' . 'may indicate CSRF attack.'); exit; } else { /* * acquire and save our token (using our existing code), pass back the * newly-acquired token in session data
</tr> <?php } ?> </table> </td> <?php } ?> </tr> </table> <div id="link"><a href="<?php $shortlink = DataUtilities::URLfromPath(__FILE__) . '?cache=' . $key; echo $shortlink; ?> "><?php echo $shortlink; ?> </a></div> </div> <?php if (!$printable) { ?> <h2>Enter a note for the bottom of the schedule</h2> <?php }
$advisee = isset($_REQUEST['advisee']) ? $_REQUEST['advisee'] : $advisees[0]['user']['id']; $toolbox->cache_pushKey($advisee); $courses = $toolbox->cache_get('courses'); if ($courses === false) { $allCourses = $toolbox->api_get("users/{$advisee}/courses"); $courses = []; foreach ($allCourses as $course) { if (!empty($course['account_id']) && isAcademic($course['account_id'])) { $courses[$course['id']] = $course; } } $toolbox->cache_set('courses', $courses); } $analytics = $toolbox->cache_get('analytics'); if ($analytics === false) { $analytics = []; foreach ($courses as $course) { $analytics[$course['id']] = $toolbox->api_get("courses/{$course['id']}/analytics/users/{$advisee}/assignments"); } $toolbox->cache_set('analytics', $analytics); } $toolbox->cache_popKey(); $toolbox->cache_popKey(); $toolbox->smarty_assign(['advisee' => $advisee, 'advisees' => $advisees, 'terms' => $terms, 'courses' => $courses, 'analytics' => $analytics, 'canvasInstanceUrl' => $_SESSION[CANVAS_INSTANCE_URL]]); /* * FIXME unclear why the post-bootstrap-scripts block isn't working in the * relative-grades.tpl file */ $toolbox->getSmarty()->addScript(DataUtilities::URLfromPath(__DIR__ . '/../vendor/npm-asset/chart.js/dist/Chart.min.js')); $toolbox->getSmarty()->addScript(DataUtilities::URLfromPath(__DIR__ . '/../js/relative-grades.js.php') . "?advisee={$advisee}"); $toolbox->smarty_display('relative-grades.tpl');
/** * Interactively acquire an API access token * * `/config/canvas/key` and `/config/canvas/secret` must be defined in * `config.xml` for this to work! * * @param string $reason Explanation of why an API access token is necessary * @param string $redirectURL (Optional, defaults to * `$_SERVER['REQUEST_URI']`) URL of page to redirect to after * acquiring access token * @param string $errorURL (Optional) URL of page to redirect to on error * @return void */ public function interactiveGetAccessToken($reason = null, $redirectURL = null, $errorURL = null) { $redirectURL = empty($redirectURL) ? $_SERVER['REQUEST_URI'] : $redirectURL; $errorURL = empty($errorURL) ? DataUtilities::URLfromPath(__DIR__ . '/../error.php') : $errorURL; $canvas = $this->metadata['TOOL_CANVAS_API']; if (!empty($canvas['key']) && !empty($canvas['secret'])) { /* if so, request an API access token interactively */ if (session_status() !== PHP_SESSION_ACTIVE) { session_start(); } $_SESSION['oauth'] = ['purpose' => $this->metadata['TOOL_NAME'], 'key' => $canvas['key'], 'secret' => $canvas['secret']]; header('Location: ' . DataUtilities::URLfromPath(__DIR__ . '/../oauth.php') . '?' . http_build_query(['oauth-return' => $redirectURL, 'oauth-error' => $errorURL, 'reason' => $reason])); break; } else { /* no (understandable) API credentials available -- doh! */ throw new ConfigurationException('Missing OAuth key/secret pair in configuration, which is ' . 'required to interactively acquire an API access token', ConfigurationException::CANVAS_API_MISSING); } }