public function index(){

		$view = $this->getView();

		$permissionmanager = \Core\user()->checkAccess('p:/user/permissions/manage');

		$factory = new ModelFactory('UserGroupModel');

		if(Core::IsComponentAvailable('multisite') && MultiSiteHelper::IsEnabled()){
			if(MultiSiteHelper::GetCurrentSiteID()){
				// Child site, only display global and site-specific sites.
				$w = new \Core\Datamodel\DatasetWhereClause();
				$w->setSeparator('or');
				$w->addWhere('site = ' . MultiSiteHelper::GetCurrentSiteID());
				$w->addWhere('site = -1');
				$factory->where($w);

				$displayglobal = true;
				$multisite = false;
			}
			else {
				// Root site, display all groups across all sites.
				$factory->where('site != -2');
				$displayglobal = false;
				$multisite = true;
			}
			$site = MultiSiteHelper::GetCurrentSiteID();
		}
		else{
			$displayglobal = false;
			$multisite = false;
			$site = null;
		}

		$factory->order('name');
		$groups = $factory->get();

		$view->title = 'User Group Administration';
		$view->assign('groups', $groups);
		$view->assign('permissionmanager', $permissionmanager);
		$view->assign('display_global', $displayglobal);
		$view->assign('site', $site);
		$view->assign('multisite', $multisite);
		$view->addControl('Add Group', '/usergroupadmin/create', 'add');
	}
