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');
 }
예제 #3
0
	/**
	 * 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;
	}
예제 #4
0
	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']);
			}
		}
	}
예제 #5
0
	/**
	 * 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);
	}
예제 #6
0
 /**
  * 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;
 }