public static function Catch404Hook(View $view){ $request = PageRequest::GetSystemRequest(); // All the exact matches, in the order of precedence. $exactmatches = []; // The first search I want do is for the full URL exactly as submitted. // This is because the user can submit URLs with GET parameters attached to them. // It needs to act in a google-esque manner, where if the user requested x=1&y=2... then give them x=1 and y=2! $exactmatches[] = '/' . substr($request->uri, strlen(ROOT_WDIR)); // This one is the resolved URL, without any GET parameters. It's still a very common and very specific rewrite choice. $exactmatches[] = $request->uriresolved; // Now, look for them! foreach($exactmatches as $incomingurl){ // Look for it! $maps = RewriteMapModel::Find(array('rewriteurl' => $incomingurl)); // Did I get one did I get one did I get one? if(sizeof($maps)){ // Grab the first one, that'll be the latest, (should multiple exist.... somehow :/ ) $match = $maps[0]->get('baseurl'); // Resolve that to the new rewriteurl and redirect! $newpage = PageModel::Construct($match); \core\redirect($newpage->get('rewriteurl'), 301); } } // Else, no match was found... maybe it's a fuzzy page! // Since this page will have no longer existed, I can't just use the builtin logic :( $fuzzy = $request->uriresolved; do{ $fuzzy = substr($fuzzy, 0, strrpos($fuzzy, '/')); $fuzzymaps = RewriteMapModel::Find(array('rewriteurl' => $fuzzy, 'fuzzy' => '1')); if(sizeof($fuzzymaps)){ // Yay! // Don't forget to throw on the rest of the url. $match = $fuzzymaps[0]->get('baseurl'); $newpage = PageModel::Construct($match); $url = $newpage->get('rewriteurl'); if($newpage->get('fuzzy')){ // Only if the new page is fuzzy too. $url .= substr($incomingurl, strlen($fuzzy)); } \core\redirect($url, 301); } } while($fuzzy); // Sigh, guess this page didn't make the cut. // There is no return necessary, this hook will simply silently continue to the next. }
/** * Get the access string of the current page, if possible. * This is pulled from the corresponding page, but if it's an external page or manual URL, there won't be any Page to lookup, * so '*' is returned instead. * * @return string */ public function getAccessString() { if ($this->get('type') != 'int') { // External pages never can support access permissions, I don't know what the external page has! return '*'; } $page = PageModel::Construct($this->get('baseurl')); if (!$page->exists()) { // If the page doesn't exist, then there's nothing to go off of either! return '*'; } if ($page->get('access') == '') { // There's a weird bug where sometimes the access cache is empty. // In that case, just allow the user to view the page. return '*'; } // Otherwise! return $page->get('access'); }
/** * The save handler for /admin/pages quick edit. * * @param Form $form * * @return bool */ public static function PagesSave(Form $form) { $models = []; foreach($form->getElements() as $el){ /** @var FormElement $el */ $n = $el->get('name'); // i only want model if(strpos($n, 'model[') !== 0){ continue; } $baseurl = substr($n, 6, strpos($n, ']')-6); $n = substr($n, strpos($n, ']')+1); // Is this a meta attribute? if(strpos($n, '[meta][') === 0){ $ismeta = true; $n = substr($n, 7, -1); } else{ $ismeta = false; $n = substr($n, 1, -1); } // Make sure the model is available. if(!isset($models[$baseurl])){ $models[$baseurl] = PageModel::Construct($baseurl); } /** @var PageModel $p */ $p = $models[$baseurl]; if($ismeta){ $p->setMeta($n, $el->get('value')); } else{ $p->set($n, $el->get('value')); } } foreach($models as $p){ /** @var PageModel $p */ $p->save(); } return true; }
public function testSchema(){ $schema = PageModel::GetSchema(); $this->assertInternalType('array', $schema); /** @var PageModel $page */ $page = PageModel::Construct('/admin'); $schema2 = $page->getKeySchemas(); // The two schemas should be identical sizes, (they should contain identical content). $this->assertEquals(sizeof($schema), sizeof($schema2)); $asArray = $page->getAsArray(); foreach($asArray as $k => $val){ $this->assertArrayHasKey($k, $schema, 'PageModel schema does not contain key ' . $k); // Ensure that this schema element has all the required fields. $this->assertArrayHasKey('type', $schema[$k], 'PageModel schema ' . $k . ' does not contain a type'); $this->assertArrayHasKey('maxlength', $schema[$k], 'PageModel schema ' . $k . ' does not contain a maxlength'); $this->assertArrayHasKey('default', $schema[$k], 'PageModel schema ' . $k . ' does not contain a default field'); $this->assertArrayHasKey('comment', $schema[$k], 'PageModel schema ' . $k . ' does not contain a comment'); $this->assertArrayHasKey('null', $schema[$k], 'PageModel schema ' . $k . ' does not contain a null field'); $this->assertArrayHasKey('encrypted', $schema[$k], 'PageModel schema ' . $k . ' does not contain an encrypted field'); $this->assertArrayHasKey('required', $schema[$k], 'PageModel schema ' . $k . ' does not contain a required field'); $this->assertArrayHasKey('options', $schema[$k], 'PageModel schema ' . $k . ' does not contain an options field'); $this->assertArrayHasKey('title', $schema[$k], 'PageModel schema ' . $k . ' does not contain a title'); // If the default is null, then null must be true. if($schema[$k]['default'] === null){ $this->assertTrue($schema[$k]['null']); } } }
/** * Display a listing of all widgets registered in the system. */ public function admin(){ $view = $this->getView(); $request = $this->getPageRequest(); $viewer = \Core\user()->checkAccess('p:/core/widgets/manage'); $manager = \Core\user()->checkAccess('p:/core/widgets/manage'); if(!($viewer || $manager)){ return View::ERROR_ACCESSDENIED; } // Build a list of create pages for all registered components. $components = Core::GetComponents(); $pages = []; $skins = []; $selected = null; $selectedtype = null; $baseurl = null; $selectoptions = []; $links = []; $theme = ThemeHandler::GetTheme(); $formtheme = null; $formskin = null; $formtemplate = null; foreach($components as $c){ /** @var Component_2_1 $c */ $viewdir = $c->getViewSearchDir(); if($viewdir){ $dirlen = strlen($viewdir); $component = $c->getName(); $dh = \Core\Filestore\Factory::Directory($viewdir); //$pagetplfiles = $dh->ls('tpl', true); $pagetplfiles = $dh->ls(null, true); // not sure why getFilename(path) isn't working as expected, but this works too. foreach($pagetplfiles as $obj){ // I don't want directories. if($obj instanceof \Core\Filestore\Directory) continue; /** @var $obj \Core\Filestore\File */ $file = substr($obj->getFilename(), $dirlen); // Since this is a template, it may actually be in a different location than where the package maintainer put it. // ie: user template user/templates/pages/user/view.tpl may be installed to themes/myawesometheme/pages/user/view.tpl instead. $tpl = Core\Templates\Template::Factory($file); if($tpl->hasWidgetAreas()){ $pagetitle = $file; if(strpos($pagetitle, 'pages/') === 0){ $pagetitle = substr($pagetitle, 6); } // Replace directory slashes with a space $pagetitle = str_replace(['/', '-'], ' ', $pagetitle); // Capitalize them $pagetitle = ucwords($pagetitle); // And trim off the ".tpl" suffix. $pagetitle = substr($pagetitle, 0, -4); $pages[$file] = $pagetitle; } } } foreach($c->getXML()->getElements('/widgets/widgetcreate') as $node){ /** @var DOMElement $node */ if($node->getAttribute('baseurl')){ $nodebaseurl = $node->getAttribute('baseurl'); $image = ''; } elseif($node->getAttribute('class')){ /** @var Widget_2_1 $obj */ $obj = Widget_2_1::Factory($node->getAttribute('class')); $nodebaseurl = '/widget/create?class=' . $node->getAttribute('class'); if($obj){ $image = $obj->getPreviewImage(); } else{ \Core\set_message('Invalid "widgetcreate" found in ' .$node->getAttribute('class') . ', ' . $node->getAttribute('title'), 'error'); $image = ''; } } else{ \Core\set_message('Invalid "widgetcreate" found in ' . $c->getName() . ', ' . $node->getAttribute('title'), 'error'); continue; } $links[] = [ 'baseurl' => $nodebaseurl, 'title' => $node->getAttribute('title'), 'preview' => $image, ]; } } // Build the array of skins for the current theme $themeskins = $theme->getSkins(); $defaultskin = null; foreach($themeskins as $dat){ $skins[ 'skins/' . $dat['file'] ] = $dat['title']; if($dat['default']){ $defaultskin = 'skins/' . $dat['file']; } } // Now that the various templates have been loaded into a flat array, I need to sort them. asort($pages); asort($skins); foreach($skins as $k => $v){ $selectoptions[ $k ] = 'Skin: ' . $v; } foreach($pages as $k => $v){ $selectoptions[ $k ] = 'Page: ' . $v; } if($request->getParameter('baseurl')){ // It's a URL-specific request, lookup which template that page used last. $baseurl = $request->getParameter('baseurl'); $page = PageModel::Construct($baseurl); if(!isset($pages[ $page->get('last_template') ])){ \Core\set_message('Requested page template does not seem to contain any widget areas.', 'error'); \Core\go_back(); } $selected = $page->get('last_template'); $selectedtype = 'url'; $formtemplate = $selected; } elseif($request->getParameter('template')){ $selected = $request->getParameter('template'); if(isset($pages[ $selected ])){ $selectedtype = 'page'; $formtemplate = $selected; } else{ $selectedtype = 'skin'; $formtheme = $theme->getKeyName(); $formskin = $selected; } } else{ // Just use the default theme skin. $selected = $defaultskin; $selectedtype = 'skin';$formtheme = $theme->getKeyName(); $formskin = $selected; } $template = \Core\Templates\Template::Factory($selected); $areas = $template->getWidgetAreas(); $installables = [0 => '']; foreach($areas as $k => $dat){ // Ensure that each area has a widgets array, (even if it's empty) $areas[$k]['widgets'] = []; $installables[] = $dat['installable']; } $installables = array_unique($installables); $factory = new ModelFactory('WidgetInstanceModel'); $factory->order('weight'); if(Core::IsComponentAvailable('multisite') && MultiSiteHelper::IsEnabled()){ $factory->whereGroup('or', ['site = -1', 'site = ' . MultiSiteHelper::GetCurrentSiteID()]); } if($selectedtype == 'skin'){ // First, the skin-level where clause. $skinwhere = new Core\Datamodel\DatasetWhereClause(); $skinwhere->setSeparator('AND'); //$skinwhere->addWhere('theme = ' . $theme->getKeyName()); $skinwhere->addWhere('template = ' . $selected); $factory->where($skinwhere); } elseif($selectedtype == 'page'){ $factory->where('template = ' . $selected); } elseif($selectedtype == 'url'){ $factory->where('page_baseurl = ' . $baseurl); } else{ \Core\set_message('Invalid/unknown template type', 'error'); \Core\go_back(); } foreach($factory->get() as $wi){ /** @var $wi WidgetInstanceModel */ $a = $wi->get('widgetarea'); $areas[$a]['widgets'][] = $wi; } $available = WidgetModel::Find(['installable IN ' . implode(', ', $installables)]); /* $table = new Core\ListingTable\Table(); $table->setName('/admin/widgets'); $table->setModelName('WidgetModel'); // Add in all the columns for this listing table. $table->addColumn('Title', 'title'); if(Core::IsComponentAvailable('enterprise') && MultiSiteHelper::IsEnabled() && \Core\user()->checkAccess('g:admin')){ $table->addColumn('Site', 'site', false); $ms = true; } else{ $ms = false; } $table->getModelFactory()->where('installable IN ' . implode(', ', $installables)); $table->addColumn('Base URL', 'baseurl'); $table->addColumn('Installable', 'installable'); $table->addColumn('Created', 'created'); $table->loadFiltersFromRequest(); */ $view->mastertemplate = 'admin'; $view->title = 'All Widgets'; //$view->assign('table', $table); $view->assign('available_widgets', $available); $view->assign('links', $links); $view->assign('manager', $manager); $view->assign('theme', $formtheme); $view->assign('skin', $formskin); $view->assign('template', $selected); $view->assign('page_template', $formtemplate); $view->assign('page_baseurl', $baseurl); $view->assign('options', $selectoptions); $view->assign('selected', $selected); $view->assign('areas', $areas); //$view->assign('multisite', $ms); }
/** * Helper utility to import a given remote blog. * * @param bool $verbose Set to true to enable real-time verbose output of the operation. * @return array * * @throws Exception */ public function importFeed($verbose = false) { $blogid = $this->get('id'); if (!$this->exists()) { throw new Exception('Unable to import a blog that does not exist!'); } // Make sure this is a remote blog. if ($this->get('type') != 'remote') { throw new Exception('Cannot import a blog that is not remote!'); } $file = \Core\Filestore\Factory::File($this->get('remote_url')); if (!$file->exists()) { throw new Exception($this->get('remote_url') . ' does not appear to exist'); } $defaults = ['parenturl' => $this->get('baseurl'), 'site' => $this->get('site'), 'component' => 'blog']; $changes = ['added' => 0, 'updated' => 0, 'skipped' => 0, 'deleted' => 0]; $changelog = ''; // I need a list of current articles in this feed. This is because remote deletions won't be coming in on the feed. $map = array(); $articles = BlogArticleModel::FindRaw(['blogid = ' . $blogid]); foreach ($articles as $a) { $map[$a['guid']] = $a['id']; } // I can't trust that remote files list what they actually are because many frameworks, // (WP in specific), do not correctly use content-types :/ $contents = $file->getContents(); // Which feed type is this? $header = substr($contents, 0, 400); // All the standardized records $records = array(); if (strpos($header, '<rss ') !== false) { if ($verbose) { echo 'Found an RSS feed with the URL of ' . $file->getURL() . '!<br/>' . "\n"; ob_flush(); flush(); } $xml = new XMLLoader(); $xml->setRootName('rss'); $xml->loadFromString($contents); foreach ($xml->getElements('channel/item') as $item) { $dat = ['guid' => '', 'link' => '', 'thumbnail' => '', 'published' => '', 'updated' => '', 'description' => '']; foreach ($item->childNodes as $child) { if ($child->nodeName == '#text') { continue; } switch ($child->nodeName) { case 'media:thumbnail': $dat['thumbnail'] = $child->getAttribute('url'); break; case 'pubDate': $dat['published'] = $child->nodeValue; break; default: $dat[$child->nodeName] = $child->nodeValue; } } $records[] = $dat; } } elseif (strpos($header, 'http://www.w3.org/2005/Atom') !== false) { if ($verbose) { echo 'Found an ATOM feed with the URL of ' . $file->getURL() . '!<br/>' . "\n"; ob_flush(); flush(); } $xml = new XMLLoader(); $xml->setRootName('feed'); $xml->loadFromString($contents); foreach ($xml->getRootDOM()->childNodes as $item) { if ($item->nodeName != 'entry') { continue; } $dat = ['guid' => '', 'link' => '', 'thumbnail' => '', 'published' => '', 'updated' => '', 'description' => '']; $imgheight = 0; foreach ($item->childNodes as $child) { if ($child->nodeName == '#text') { continue; } switch ($child->nodeName) { case 'id': $dat['guid'] = $child->nodeValue; break; case 'link': if ($child->getAttribute('rel') == 'alternate' && $child->getAttribute('type') == 'text/html') { if ($child->nodeValue) { $dat['link'] = $child->nodeValue; } else { $dat['link'] = $child->getAttribute('href'); } } break; case 'im:image': if ($child->getAttribute('height') > $imgheight) { $dat['thumbnail'] = $child->nodeValue; $imgheight = $child->getAttribute('height'); } break; case 'updated': $dat['updated'] = strtotime($child->nodeValue); break; case 'summary': if ($dat['description'] != '') { $dat['description'] = $child->nodeValue; } break; case 'content': $dat['description'] = $child->nodeValue; break; default: $dat[$child->nodeName] = $child->nodeValue; } } if (!$dat['published'] && $dat['updated']) { // make sure that there's a published date. $dat['published'] = $dat['updated']; } $records[] = $dat; } } else { throw new Exception('Invalid remote file found, please ensure it is either an RSS or Atom feed!'); } // Now that they're standardized... foreach ($records as $dat) { /** @var PageModel $page */ $page = PageModel::Construct($dat['link']); $published = $dat['published'] == '' || is_numeric($dat['published']) ? $dat['published'] : strtotime($dat['published']); $updated = $dat['updated'] != '' ? is_numeric($dat['updated']) ? $dat['updated'] : strtotime($dat['updated']) : $published; $pagedat = ['published' => $published, 'title' => $dat['title'], 'body' => $dat['description'], 'updated' => $updated]; $newpagedat = array_merge($defaults, ['selectable' => '0']); $page->setFromArray($pagedat); if (!$page->exists()) { // Add the "new" dat only if the page doesn't exist before. $page->setFromArray($newpagedat); } if ($dat['thumbnail']) { $remote = \Core\Filestore\Factory::File($dat['thumbnail']); $new = $remote->copyTo('public/blog/'); $page->setMeta('image', $new->getFilename(false)); } $page->setMeta('guid', $dat['guid']); $thischange = $page->exists() ? 'updated' : 'added'; if ($page->changed()) { $page->save(); $changes[$thischange]++; $changelog .= $thischange . ' ' . $dat['title'] . "<br/>\n"; if ($verbose) { echo $thischange . ' ' . $dat['title'] . "<br/>\n"; ob_flush(); flush(); } } else { $changes['skipped']++; if ($verbose) { echo 'No changes to ' . $dat['title'] . "<br/>\n"; ob_flush(); flush(); } } } return ['status' => 1, 'message' => 'Import feed successfully!', 'added' => $changes['added'], 'updated' => $changes['updated'], 'deleted' => $changes['deleted'], 'skipped' => $changes['skipped'], 'changelog' => $changelog]; }
/** * Hook to check for any new files in the system and register pages (or deregister them as necessary). * * Only runs if the config option is enabled to do so. */ public static function _AutoRegisterFiles() { if (!\ConfigHandler::Get('/markdownbrowser/autoregister')) { echo 'Skipping autoregistration of markdown files, configuration option for this feature is disabled.' . "\n"; return true; } $dir = \ConfigHandler::Get('/markdownbrowser/basedir'); $markdownFiles = []; if (!$dir) { echo 'Skipping autoregistration of markdown files, no markdown directory configured.' . "\n"; return true; } // Make sure it's readable! $dir = \Core\Filestore\Factory::Directory($dir); $dirbase = $dir->getPath(); $dirlen = strlen($dirbase); if (!$dir->exists()) { echo 'Skipping autoregistration of markdown files, ' . $dir->getPath() . ' does not exist.' . "\n"; return true; } $all = []; $files = $dir->ls('md', true); foreach ($files as $file) { /** @var \Core\Filestore\File $file */ $fileBase = substr($file->getFilename(), $dirlen); if (strpos($fileBase, 'index.md') !== false) { $fileRel = substr($fileBase, 0, -8); } else { $fileRel = substr($fileBase, 0, -3); } if (preg_match('/[A-Z]/', $fileRel) !== 0) { $warning = t('STRING_MARKDOWNBROWSER_WARNING_FILE_HAS_CAPITALS'); } elseif (strpos($fileRel, ' ')) { $warning = t('STRING_MARKDOWNBROWSER_WARNING_FILE_HAS_SPACES'); } else { $warning = ''; } if ($warning == '') { $url = '/markdownbrowser/view/' . $fileRel; $all[$url] = ['file' => $file, 'page' => null]; } else { echo $warning . ' - ' . $fileRel . "\n"; } } // Now that the files are loaded into memory, load any page that may already exist. // This will be used to ignore entries that already have a page, to create ones without, // and to remove pages that no longer have a corresponding file. $pages = PageModel::Find(['baseurl LIKE /markdownbrowser/view%']); foreach ($pages as $p) { $url = $p->get('baseurl'); if (!isset($all[$url])) { $all[$url] = ['file' => null, 'page' => null]; } $all[$url]['page'] = $p; } // Now $all contains everything I need to process on! :) foreach ($all as $url => &$dat) { /** @var PageModel|null $page */ $page = $dat['page']; /** @var \Core\Filestore\File|null $file */ $file = $dat['file']; if ($page && !$file) { // There is a page but no file, DELETE! $page->delete(); echo 'Deleted page for non-existent file: ' . $url . "\n"; } elseif (!$page && $file) { // There is a file but no page, create. $contents = $file->getContents(); // Convert these contents from markdown to HTML. $processor = new \Core\MarkdownProcessor(); $html = $processor->transform($contents); // Pre-populate this page with information from the rendered markdown document. // If this page exists, then it'll be updated and kept in sync. // Else, it'll still be set with what's in the document and kept in sync. $page = PageModel::Construct($url); $page->set('title', $processor->getMeta('title')); $page->set('body', $html); $page->set('baseurl', $url); $page->set('rewriteurl', $url); $page->set('editurl', str_replace('/markdownbrowser/view', '/markdownbrowser/update', $url)); $page->set('component', 'markdown-browser'); $page->set('selectable', 1); $page->set('published', $file->getMTime()); $page->set('updated', $file->getMTime()); $page->set('created', $file->getMTime()); $page->save(); echo 'Created page for new file: ' . $url . "\n"; } } return true; }