Example #2
0
	/**
	 * Internal function to do the multisite check on the model.
	 * If the model supports a site attribute and none requested, then set it to the current site.
	 */
	private function _performMultisiteCheck(){

		$m = $this->_model;
		$ref = new ReflectionClass($m);

		$schema = $ref->getMethod('GetSchema')->invoke(null);
		$index = $ref->getMethod('GetIndexes')->invoke(null);
		//$schema = $m::GetSchema();
		//$index = $m::

		// Is there a site property?  If not I don't even care.
		if(
			isset($schema['site']) &&
			$schema['site']['type'] == Model::ATT_TYPE_SITE &&
			Core::IsComponentAvailable('multisite') &&
			MultiSiteHelper::IsEnabled()
		){
			// I want to look it up because if the script actually set the site, then
			// it evidently wants it for a reason.
			$siteexact = (sizeof($this->_dataset->getWhereClause()->findByField('site')) > 0);
			$idexact = false;

			// The primary check will allow a model to be instantiated with the exact primary key string.
			$pri = isset($index['primary']) ? $index['primary'] : null;
			
			if($pri && !is_array($pri)) $pri = [$pri];
			
			if($pri){
				$allids = true;
				foreach($pri as $k){
					if(sizeof($this->_dataset->getWhereClause()->findByField($k)) == 0){
						$allids = false;
						break;
					}
				}
				if($allids) $idexact = true;
			}

			if(!($siteexact || $idexact)){
				$w = new \Core\Datamodel\DatasetWhereClause();
				$w->setSeparator('or');
				$w->addWhere('site = ' . MultiSiteHelper::GetCurrentSiteID());
				$w->addWhere('site = -1');
				$this->_dataset->where($w);

				//$this->_dataset->where('site = ' . MultiSiteHelper::GetCurrentSiteID());
			}
		}
	}
	/**
	 * parses conditions usually used in WHERE
	 *
	 * @return  array   parsed condition
	 * @uses  SQL_Parser::$token
	 * @uses  SQL_Parser::$lexer
	 * @uses  SQL_Parser::getTok()
	 * @uses  SQL_Parser::raiseError()
	 * @uses  SQL_Parser::getParams()
	 * @uses  SQL_Parser::isFunc()
	 * @uses  SQL_Parser::parseFunctionOpts()
	 * @uses  SQL_Parser::parseCondition()
	 * @uses  SQL_Parser::isReserved()
	 * @uses  SQL_Parser::isOperator()
	 * @uses  SQL_Parser::parseSelect()
	 * @uses  SQL_Parser_Lexer::$tokText
	 * @uses  SQL_Parser_Lexer::unget()
	 * @uses  SQL_Parser_Lexer::pushBack()
	 */
	public function parseWhereCondition()
	{
		$clause = new Core\Datamodel\DatasetWhereClause();

		$laststatement = new Core\Datamodel\DatasetWhere();

		while (true) {
			// parse the first argument
			if ($this->token == 'not') {
				$this->getTok();
			}

			if ($this->token == '(') {
				$this->getTok();
				$clause->addWhere($this->parseWhereCondition());
				if ($this->token != ')') {
					$this->raiseError('Expected ")"');
				}
				$this->getTok();
			} elseif ($this->isFunc()) {
				$result = $this->parseFunctionOpts();
				if (false === $result) {
					return $result;
				}
				var_dump($result); die('Umm, now what?');
				$clause['args'][] = $result;
			} elseif ($this->token == 'ident') {
				// This is a column key name
				$parsed = $this->parseIdentifier();
				$laststatement->field = $parsed['column'];
			} else {
				$arg = $this->lexer->tokText;

				// Translate NULL to the actual null value.
				if($arg === 'NULL' || $arg === 'null') $arg = null;

				$laststatement->value = $arg;
				$this->getTok();
			}

			if (! $this->isOperator()) {
				// no operator, return (after I append the final last statement)
				if($laststatement->field){
					$clause->addWhere($laststatement);
				}

				return $clause;
			}

			// parse the operator
			$op = $this->token;
			if ($op == 'not') {
				$this->getTok();
				$not = 'not ';
				$op = $this->token;
			} else {
				$not = '';
			}

			$this->getTok();
			switch ($op) {
				case 'is':
					// parse for 'is' operator
					if ($this->token == 'not') {
						$op .= ' not';
						$this->getTok();
					}
					$laststatement->op = $op;
					break;
				case 'like':
					$laststatement->op = $not . $op;
					break;
				case 'between':
					// @todo
					//$clause['ops'][] = $not . $op;
					//$this->getTok();
					break;
				case 'in':
					// parse for 'in' operator
					if ($this->token != '(') {
						$this->raiseError('Expected "("');
					}

					// read the subset
					$this->getTok();
					// is this a subselect?
					if ($this->token == 'select') {
						$this->raiseError('Datasets do not supported nested SELECT statements!');
						//$clause['args'][] = $this->parseSelect(true);
					} else {
						$this->lexer->pushBack();
						// parse the set
						$result = $this->getParams($values, $types);
						if (false === $result) {
							return $result;
						}

						$laststatement->value = $values;
					}
					if ($this->token != ')') {
						$this->raiseError('Expected ")"');
					}
					break;
				case 'and':
				case 'or':
					// AND and OR statements are where statement separators.
					$clause->setSeparator($not . $op);
					// Don't forget to append the previous statement and start a new one.
					$clause->addWhere($laststatement);
					$laststatement = new Core\Datamodel\DatasetWhere();
					continue;
					break;
				default:
					$laststatement->op = $not . $op;
			}
			// next argument [with operator]
		}

		return $clause;
	}
	/**
	 * Given all the user defined filter, sort, and what not, apply those values to the ModelFactory if possible.
	 *
	 * @since 2.4.0
	 * @param ModelFactory $factory
	 */
	public function applyToFactory(ModelFactory $factory){
		if($this->hassort){
			$factory->order($this->getOrder());
		}

		if($this->haspagination){
			// Determine the starting count if the page is requested.
			if($this->_currentpage > 1){
				$startat = $this->_limit * ($this->_currentpage - 1);
				$factory->limit($startat . ', ' . $this->_limit);
			}
			else{
				$factory->limit($this->_limit);
			}
		}

		foreach($this->_elements as $el){
			/** @var $el FormElement */
			$name = $el->get('name');
			$idxname = $name;

			if(strpos($name, 'filter[') === 0){
				$name = substr($name, 7, -1);
			}

			// If this element is not in the index of elements, skip to the next element.
			if(!isset($this->_elementindexes[$idxname])){
				continue;
			}

			// If this doesn't have a link attribute, just skip.
			if(!$el->get('link')){
				continue;
			}

			// No value, just skip.
			if($el->get('value') === '' || $el->get('value') === null){
				continue;
			}

			// If there is a "" option, interpret that as empty and allow "0" to be used.
			if($el->get('value') === '0'){
				if($el->get('options') && isset($el->get('options')[''])){
					// '' is set... proceed.
				}
				else{
					continue;
				}
			}

			$value = $el->get('value');

			// Was there a prefix and/or suffix requested?
			if($el->get('linkvalueprefix')){
				$value = $el->get('linkvalueprefix') . $value;
			}
			if($el->get('linkvaluesuffix')){
				$value = $value . $el->get('linkvaluesuffix');
			}

			// If this link is a date object, convert a date string to its unix timestamp representation.
			if($el instanceof FormDateInput || $el->get('dateformat')){
				// Default to a unix timestamp, but allow the user to override this.
				// This is useful for saving a date in the datastore as a human-readable format.
				$format = $el->get('dateformat') ? $el->get('dateformat') : 'U';
				$date = new CoreDateTime($value);
				$value = $date->getFormatted($format, Time::TIMEZONE_GMT);
			}

			if($el->get('linkname')){
				$name = $el->get('linkname');
			}

			// New support for multiple link names!
			if(!is_array($name)){
				$name = [$name];
			}
			$statements = [];

			foreach($name as $n){
				switch($el->get('link')){
					case FilterForm::LINK_TYPE_STANDARD:
					case FilterForm::LINK_TYPE_GT:
					case FilterForm::LINK_TYPE_GE:
					case FilterForm::LINK_TYPE_LT:
					case FilterForm::LINK_TYPE_LE:
						$statements[] = $n . $el->get('link') . $value;
						break;
					case FilterForm::LINK_TYPE_STARTSWITH:
						$statements[] = $n . ' LIKE ' . $value . '%';
						break;
					case FilterForm::LINK_TYPE_CONTAINS:
						$statements[] = $n . ' LIKE %' . $value . '%';
						break;
				}
			}

			if(sizeof($statements) > 1){
				// Create a sub where clause for these.
				$subwhere = new \Core\Datamodel\DatasetWhereClause();
				$subwhere->setSeparator('OR');
				foreach($statements as $s){
					$subwhere->addWhere($s);
				}
				// Add this sub clause to the main where clause.
				$factory->where($subwhere);
			}
			else{
				// A single command just gets added to the main clause.
				$factory->where($statements[0]);
			}
		}

		// Might as well update the count now, it can always be updated later.
		$this->setTotalCount($factory->count());
	}
 public function analytics()
 {
     $request = $this->getPageRequest();
     $view = $this->getView();
     $manager = \Core\user()->checkAccess('p:/package_repository/view_analytics');
     if (!$manager) {
         return View::ERROR_ACCESSDENIED;
     }
     // Retrieve a list of connections to this repo for both downloading and checks!
     $where = new \Core\Datamodel\DatasetWhereClause();
     $where->addWhereSub('OR', ['baseurl = /packagerepository', 'baseurl = /packagerepository/download']);
     // Default to a 3-month window for now just to have a relatively useful sample of data.
     // This will be expanded to include a filter at some point in time.
     $window = new \Core\Date\DateTime();
     $window->modify('-3 months');
     $window = $window->format('U');
     // Generate a boilerplate dataset for the all-history view.
     // This is required because the graphing software expects all data to be in the same columns,
     // and these columns are simply indexed arrays.
     // As we're pulling the data potentially out-of-order and across different versions,
     // this array will provide a consistent scaffold for unset versions.
     $allboilerplate = [];
     // Series Boilerplate
     $allmonths = [];
     // Labels
     // This goes back 12 months.
     $date = new \Core\Date\DateTime();
     $date->modify('-11 months');
     for ($i = 1; $i <= 12; $i++) {
         $allboilerplate[$date->format('Ym')] = null;
         $allmonths[] = $date->format('M');
         $date->nextMonth();
     }
     $raw = UserActivityModel::FindRaw($where);
     // Will contain a list of useragents along with the count of how many access.
     $useragents = [];
     // Will contain how many times a given IP has requested the site.
     // This is for a metric that currently is not enabled.
     $ipaddresses = [];
     // A rich list of hosts along with the latest version, the IP connecting from, and the date of the last check.
     $hosts = [];
     // All series for the bar graph at the top of the page, Keyed by version and contains the total number of hits.
     $allseries = [];
     $allseries['Total'] = ['class' => 'series-other', 'name' => 'Total', 'title' => 'Total', 'useragent' => '', 'values' => $allboilerplate];
     // Used so I can compare the version of the connecting useragent against the current version of Core.
     // This of course does noothing to ensure that this site is updated, but it at least should give some perspective.
     $currentVersion = Core::VersionSplit(Core::GetComponent('core')->getVersion());
     foreach ($raw as $dat) {
         if (strpos($dat['useragent'], '(http://corepl.us)') !== false) {
             /** @var string $ua ex: "Core Plus 1.2.3" */
             $ua = str_replace(' (http://corepl.us)', '', $dat['useragent']);
             /** @var string $version Just the version, ex: "1.2.3" */
             $version = str_replace('Core Plus ', '', $ua);
             /** @var string $referrer Original Site/Server, ex: "http://corepl.us" */
             $referrer = $dat['referrer'] ? $dat['referrer'] : $dat['ip_addr'];
             // The set of logic to compare the current version of Core against the version connecting.
             // This is used primarily to set a class name onto the graphs so that they can be coloured specifically.
             $v = Core::VersionSplit($version);
             // These two values are used in the historical map, (as revision may be a bit useless at this scale).
             $briefVersion = $v['major'] . '.' . $v['minor'] . '.x';
             $briefUA = 'Core Plus ' . $briefVersion;
             if ($v['major'] == $currentVersion['major'] && $v['minor'] == $currentVersion['minor']) {
                 // Check is same version as current (or newer), blue!
                 $class = 'series-current';
             } elseif ($v['major'] + 2 <= $currentVersion['major']) {
                 // Check is at least 2 major versions out of date, red.
                 $class = 'series-outdated-2';
             } elseif ($v['major'] + 1 <= $currentVersion['major']) {
                 // Check is at least 1 major version out of date, green.
                 $class = 'series-outdated-1';
             } else {
                 // Same major version, close enough.
                 $class = 'series-outdated-0';
             }
             $month = date('Ym', $dat['datetime']);
         } else {
             $ua = 'Other';
             $briefUA = 'Other';
             $version = null;
             $briefVersion = null;
             $referrer = null;
             $class = 'series-other';
             $month = null;
         }
         // All Data!
         if ($month && array_key_exists($month, $allboilerplate)) {
             if (!isset($allseries[$briefUA])) {
                 $allseries[$briefUA] = ['class' => $class, 'name' => $briefVersion, 'title' => $briefUA, 'useragent' => $briefUA, 'values' => $allboilerplate];
             }
             $allseries[$briefUA]['values'][$month]++;
             //$allseries['Total']['values'][$month]++;
         }
         // Is this data new enough to display on the graph?
         // This is required because the "all" graph at the top needs all-time, (or at least the past 12 months).
         if ($dat['datetime'] >= $window) {
             // USER AGENT DATA
             if (!isset($useragents[$ua])) {
                 $useragents[$ua] = ['value' => 0, 'class' => $class, 'name' => $version, 'title' => $ua, 'useragent' => $ua];
             }
             $useragents[$ua]['value']++;
             // IP ADDRESS DATA
             if (!isset($ipaddresses[$dat['ip_addr']])) {
                 $ipaddresses[$dat['ip_addr']] = ['ip_addr' => $dat['ip_addr'], 'count' => 0];
             }
             $ipaddresses[$dat['ip_addr']]['count']++;
             // HOSTS DATA
             if ($version && $referrer) {
                 $k = $referrer . '-' . $dat['ip_addr'];
                 if (!isset($hosts[$k])) {
                     $hosts[$k] = ['servername' => $referrer, 'ip_addr' => $dat['ip_addr'], 'version' => '0.0', 'datetime' => 1];
                 }
                 if (Core::VersionCompare($hosts[$k]['version'], $version, 'lt')) {
                     $hosts[$k]['version'] = $version;
                 }
                 if ($hosts[$k]['datetime'] < $dat['datetime']) {
                     $hosts[$k]['datetime'] = $dat['datetime'];
                 }
             }
         }
     }
     ksort($useragents);
     ksort($hosts, SORT_NATURAL);
     ksort($allseries, SORT_NATURAL);
     ksort($ipaddresses, SORT_NATURAL);
     // Update the title of the values now that the totals have been created.
     // Also, take this opportunity to set the chart data as necessary, (as its format will be slightly different).
     $chart = ['versions' => ['labels' => [], 'series' => []], 'all' => ['labels' => array_values($allmonths), 'series' => []], 'ips' => []];
     foreach ($useragents as &$dat) {
         $dat['title'] .= ' (' . $dat['value'] . ' Total Hits)';
         $chart['versions']['labels'][] = $dat['name'];
         $chart['versions']['series'][] = ['value' => $dat['value'], 'className' => $dat['class'], 'name' => $dat['name'], 'title' => $dat['title']];
     }
     foreach ($allseries as &$dat) {
         $data = [];
         foreach ($dat['values'] as $v) {
             $data[] = ['value' => $v, 'title' => $dat['title'] . ' (' . $v . ' Monthly Hits)'];
             //$data[] = $v;
         }
         $chart['all']['series'][] = ['data' => $data, 'className' => $dat['class'], 'name' => $dat['name'], 'title' => $dat['title']];
     }
     // Convert these to JSON data!
     $chart['versions'] = json_encode($chart['versions']);
     $chart['all'] = json_encode($chart['all']);
     //$chart['ips'] = json_encode($chart['ips']);
     $view->title = 't:STRING_PACKAGE_REPOSITORY_ANALYTICS';
     $view->assign('chart', $chart);
     $view->assign('raw', $raw);
     $view->assign('ip_addresses', $ipaddresses);
     $view->assign('useragents', $useragents);
     $view->assign('hosts', $hosts);
     //var_dump($chart, $useragents, $ipaddresses, $ipversion, $raw); die();
 }
