/** * @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); }
$sections[$section['metadata']['id']] = $section; } $section = array(); $section['metadata']['id'] = $d['CourseID']; foreach ($d as $column => $value) { switch ($column) { case 'ClassName': $section['metadata']['name'] = DataUtilities::titleCase($value); break; case 'TeacherName': $section['metadata']['teacher'] = $value; break; default: if (preg_match('/Book\\d+/', $column)) { if (!empty($value)) { $section['books'][] = DataUtilities::titleCase($value); } } } } } $section['students'][] = $d['StudentName']; } if (!empty($section['metadata']['id'])) { $sections[$section['metadata']['id']] = $section; } $smarty->assign('teacherDeadline', date('l, F j', strtotime($_REQUEST['teacher-deadline']))); $smarty->assign('deptDeadline', date('l, F j', strtotime($_REQUEST['dept-deadline']))); $smarty->assign('blanks', isset($_REQUEST['blanks']) ? $_REQUEST['blanks'] : 0); $smarty->assign('sections', $sections); $smarty->display('book-list/sheet.tpl');
<?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']))]);
require_once __DIR__ . '/vendor/autoload.php'; use smtech\CanvasHack\Toolbox; use smtech\ReflexiveCanvasLTI\LTI\ToolProvider; use Battis\DataUtilities; define('CONFIG_FILE', __DIR__ . '/config.xml'); define('CANVAS_INSTANCE_URL', 'canvasInstanceUrl'); @session_start(); // TODO I don't feel good about suppressing warnings /* prepare the toolbox */ if (empty($_SESSION[Toolbox::class])) { $_SESSION[Toolbox::class] =& Toolbox::fromConfiguration(CONFIG_FILE); } $toolbox =& $_SESSION[Toolbox::class]; if (php_sapi_name() !== 'cli') { $toolbox->smarty_prependTemplateDir(__DIR__ . '/templates', basename(__DIR__)); $toolbox->smarty_assign(['category' => DataUtilities::titleCase(preg_replace('/[\\-_]+/', ' ', basename(__DIR__)))]); $smarty =& $toolbox->getSmarty(); // FIXME } /* * FIXME convience variables until plugins are all updated */ $api =& $toolbox->getAPI(); $sql =& $toolbox->getMySQL(); $customPrefs =& $toolbox->getCustomPrefs(); /* 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'];
/** * 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 }
unset($source['sis_course_id']); unset($source['integration_id']); unset($source['name']); unset($source['course_code']); unset($source['account_id']); unset($source['enrollment_term_id']); unset($source['start_at']); unset($source['end_at']); unset($source['enrollments']); /* why nest this, I mean... really? */ $source = array('course' => $source); } catch (Exception $e) { $toolbox->exceptionErrorMessage($e); } } $csv = DataUtilities::loadCsvToArray('csv'); if ($csv) { $courses = array_merge($courses, $csv); } if (!empty($courses)) { foreach ($courses as $course) { /* build parameter list */ $params = array(); if (!empty($course['course_id'])) { $params['sis_course_id'] = $course['course_id']; } else { $params['sis_course_id'] = generateSisId(empty($course['course_id']) ? $course['long_name'] : $course['course_id']); } if (!empty($course['long_name'])) { $params['name'] = $course['long_name']; }
$sourceName = $source['name']; /* clear settings that are provided form entry */ unset($source['id']); unset($source['sis_course_id']); unset($source['integration_id']); unset($source['name']); unset($source['course_code']); unset($source['account_id']); unset($source['enrollment_term_id']); unset($source['start_at']); unset($source['end_at']); unset($source['enrollments']); /* why nest this, I mean... really? */ $source = array('course' => $source); } $courses = DataUtilities::loadCsvToArray('csv'); if ($step == STEP_CONFIRM) { if (empty($courses)) { $toolbox->smarty_addMessage('Courses', 'No courses found in uploaded list.', NotificationMessage::ERROR); $step = STEP_INSTRUCTIONS; } else { foreach ($courses as $course) { /* duplicate course settings */ $course = $toolbox->api_put("/courses/sis_course_id:{$course['course_id']}", $source); // TODO nice to figure out navigation settings copy /* duplicate course content */ $migration = $toolbox->api_post("courses/{$course['id']}/content_migrations", array('migration_type' => 'course_copy_importer', 'settings[source_course_id]' => $template)); $toolbox->smarty_addMessage("<a target=\"_parent\" href=\"{$_SESSION[CANVAS_INSTANCE_URL]}/courses/{$course['id']}\">{$course['name']}</a>", "has been templated as a clone of <a target=\"_parent\" href=\"{$_SESSION[CANVAS_INSTANCE_URL]}/courses/{$sourceId}\">{$sourceName}</a>. Course content is being <a target=\"_parent\" href=\"{$_SESSION[CANVAS_INSTANCE_URL]}/courses/{$course['id']}/content_migrations\">migrated</a> right now.", NotificationMessage::GOOD); } } }
$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); } }
<?php require_once 'common.inc.php'; use Battis\DataUtilities; use Battis\BootstrapSmarty\NotificationMessage; define('STEP_INSTRUCTIONS', 1); define('STEP_CONFIRM', 2); define('STEP_UPDATE', 3); $step = empty($_REQUEST['step']) ? STEP_INSTRUCTIONS : $_REQUEST['step']; $ignoreCourseId = empty($_REQUEST['ignore_course_id']) ? false : $_REQUEST['ignore_course_id']; switch ($step) { case STEP_CONFIRM: $sections = DataUtilities::loadCsvToArray('csv'); if (empty($sections)) { $step = STEP_INSTRUCTIONS; $toolbox->smarty_addMessage('Empty section list', 'The uploaded CSV file contained no sections.', NotificationMessage::WARNING); } if ($step == STEP_CONFIRM) { $toolbox->smarty_assign(['fields' => array_keys($sections[0]), 'sections' => $sections, 'formHidden' => ['step' => STEP_UPDATE, 'ignore_course_id' => $ignoreCourseId]]); $toolbox->smarty_display(basename(__FILE__, '.php') . '/confirm.tpl'); break; } /* flows into STEP_UPDATE */ /* flows into STEP_UPDATE */ case STEP_UPDATE: if ($step == STEP_UPDATE) { $links = []; $crosslist = []; $crosslistFail = []; foreach ($_REQUEST['sections'] as $section) { if (isset($section['batch-include']) && $section['batch-include'] == 'include') {
function titleCase($s) { return DataUtilities::titleCase(str_replace('_', ' ', $s)); }