123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625 |
- <?php
- namespace Elgg;
- /**
- * WARNING: API IN FLUX. DO NOT USE DIRECTLY.
- *
- * Use the elgg_* versions instead.
- *
- * @todo 1.10 remove deprecated view injections
- * @todo inject/remove dependencies: $CONFIG, hooks, site_url
- *
- * @access private
- *
- * @package Elgg.Core
- * @subpackage Views
- * @since 1.9.0
- */
- class ViewsService {
- protected $config_wrapper;
- protected $site_url_wrapper;
- protected $user_wrapper;
- protected $user_wrapped;
- /**
- * @see \Elgg\ViewsService::fileExists
- * @var array
- */
- protected $file_exists_cache = array();
- /**
- * Global Elgg configuration
- *
- * @var \stdClass
- */
- private $CONFIG;
- /**
- * @var PluginHooksService
- */
- private $hooks;
- /**
- * @var Logger
- */
- private $logger;
- /**
- * @var array
- */
- private $overriden_locations = array();
- /**
- * Constructor
- *
- * @param \Elgg\PluginHooksService $hooks The hooks service
- * @param \Elgg\Logger $logger Logger
- */
- public function __construct(\Elgg\PluginHooksService $hooks, \Elgg\Logger $logger) {
- global $CONFIG;
- $this->CONFIG = $CONFIG;
- $this->hooks = $hooks;
- $this->logger = $logger;
- }
- /**
- * Get the user object in a wrapper
- *
- * @return \Elgg\DeprecationWrapper|null
- */
- protected function getUserWrapper() {
- $user = _elgg_services()->session->getLoggedInUser();
- if ($user) {
- if ($user !== $this->user_wrapped) {
- $warning = 'Use elgg_get_logged_in_user_entity() rather than assuming elgg_view() '
- . 'populates $vars["user"]';
- $this->user_wrapper = new \Elgg\DeprecationWrapper($user, $warning, 1.8);
- $this->user_wrapped = $user;
- }
- $user = $this->user_wrapper;
- }
- return $user;
- }
- /**
- * @access private
- */
- public function autoregisterViews($view_base, $folder, $base_location_path, $viewtype) {
- $handle = opendir($folder);
- if ($handle) {
- while ($view = readdir($handle)) {
- if (!empty($view_base)) {
- $view_base_new = $view_base . "/";
- } else {
- $view_base_new = "";
- }
- if (substr($view, 0, 1) !== '.') {
- if (is_dir($folder . "/" . $view)) {
- $this->autoregisterViews($view_base_new . $view, $folder . "/" . $view,
- $base_location_path, $viewtype);
- } else {
- $this->setViewLocation($view_base_new . basename($view, '.php'),
- $base_location_path, $viewtype);
- }
- }
- }
- return true;
- }
- return false;
- }
- /**
- * @access private
- */
- public function getViewLocation($view, $viewtype = '') {
-
- if (empty($viewtype)) {
- $viewtype = elgg_get_viewtype();
- }
- if (!isset($this->CONFIG->views->locations[$viewtype][$view])) {
- if (!isset($this->CONFIG->viewpath)) {
- return dirname(dirname(dirname(__FILE__))) . "/views/";
- } else {
- return $this->CONFIG->viewpath;
- }
- } else {
- return $this->CONFIG->views->locations[$viewtype][$view];
- }
- }
- /**
- * @access private
- */
- public function setViewLocation($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));
- } else 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->CONFIG->views->locations[$viewtype][$view] = $location;
- }
- }
- /**
- * @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;
- }
- /**
- * Display a view with a deprecation notice. No missing view NOTICE is logged
- *
- * @see elgg_view()
- *
- * @param string $view The name and location of the view to use
- * @param array $vars Variables to pass to the view
- * @param string $suggestion Suggestion with the deprecation message
- * @param string $version Human-readable *release* version: 1.7, 1.8, ...
- *
- * @return string The parsed view
- * @access private
- */
- public function renderDeprecatedView($view, array $vars, $suggestion, $version) {
- $rendered = $this->renderView($view, $vars, false, '', false);
- if ($rendered) {
- elgg_deprecated_notice("The $view view has been deprecated. $suggestion", $version, 3);
- }
- return $rendered;
- }
- /**
- * @access private
- */
- public function renderView($view, array $vars = array(), $bypass = false, $viewtype = '', $issue_missing_notice = true) {
-
- if (!is_string($view) || !is_string($viewtype)) {
- $this->logger->log("View and Viewtype in views must be a strings: $view", 'NOTICE');
- return '';
- }
- // basic checking for bad paths
- if (strpos($view, '..') !== false) {
- return '';
- }
- if (!is_array($vars)) {
- $this->logger->log("Vars in views must be an array: $view", 'ERROR');
- $vars = array();
- }
- // Get the current viewtype
- if ($viewtype === '' || !_elgg_is_valid_viewtype($viewtype)) {
- $viewtype = elgg_get_viewtype();
- }
- // allow altering $vars
- $vars_hook_params = [
- 'view' => $view,
- 'vars' => $vars,
- 'viewtype' => $viewtype,
- ];
- $vars = $this->hooks->trigger('view_vars', $view, $vars_hook_params, $vars);
- $view_orig = $view;
- // Trigger the pagesetup event
- if (!isset($this->CONFIG->pagesetupdone) && $this->CONFIG->boot_complete) {
- $this->CONFIG->pagesetupdone = true;
- _elgg_services()->events->trigger('pagesetup', 'system');
- }
- // @warning - plugin authors: do not expect user, config, and url to be
- // set by elgg_view() in the future. Instead, use elgg_get_logged_in_user_entity(),
- // elgg_get_config(), and elgg_get_site_url() in your views.
- if (!isset($vars['user'])) {
- $vars['user'] = $this->getUserWrapper();
- }
- if (!isset($vars['config'])) {
- if (!$this->config_wrapper) {
- $warning = 'Do not rely on $vars["config"] or $CONFIG being available in views';
- $this->config_wrapper = new \Elgg\DeprecationWrapper($this->CONFIG, $warning, 1.8);
- }
- $vars['config'] = $this->config_wrapper;
- }
- if (!isset($vars['url'])) {
- if (!$this->site_url_wrapper) {
- $warning = 'Do not rely on $vars["url"] being available in views';
- $this->site_url_wrapper = new \Elgg\DeprecationWrapper(elgg_get_site_url(), $warning, 1.8);
- }
- $vars['url'] = $this->site_url_wrapper;
- }
- // full_view is the new preferred key for full view on entities @see elgg_view_entity()
- // check if full_view is set because that means we've already rewritten it and this is
- // coming from another view passing $vars directly.
- if (isset($vars['full']) && !isset($vars['full_view'])) {
- elgg_deprecated_notice("Use \$vars['full_view'] instead of \$vars['full']", 1.8, 2);
- $vars['full_view'] = $vars['full'];
- }
- if (isset($vars['full_view'])) {
- $vars['full'] = $vars['full_view'];
- }
- // internalname => name (1.8)
- if (isset($vars['internalname']) && !isset($vars['__ignoreInternalname']) && !isset($vars['name'])) {
- elgg_deprecated_notice('You should pass $vars[\'name\'] now instead of $vars[\'internalname\']', 1.8, 2);
- $vars['name'] = $vars['internalname'];
- } elseif (isset($vars['name'])) {
- if (!isset($vars['internalname'])) {
- $vars['__ignoreInternalname'] = '';
- }
- $vars['internalname'] = $vars['name'];
- }
- // internalid => id (1.8)
- if (isset($vars['internalid']) && !isset($vars['__ignoreInternalid']) && !isset($vars['name'])) {
- elgg_deprecated_notice('You should pass $vars[\'id\'] now instead of $vars[\'internalid\']', 1.8, 2);
- $vars['id'] = $vars['internalid'];
- } elseif (isset($vars['id'])) {
- if (!isset($vars['internalid'])) {
- $vars['__ignoreInternalid'] = '';
- }
- $vars['internalid'] = $vars['id'];
- }
- // If it's been requested, pass off to a template handler instead
- if ($bypass == false && isset($this->CONFIG->template_handler) && !empty($this->CONFIG->template_handler)) {
- $template_handler = $this->CONFIG->template_handler;
- if (is_callable($template_handler)) {
- return call_user_func($template_handler, $view, $vars);
- }
- }
- // Set up any extensions to the requested view
- if (isset($this->CONFIG->views->extensions[$view])) {
- $viewlist = $this->CONFIG->views->extensions[$view];
- } else {
- $viewlist = array(500 => $view);
- }
- $content = '';
- foreach ($viewlist as $view) {
- $rendering = $this->renderViewFile($view, $vars, $viewtype, $issue_missing_notice);
- if ($rendering !== false) {
- $content .= $rendering;
- continue;
- }
- // attempt to load default view
- if ($viewtype !== 'default' && $this->doesViewtypeFallback($viewtype)) {
- $rendering = $this->renderViewFile($view, $vars, 'default', $issue_missing_notice);
- if ($rendering !== false) {
- $content .= $rendering;
- }
- }
- }
- // Plugin hook
- $params = array('view' => $view_orig, 'vars' => $vars, 'viewtype' => $viewtype);
- $content = $this->hooks->trigger('view', $view_orig, $params, $content);
- // backward compatibility with less granular hook will be gone in 2.0
- $content_tmp = $this->hooks->trigger('display', 'view', $params, $content);
- if ($content_tmp !== $content) {
- $content = $content_tmp;
- elgg_deprecated_notice('The display:view plugin hook is deprecated by view:view_name', 1.8);
- }
- return $content;
- }
- /**
- * Wrapper for file_exists() that caches false results (the stat cache only caches true results).
- * This saves us from many unneeded file stat calls when a common view uses a fallback.
- *
- * @param string $path Path to the file
- * @return bool
- */
- protected function fileExists($path) {
- if (!isset($this->file_exists_cache[$path])) {
- $this->file_exists_cache[$path] = file_exists($path);
- }
- return $this->file_exists_cache[$path];
- }
- /**
- * Includes view PHP or static file
- *
- * @param string $view The view name
- * @param array $vars Variables passed to view
- * @param string $viewtype The viewtype
- * @param bool $issue_missing_notice Log a notice if the view is missing
- *
- * @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);
- // @warning - plugin authors: do not expect $CONFIG to be available in views
- // in the future. Instead, use elgg_get_config() in your views.
- // Note: this is intentionally a local var.
- $CONFIG = $this->config_wrapper;
- 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");
- } else {
- if ($issue_missing_notice) {
- $this->logger->log("$viewtype/$view view does not exist.", 'NOTICE');
- }
- return false;
- }
- }
- /**
- * @access private
- */
- public function viewExists($view, $viewtype = '', $recurse = true) {
-
- if (empty($view) || !is_string($view)) {
- return false;
- }
-
- // Detect view type
- if ($viewtype === '' || !_elgg_is_valid_viewtype($viewtype)) {
- $viewtype = elgg_get_viewtype();
- }
- if (!isset($this->CONFIG->views->locations[$viewtype][$view])) {
- if (!isset($this->CONFIG->viewpath)) {
- $location = dirname(dirname(dirname(__FILE__))) . "/views/";
- } else {
- $location = $this->CONFIG->viewpath;
- }
- } else {
- $location = $this->CONFIG->views->locations[$viewtype][$view];
- }
- if ($this->fileExists("{$location}$viewtype/$view.php") ||
- $this->fileExists("{$location}$viewtype/$view")) {
- return true;
- }
- // If we got here then check whether this exists as an extension
- // We optionally recursively check whether the extended view exists also for the viewtype
- if ($recurse && isset($this->CONFIG->views->extensions[$view])) {
- foreach ($this->CONFIG->views->extensions[$view] as $view_extension) {
- // do not recursively check to stay away from infinite loops
- if ($this->viewExists($view_extension, $viewtype, false)) {
- return true;
- }
- }
- }
- // Now check if the default view exists if the view is registered as a fallback
- if ($viewtype != 'default' && $this->doesViewtypeFallback($viewtype)) {
- return $this->viewExists($view, 'default');
- }
- return false;
- }
- /**
- * @access private
- */
- public function extendView($view, $view_extension, $priority = 501, $viewtype = '') {
-
- if (!isset($this->CONFIG->views)) {
- $this->CONFIG->views = (object) array(
- 'extensions' => array(),
- );
- $this->CONFIG->views->extensions[$view][500] = (string) $view;
- } else {
- if (!isset($this->CONFIG->views->extensions[$view])) {
- $this->CONFIG->views->extensions[$view][500] = (string) $view;
- }
- }
- // raise priority until it doesn't match one already registered
- while (isset($this->CONFIG->views->extensions[$view][$priority])) {
- $priority++;
- }
- $this->CONFIG->views->extensions[$view][$priority] = (string) $view_extension;
- ksort($this->CONFIG->views->extensions[$view]);
- }
- /**
- * @access private
- */
- public function unextendView($view, $view_extension) {
-
- if (!isset($this->CONFIG->views)) {
- return false;
- }
- if (!isset($this->CONFIG->views->extensions)) {
- return false;
- }
- if (!isset($this->CONFIG->views->extensions[$view])) {
- return false;
- }
- $priority = array_search($view_extension, $this->CONFIG->views->extensions[$view]);
- if ($priority === false) {
- return false;
- }
- unset($this->CONFIG->views->extensions[$view][$priority]);
- return true;
- }
- /**
- * @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';
- }
- // 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)) {
- return true;
- }
- }
- // Assume not-cacheable by default
- return false;
- }
- }
- /**
- * Register a plugin's views
- *
- * @param string $path Base path of the plugin
- * @param string $failed_dir This var is set to the failed directory if registration fails
- * @return bool
- *
- * @access private
- */
- public function registerPluginViews($path, &$failed_dir = '') {
- $view_dir = "$path/views/";
- // plugins don't have to have views.
- if (!is_dir($view_dir)) {
- return true;
- }
- // but if they do, they have to be readable
- $handle = opendir($view_dir);
- if (!$handle) {
- $failed_dir = $view_dir;
- return false;
- }
- while (false !== ($view_type = readdir($handle))) {
- $view_type_dir = $view_dir . $view_type;
- if ('.' !== substr($view_type, 0, 1) && is_dir($view_type_dir)) {
- if ($this->autoregisterViews('', $view_type_dir, $view_dir, $view_type)) {
- elgg_register_viewtype($view_type);
- } else {
- $failed_dir = $view_type_dir;
- return false;
- }
- }
- }
- return true;
- }
- /**
- * Get views overridden by setViewLocation() calls.
- *
- * @return array
- *
- * @access private
- */
- public function getOverriddenLocations() {
- return $this->overriden_locations;
- }
- /**
- * Set views overridden by setViewLocation() calls.
- *
- * @param array $locations
- * @return void
- *
- * @access private
- */
- public function setOverriddenLocations(array $locations) {
- $this->overriden_locations = $locations;
- }
- }
|