/**
 * @todo Finish documentation of smarty_function_widgetarea
 * @param array  $params  Associative (and/or indexed) array of smarty parameters passed in from the template
 * @param Smarty_Internal_Template $smarty  Parent Smarty template object
 *
 * @return string|void
 */
function smarty_function_widgetarea($params, $smarty) {
	// Get all widgets set to load in this area.

	$body     = '';
	$baseurl  = PageRequest::GetSystemRequest()->getBaseURL();
	$template = $smarty->template_resource;
	$tmpl     = $smarty->getTemplateVars('__core_template');
	$topview  = ($tmpl instanceof \Core\Templates\TemplateInterface) ? $tmpl->getView() : \Core\view();

	$parameters  = [];
	$name        = null;
	$installable = null;
	$assign      = null;
	foreach($params as $k => $v){
		switch($k){
			case 'name':
				$name = $v;
				break;
			case 'installable':
				$installable = $v;
				break;
			case 'assign':
				$assign = $v;
				break;
			default:
				$parameters[$k] = $v;
				break;
		}
	}

	// I need to resolve the page template down to the base version in order for the lookup to work.
	foreach(Core\Templates\Template::GetPaths() as $base){
		if(strpos($template, $base) === 0){
			$template = substr($template, strlen($base));
			break;
		}
	}

	// Given support for page-level widgets, this logic gets slightly more difficult...
	$factory = new ModelFactory('WidgetInstanceModel');
	$factory->order('weight');
	if(Core::IsComponentAvailable('multisite') && MultiSiteHelper::IsEnabled()){
		$factory->whereGroup('or', ['site = -1', 'site = ' . MultiSiteHelper::GetCurrentSiteID()]);
	}

	$subwhere = new Core\Datamodel\DatasetWhereClause();
	$subwhere->setSeparator('OR');

	// First, the skin-level where clause.
	$skinwhere = new Core\Datamodel\DatasetWhereClause();
	$skinwhere->setSeparator('AND');
	$skinwhere->addWhere('template = ' . $template);
	$skinwhere->addWhere('widgetarea = ' . $name);
	$subwhere->addWhere($skinwhere);

	// And second, the page-level where clause.
	if($baseurl){
		$pagewhere = new Core\Datamodel\DatasetWhereClause();
		$pagewhere->setSeparator('AND');
		$pagewhere->addWhere('page_baseurl = ' . $baseurl);
		$pagewhere->addWhere('widgetarea = ' . $name);
		$subwhere->addWhere($pagewhere);
	}

	$factory->where($subwhere);


	$widgetcount = 0;
	try{
		$widgets = $factory->get();
	}
	catch(Exception $e){
		if(DEVELOPMENT_MODE){
			$body .= '<p class="message-error">Exception while trying to load widget area ' . $name . '!</p>';
			$body .= '<pre class="xdebug-var-dump">' . $e->getMessage() . '</pre>';
		}
		else{
			\Core\ErrorManagement\exception_handler($e, false);
		}
		$widgets = [];
		++$widgetcount;
	}


	foreach ($widgets as $wi) {
		/** @var $wi WidgetInstanceModel */
		// User cannot access this widget? Don't display it...
		if(!\Core\user()){
			continue;
		}
		if (!\Core\user()->checkAccess($wi->get('access'))){
			continue;
		}

		if($installable){
			$wi->set('installable', $installable);
		}
		$view = $wi->execute($parameters);

		// Some widgets may return simply a blank string.  Those should just be ignored.
		if ($view == ''){
			continue;
		}

		// If it's just a string, return that.
		if (is_string($view)) {
			$contents = $view;
		}
		elseif($view->error == View::ERROR_NOERROR){
			// Ensure that the widget's View knows it's linked to a parent!
			$view->parent = $topview;

			$contents = $view->fetch();
		}
		else{
			$contents = 'Error displaying widget [' . $wi->get('baseurl') . '], returned error [' . $view->error . ']';
		}
		++$widgetcount;
		
		// Does this widget have controls attached to it?
		$widget = $wi->getWidget();
		if($widget->controls instanceof ViewControls && $widget->controls->hasLinks()){
			$contents = '<div class="widget-controls-wrapper">' .
				'<menu id="widget-controls-' . $wi->get('id') . '">' . 
				$widget->controls->fetch() . 
				'</menu>' . 
				'</div>' .
				$contents;
		}

		$body .= '<div class="widget">' . $contents . '</div>';
	}

	// Do some sanitizing for the css data
	$class = 'widgetarea-' . strtolower(str_replace(' ', '', $name));

	$html = '<div class="widgetarea ' . $class . '" widgetarea="' . $name . '">' . $body . '</div>';

	// No widgets, no inner content!
	if($widgetcount == 0){
		$html = '';
	}

	if($assign){
		$smarty->assign($assign, $html);
	}
	else{
		return $html;
	}
}
	/**
	 * 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);
	}