Skip to content

Commit

Permalink
feature(views): Allow specifying exact view paths via views.php
Browse files Browse the repository at this point in the history
We use this feature to bring vendor(s) files into the views system.
This also loads jQuery UI completely asynchronously.

Fixes Elgg#6844
Fixes Elgg#8515
  • Loading branch information
mrclay committed Jun 18, 2015
1 parent 23e0d73 commit 8b4b0cc
Show file tree
Hide file tree
Showing 24 changed files with 251 additions and 150 deletions.
7 changes: 7 additions & 0 deletions engine/classes/Elgg/Cache/SystemCache.php
Expand Up @@ -151,6 +151,12 @@ function loadAll() {
}
$this->CONFIG->view_types = unserialize($data);

$data = $this->load('view_files');
if (!is_string($data)) {
return;
}
_elgg_services()->views->setFiles(unserialize($data));

// Note: We don't need view_overrides for operation. Inspector can pull this from the cache

$this->CONFIG->system_cache_loaded = true;
Expand All @@ -171,6 +177,7 @@ function init() {
if (!$this->CONFIG->system_cache_loaded) {
$this->save('view_locations', serialize($this->CONFIG->views->locations));
$this->save('view_types', serialize($this->CONFIG->view_types));
$this->save('view_files', serialize(_elgg_services()->views->getFiles()));

// this is saved just for the inspector and is not loaded in loadAll()
$this->save('view_overrides', serialize(_elgg_services()->views->getOverriddenLocations()));
Expand Down
202 changes: 132 additions & 70 deletions engine/classes/Elgg/ViewsService.php
Expand Up @@ -20,6 +20,11 @@ class ViewsService {
*/
protected $file_exists_cache = array();

/**
* @var array [viewtype][view] => file
*/
private $files = [];

/**
* Global Elgg configuration
*
Expand All @@ -40,7 +45,7 @@ class ViewsService {
/**
* @var array
*/
private $overriden_locations = array();
private $overridden_dirs = array();

/**
* Constructor
Expand All @@ -53,6 +58,25 @@ public function __construct(\Elgg\PluginHooksService $hooks, \Elgg\Logger $logge
$this->CONFIG = $CONFIG;
$this->hooks = $hooks;
$this->logger = $logger;

if (!isset($this->CONFIG->views)) {
$this->CONFIG->views = (object)[];
}
if (!isset($this->CONFIG->views->locations)) {
$this->CONFIG->views->locations = [];
}
if (!isset($this->CONFIG->views->extensions)) {
$this->CONFIG->views->extensions = [];
}
if (!isset($this->CONFIG->viewtype)) {
$this->CONFIG->viewtype = (object)[];
}
if (!isset($this->CONFIG->viewtype->fallback)) {
$this->CONFIG->viewtype->fallback = [];
}
if (!isset($this->CONFIG->views->simplecache)) {
$this->CONFIG->views->simplecache = [];
}
}

/**
Expand All @@ -73,7 +97,7 @@ public function autoregisterViews($view_base, $folder, $base_location_path, $vie
$this->autoregisterViews($view_base_new . $view, $folder . "/" . $view,
$base_location_path, $viewtype);
} else {
$this->setViewLocation($view_base_new . basename($view, '.php'),
$this->setViewDir($view_base_new . basename($view, '.php'),
$base_location_path, $viewtype);
}
}
Expand All @@ -86,7 +110,7 @@ public function autoregisterViews($view_base, $folder, $base_location_path, $vie
/**
* @access private
*/
public function getViewLocation($view, $viewtype = '') {
public function getViewDir($view, $viewtype = '') {


if (empty($viewtype)) {
Expand All @@ -107,26 +131,21 @@ public function getViewLocation($view, $viewtype = '') {
/**
* @access private
*/
public function setViewLocation($view, $location, $viewtype = '') {
public function setViewDir($view, $location, $viewtype = '') {


if (empty($viewtype)) {
$viewtype = 'default';
}

if (!isset($this->CONFIG->views)) {
$this->CONFIG->views = new \stdClass;
}

if (!isset($this->CONFIG->views->locations)) {
$this->CONFIG->views->locations = array($viewtype => array($view => $location));
unset($this->files[$viewtype][$view]);

} else if (!isset($this->CONFIG->views->locations[$viewtype])) {
if (!isset($this->CONFIG->views->locations[$viewtype])) {
$this->CONFIG->views->locations[$viewtype] = array($view => $location);

} else {
if (isset($this->CONFIG->views->locations[$viewtype][$view])) {
$this->overriden_locations[$viewtype][$view][] = $this->CONFIG->views->locations[$viewtype][$view];
$this->overridden_dirs[$viewtype][$view][] = $this->CONFIG->views->locations[$viewtype][$view];
}

$this->CONFIG->views->locations[$viewtype][$view] = $location;
Expand All @@ -137,30 +156,14 @@ public function setViewLocation($view, $location, $viewtype = '') {
* @access private
*/
public function registerViewtypeFallback($viewtype) {


if (!isset($this->CONFIG->viewtype)) {
$this->CONFIG->viewtype = new \stdClass;
}

if (!isset($this->CONFIG->viewtype->fallback)) {
$this->CONFIG->viewtype->fallback = array();
}

$this->CONFIG->viewtype->fallback[] = $viewtype;
}

/**
* @access private
*/
public function doesViewtypeFallback($viewtype) {


if (isset($this->CONFIG->viewtype) && isset($this->CONFIG->viewtype->fallback)) {
return in_array($viewtype, $this->CONFIG->viewtype->fallback);
}

return false;
return in_array($viewtype, $this->CONFIG->viewtype->fallback);
}

/**
Expand Down Expand Up @@ -291,20 +294,37 @@ protected function fileExists($path) {
* @return string|false output generated by view file inclusion or false
*/
private function renderViewFile($view, array $vars, $viewtype, $issue_missing_notice) {
$view_location = $this->getViewLocation($view, $viewtype);

if ($this->fileExists("{$view_location}$viewtype/$view.php")) {
ob_start();
include("{$view_location}$viewtype/$view.php");
return ob_get_clean();
} else if ($this->fileExists("{$view_location}$viewtype/$view")) {
return file_get_contents("{$view_location}$viewtype/$view");
if (isset($this->files[$viewtype][$view])) {
$file = $this->files[$viewtype][$view];
$exists = $this->fileExists($file);
} else {
$views_dir = $this->getViewDir($view, $viewtype);
if ($this->fileExists("{$views_dir}$viewtype/$view.php")) {
$file = "{$views_dir}$viewtype/$view.php";
$exists = true;
} elseif ($this->fileExists("{$views_dir}$viewtype/$view")) {
$file = "{$views_dir}$viewtype/$view";
$exists = true;
} else {
$file = null;
$exists = false;
}
}

if (!$exists) {
if ($issue_missing_notice) {
$this->logger->log("$viewtype/$view view does not exist.", 'NOTICE');
}
return false;
}

if (pathinfo($file, PATHINFO_EXTENSION) === 'php') {
ob_start();
include $file;
return ob_get_clean();
}

return file_get_contents($file);
}

/**
Expand All @@ -321,6 +341,10 @@ public function viewExists($view, $viewtype = '', $recurse = true) {
$viewtype = elgg_get_viewtype();
}

if (isset($this->files[$viewtype][$view])) {
return $this->fileExists($this->files[$viewtype][$view]);
}

if (!isset($this->CONFIG->views->locations[$viewtype][$view])) {
if (!isset($this->CONFIG->viewpath)) {
$location = dirname(dirname(dirname(__FILE__))) . "/views/";
Expand Down Expand Up @@ -359,7 +383,7 @@ public function viewExists($view, $viewtype = '', $recurse = true) {
/**
* @access private
*/
public function extendView($view, $view_extension, $priority = 501, $viewtype = '') {
public function extendView($view, $view_extension, $priority = 501) {


if (!isset($this->CONFIG->views)) {
Expand Down Expand Up @@ -415,52 +439,41 @@ public function unextendView($view, $view_extension) {
* @access private
*/
public function registerCacheableView($view) {


if (!isset($this->CONFIG->views)) {
$this->CONFIG->views = new \stdClass;
}

if (!isset($this->CONFIG->views->simplecache)) {
$this->CONFIG->views->simplecache = array();
}

$this->CONFIG->views->simplecache[$view] = true;
}

/**
* @access private
*/
public function isCacheableView($view) {
if (!isset($this->CONFIG->views)) {
$this->CONFIG->views = new \stdClass;
}

if (!isset($this->CONFIG->views->simplecache)) {
$this->CONFIG->views->simplecache = array();
}

if (isset($this->CONFIG->views->simplecache[$view])) {
return true;
} else {
$currentViewtype = elgg_get_viewtype();
$viewtypes = array($currentViewtype);
}

if ($this->doesViewtypeFallback($currentViewtype) && $currentViewtype != 'default') {
$viewtypes[] = 'defaut';
}
$currentViewtype = elgg_get_viewtype();
$viewtypes = array($currentViewtype);

// If a static view file is found in any viewtype, it's considered cacheable
foreach ($viewtypes as $viewtype) {
$view_file = $this->getViewLocation($view, $viewtype) . "$viewtype/$view";
if ($this->fileExists($view_file)) {
if ($this->doesViewtypeFallback($currentViewtype) && $currentViewtype != 'default') {
$viewtypes[] = 'default';
}

// If a static view file is found in any viewtype, it's considered cacheable
foreach ($viewtypes as $viewtype) {
if (isset($this->files[$viewtype][$view])) {
$file = $this->files[$viewtype][$view];
if (pathinfo($file, PATHINFO_EXTENSION) !== 'php' && $this->fileExists($file)) {
return true;
}
}

// Assume not-cacheable by default
return false;
$view_file = $this->getViewDir($view, $viewtype) . "$viewtype/$view";
if ($this->fileExists($view_file)) {
return true;
}
}

// Assume not-cacheable by default
return false;
}

/**
Expand Down Expand Up @@ -503,6 +516,34 @@ public function registerPluginViews($path, &$failed_dir = '') {
return true;
}

/**
* Merge a specification of absolute view paths
*
* @param array $spec Specification
* viewtype => [
* view_name => path
* ]
*
* @access private
*/
public function mergeViewsSpec(array $spec) {
foreach ($spec as $viewtype => $list) {
foreach ($list as $view => $path) {
if (substr($view, -1) === '/') {
// prefixes. not supported yet
} else {
if (preg_match('~^([/\\\\]|[a-zA-Z]\:)~', $view)) {
// absolute path
} else {
// relative path
$path = elgg_get_root_path() . $path;
}
$this->files[$viewtype][$view] = $path;
}
}
}
}

/**
* Get views overridden by setViewLocation() calls.
*
Expand All @@ -511,7 +552,7 @@ public function registerPluginViews($path, &$failed_dir = '') {
* @access private
*/
public function getOverriddenLocations() {
return $this->overriden_locations;
return $this->overridden_dirs;
}

/**
Expand All @@ -523,6 +564,27 @@ public function getOverriddenLocations() {
* @access private
*/
public function setOverriddenLocations(array $locations) {
$this->overriden_locations = $locations;
$this->overridden_dirs = $locations;
}

/**
* Export file locations for caching
*
* @return array
* @access private
*/
public function getFiles() {
return $this->files;
}

/**
* Import file locations for caching
*
* @param array $files File paths for views
* @return void
* @access private
*/
public function setFiles(array $files) {
$this->files = $files;
}
}
14 changes: 13 additions & 1 deletion engine/classes/ElggPlugin.php
Expand Up @@ -814,7 +814,19 @@ protected function canReadFile($filename) {
* @return void
*/
protected function registerViews() {
if (!_elgg_services()->views->registerPluginViews($this->path, $failed_dir)) {
$views = _elgg_services()->views;

// Declared views first
$file = "{$this->path}/views.php";
if (is_file($file)) {
$spec = (include $file);
if (is_array($spec)) {
$views->mergeViewsSpec($spec);
}
}

// Allow /views directory files to override
if (!$views->registerPluginViews($this->path, $failed_dir)) {
$msg = _elgg_services()->translator->translate('ElggPlugin:Exception:CannotRegisterViews',
array($this->getID(), $this->guid, $failed_dir));
throw new \PluginException($msg);
Expand Down

0 comments on commit 8b4b0cc

Please sign in to comment.