Elgg  Version 1.11
ViewsService.php
Go to the documentation of this file.
1 <?php
2 namespace Elgg;
3 
18 class ViewsService {
19 
20  protected $config_wrapper;
21  protected $site_url_wrapper;
22  protected $user_wrapper;
23  protected $user_wrapped;
24 
29  protected $file_exists_cache = array();
30 
36  private $CONFIG;
37 
41  private $hooks;
42 
46  private $logger;
47 
51  private $overriden_locations = array();
52 
59  public function __construct(\Elgg\PluginHooksService $hooks, \Elgg\Logger $logger) {
61  $this->CONFIG = $CONFIG;
62  $this->hooks = $hooks;
63  $this->logger = $logger;
64  }
65 
71  protected function getUserWrapper() {
72  $user = _elgg_services()->session->getLoggedInUser();
73  if ($user) {
74  if ($user !== $this->user_wrapped) {
75  $warning = 'Use elgg_get_logged_in_user_entity() rather than assuming elgg_view() '
76  . 'populates $vars["user"]';
77  $this->user_wrapper = new \Elgg\DeprecationWrapper($user, $warning, 1.8);
78  $this->user_wrapped = $user;
79  }
80  $user = $this->user_wrapper;
81  }
82  return $user;
83  }
84 
88  public function autoregisterViews($view_base, $folder, $base_location_path, $viewtype) {
89  $handle = opendir($folder);
90  if ($handle) {
91  while ($view = readdir($handle)) {
92  if (!empty($view_base)) {
93  $view_base_new = $view_base . "/";
94  } else {
95  $view_base_new = "";
96  }
97 
98  if (substr($view, 0, 1) !== '.') {
99  if (is_dir($folder . "/" . $view)) {
100  $this->autoregisterViews($view_base_new . $view, $folder . "/" . $view,
101  $base_location_path, $viewtype);
102  } else {
103  $this->setViewLocation($view_base_new . basename($view, '.php'),
104  $base_location_path, $viewtype);
105  }
106  }
107  }
108  return true;
109  }
110  return false;
111  }
112 
116  public function getViewLocation($view, $viewtype = '') {
117 
118 
119  if (empty($viewtype)) {
121  }
122 
123  if (!isset($this->CONFIG->views->locations[$viewtype][$view])) {
124  if (!isset($this->CONFIG->viewpath)) {
125  return dirname(dirname(dirname(__FILE__))) . "/views/";
126  } else {
127  return $this->CONFIG->viewpath;
128  }
129  } else {
130  return $this->CONFIG->views->locations[$viewtype][$view];
131  }
132  }
133 
137  public function setViewLocation($view, $location, $viewtype = '') {
138 
139 
140  if (empty($viewtype)) {
141  $viewtype = 'default';
142  }
143 
144  if (!isset($this->CONFIG->views)) {
145  $this->CONFIG->views = new \stdClass;
146  }
147 
148  if (!isset($this->CONFIG->views->locations)) {
149  $this->CONFIG->views->locations = array($viewtype => array($view => $location));
150 
151  } else if (!isset($this->CONFIG->views->locations[$viewtype])) {
152  $this->CONFIG->views->locations[$viewtype] = array($view => $location);
153 
154  } else {
155  if (isset($this->CONFIG->views->locations[$viewtype][$view])) {
156  $this->overriden_locations[$viewtype][$view][] = $this->CONFIG->views->locations[$viewtype][$view];
157  }
158 
159  $this->CONFIG->views->locations[$viewtype][$view] = $location;
160  }
161  }
162 
167 
168 
169  if (!isset($this->CONFIG->viewtype)) {
170  $this->CONFIG->viewtype = new \stdClass;
171  }
172 
173  if (!isset($this->CONFIG->viewtype->fallback)) {
174  $this->CONFIG->viewtype->fallback = array();
175  }
176 
177  $this->CONFIG->viewtype->fallback[] = $viewtype;
178  }
179 
183  public function doesViewtypeFallback($viewtype) {
184 
185 
186  if (isset($this->CONFIG->viewtype) && isset($this->CONFIG->viewtype->fallback)) {
187  return in_array($viewtype, $this->CONFIG->viewtype->fallback);
188  }
189 
190  return false;
191  }
192 
206  public function renderDeprecatedView($view, array $vars, $suggestion, $version) {
207  $rendered = $this->renderView($view, $vars, false, '', false);
208  if ($rendered) {
209  elgg_deprecated_notice("The $view view has been deprecated. $suggestion", $version, 3);
210  }
211  return $rendered;
212  }
213 
217  public function renderView($view, array $vars = array(), $bypass = false, $viewtype = '', $issue_missing_notice = true) {
218 
219 
220  if (!is_string($view) || !is_string($viewtype)) {
221  $this->logger->log("View and Viewtype in views must be a strings: $view", 'NOTICE');
222  return '';
223  }
224  // basic checking for bad paths
225  if (strpos($view, '..') !== false) {
226  return '';
227  }
228 
229  if (!is_array($vars)) {
230  $this->logger->log("Vars in views must be an array: $view", 'ERROR');
231  $vars = array();
232  }
233 
234  // Get the current viewtype
235  if ($viewtype === '' || !_elgg_is_valid_viewtype($viewtype)) {
237  }
238 
239  // allow altering $vars
240  $vars_hook_params = [
241  'view' => $view,
242  'vars' => $vars,
243  'viewtype' => $viewtype,
244  ];
245  $vars = $this->hooks->trigger('view_vars', $view, $vars_hook_params, $vars);
246 
247  $view_orig = $view;
248 
249  // Trigger the pagesetup event
250  if (!isset($this->CONFIG->pagesetupdone) && $this->CONFIG->boot_complete) {
251  $this->CONFIG->pagesetupdone = true;
252  _elgg_services()->events->trigger('pagesetup', 'system');
253  }
254 
255  // @warning - plugin authors: do not expect user, config, and url to be
256  // set by elgg_view() in the future. Instead, use elgg_get_logged_in_user_entity(),
257  // elgg_get_config(), and elgg_get_site_url() in your views.
258  if (!isset($vars['user'])) {
259  $vars['user'] = $this->getUserWrapper();
260  }
261  if (!isset($vars['config'])) {
262  if (!$this->config_wrapper) {
263  $warning = 'Do not rely on $vars["config"] or $CONFIG being available in views';
264  $this->config_wrapper = new \Elgg\DeprecationWrapper($this->CONFIG, $warning, 1.8);
265  }
266  $vars['config'] = $this->config_wrapper;
267  }
268  if (!isset($vars['url'])) {
269  if (!$this->site_url_wrapper) {
270  $warning = 'Do not rely on $vars["url"] being available in views';
271  $this->site_url_wrapper = new \Elgg\DeprecationWrapper(elgg_get_site_url(), $warning, 1.8);
272  }
273  $vars['url'] = $this->site_url_wrapper;
274  }
275 
276  // full_view is the new preferred key for full view on entities @see elgg_view_entity()
277  // check if full_view is set because that means we've already rewritten it and this is
278  // coming from another view passing $vars directly.
279  if (isset($vars['full']) && !isset($vars['full_view'])) {
280  elgg_deprecated_notice("Use \$vars['full_view'] instead of \$vars['full']", 1.8, 2);
281  $vars['full_view'] = $vars['full'];
282  }
283  if (isset($vars['full_view'])) {
284  $vars['full'] = $vars['full_view'];
285  }
286 
287  // internalname => name (1.8)
288  if (isset($vars['internalname']) && !isset($vars['__ignoreInternalname']) && !isset($vars['name'])) {
289  elgg_deprecated_notice('You should pass $vars[\'name\'] now instead of $vars[\'internalname\']', 1.8, 2);
290  $vars['name'] = $vars['internalname'];
291  } elseif (isset($vars['name'])) {
292  if (!isset($vars['internalname'])) {
293  $vars['__ignoreInternalname'] = '';
294  }
295  $vars['internalname'] = $vars['name'];
296  }
297 
298  // internalid => id (1.8)
299  if (isset($vars['internalid']) && !isset($vars['__ignoreInternalid']) && !isset($vars['name'])) {
300  elgg_deprecated_notice('You should pass $vars[\'id\'] now instead of $vars[\'internalid\']', 1.8, 2);
301  $vars['id'] = $vars['internalid'];
302  } elseif (isset($vars['id'])) {
303  if (!isset($vars['internalid'])) {
304  $vars['__ignoreInternalid'] = '';
305  }
306  $vars['internalid'] = $vars['id'];
307  }
308 
309  // If it's been requested, pass off to a template handler instead
310  if ($bypass == false && isset($this->CONFIG->template_handler) && !empty($this->CONFIG->template_handler)) {
311  $template_handler = $this->CONFIG->template_handler;
312  if (is_callable($template_handler)) {
313  return call_user_func($template_handler, $view, $vars);
314  }
315  }
316 
317  // Set up any extensions to the requested view
318  if (isset($this->CONFIG->views->extensions[$view])) {
319  $viewlist = $this->CONFIG->views->extensions[$view];
320  } else {
321  $viewlist = array(500 => $view);
322  }
323 
324  $content = '';
325  foreach ($viewlist as $view) {
326 
327  $rendering = $this->renderViewFile($view, $vars, $viewtype, $issue_missing_notice);
328  if ($rendering !== false) {
329  $content .= $rendering;
330  continue;
331  }
332 
333  // attempt to load default view
334  if ($viewtype !== 'default' && $this->doesViewtypeFallback($viewtype)) {
335 
336  $rendering = $this->renderViewFile($view, $vars, 'default', $issue_missing_notice);
337  if ($rendering !== false) {
338  $content .= $rendering;
339  }
340  }
341  }
342 
343  // Plugin hook
344  $params = array('view' => $view_orig, 'vars' => $vars, 'viewtype' => $viewtype);
345  $content = $this->hooks->trigger('view', $view_orig, $params, $content);
346 
347  // backward compatibility with less granular hook will be gone in 2.0
348  $content_tmp = $this->hooks->trigger('display', 'view', $params, $content);
349 
350  if ($content_tmp !== $content) {
351  $content = $content_tmp;
352  elgg_deprecated_notice('The display:view plugin hook is deprecated by view:view_name', 1.8);
353  }
354 
355  return $content;
356  }
357 
365  protected function fileExists($path) {
366  if (!isset($this->file_exists_cache[$path])) {
367  $this->file_exists_cache[$path] = file_exists($path);
368  }
369  return $this->file_exists_cache[$path];
370  }
371 
382  private function renderViewFile($view, array $vars, $viewtype, $issue_missing_notice) {
383  $view_location = $this->getViewLocation($view, $viewtype);
384 
385  // @warning - plugin authors: do not expect $CONFIG to be available in views
386  // in the future. Instead, use elgg_get_config() in your views.
387  // Note: this is intentionally a local var.
388  $CONFIG = $this->config_wrapper;
389 
390  if ($this->fileExists("{$view_location}$viewtype/$view.php")) {
391  ob_start();
392  include("{$view_location}$viewtype/$view.php");
393  return ob_get_clean();
394  } else if ($this->fileExists("{$view_location}$viewtype/$view")) {
395  return file_get_contents("{$view_location}$viewtype/$view");
396  } else {
397  if ($issue_missing_notice) {
398  $this->logger->log("$viewtype/$view view does not exist.", 'NOTICE');
399  }
400  return false;
401  }
402  }
403 
407  public function viewExists($view, $viewtype = '', $recurse = true) {
408 
409  if (empty($view) || !is_string($view)) {
410  return false;
411  }
412 
413  // Detect view type
414  if ($viewtype === '' || !_elgg_is_valid_viewtype($viewtype)) {
416  }
417 
418  if (!isset($this->CONFIG->views->locations[$viewtype][$view])) {
419  if (!isset($this->CONFIG->viewpath)) {
420  $location = dirname(dirname(dirname(__FILE__))) . "/views/";
421  } else {
422  $location = $this->CONFIG->viewpath;
423  }
424  } else {
425  $location = $this->CONFIG->views->locations[$viewtype][$view];
426  }
427 
428  if ($this->fileExists("{$location}$viewtype/$view.php") ||
429  $this->fileExists("{$location}$viewtype/$view")) {
430  return true;
431  }
432 
433  // If we got here then check whether this exists as an extension
434  // We optionally recursively check whether the extended view exists also for the viewtype
435  if ($recurse && isset($this->CONFIG->views->extensions[$view])) {
436  foreach ($this->CONFIG->views->extensions[$view] as $view_extension) {
437  // do not recursively check to stay away from infinite loops
438  if ($this->viewExists($view_extension, $viewtype, false)) {
439  return true;
440  }
441  }
442  }
443 
444  // Now check if the default view exists if the view is registered as a fallback
445  if ($viewtype != 'default' && $this->doesViewtypeFallback($viewtype)) {
446  return $this->viewExists($view, 'default');
447  }
448 
449  return false;
450 
451  }
452 
456  public function extendView($view, $view_extension, $priority = 501, $viewtype = '') {
457 
458 
459  if (!isset($this->CONFIG->views)) {
460  $this->CONFIG->views = (object) array(
461  'extensions' => array(),
462  );
463  $this->CONFIG->views->extensions[$view][500] = (string) $view;
464  } else {
465  if (!isset($this->CONFIG->views->extensions[$view])) {
466  $this->CONFIG->views->extensions[$view][500] = (string) $view;
467  }
468  }
469 
470  // raise priority until it doesn't match one already registered
471  while (isset($this->CONFIG->views->extensions[$view][$priority])) {
472  $priority++;
473  }
474 
475  $this->CONFIG->views->extensions[$view][$priority] = (string) $view_extension;
476  ksort($this->CONFIG->views->extensions[$view]);
477 
478  }
479 
483  public function unextendView($view, $view_extension) {
484 
485 
486  if (!isset($this->CONFIG->views)) {
487  return false;
488  }
489 
490  if (!isset($this->CONFIG->views->extensions)) {
491  return false;
492  }
493 
494  if (!isset($this->CONFIG->views->extensions[$view])) {
495  return false;
496  }
497 
498  $priority = array_search($view_extension, $this->CONFIG->views->extensions[$view]);
499  if ($priority === false) {
500  return false;
501  }
502 
503  unset($this->CONFIG->views->extensions[$view][$priority]);
504 
505  return true;
506  }
507 
511  public function registerCacheableView($view) {
512 
513 
514  if (!isset($this->CONFIG->views)) {
515  $this->CONFIG->views = new \stdClass;
516  }
517 
518  if (!isset($this->CONFIG->views->simplecache)) {
519  $this->CONFIG->views->simplecache = array();
520  }
521 
522  $this->CONFIG->views->simplecache[$view] = true;
523  }
524 
528  public function isCacheableView($view) {
529 
530 
531  if (!isset($this->CONFIG->views)) {
532  $this->CONFIG->views = new \stdClass;
533  }
534 
535  if (!isset($this->CONFIG->views->simplecache)) {
536  $this->CONFIG->views->simplecache = array();
537  }
538 
539  if (isset($this->CONFIG->views->simplecache[$view])) {
540  return true;
541  } else {
542  $currentViewtype = elgg_get_viewtype();
543  $viewtypes = array($currentViewtype);
544 
545  if ($this->doesViewtypeFallback($currentViewtype) && $currentViewtype != 'default') {
546  $viewtypes[] = 'defaut';
547  }
548 
549  // If a static view file is found in any viewtype, it's considered cacheable
550  foreach ($viewtypes as $viewtype) {
551  $view_file = $this->getViewLocation($view, $viewtype) . "$viewtype/$view";
552  if ($this->fileExists($view_file)) {
553  return true;
554  }
555  }
556 
557  // Assume not-cacheable by default
558  return false;
559  }
560  }
561 
571  public function registerPluginViews($path, &$failed_dir = '') {
572  $view_dir = "$path/views/";
573 
574  // plugins don't have to have views.
575  if (!is_dir($view_dir)) {
576  return true;
577  }
578 
579  // but if they do, they have to be readable
580  $handle = opendir($view_dir);
581  if (!$handle) {
582  $failed_dir = $view_dir;
583  return false;
584  }
585 
586  while (false !== ($view_type = readdir($handle))) {
587  $view_type_dir = $view_dir . $view_type;
588 
589  if ('.' !== substr($view_type, 0, 1) && is_dir($view_type_dir)) {
590  if ($this->autoregisterViews('', $view_type_dir, $view_dir, $view_type)) {
591  elgg_register_viewtype($view_type);
592  } else {
593  $failed_dir = $view_type_dir;
594  return false;
595  }
596  }
597  }
598 
599  return true;
600  }
601 
609  public function getOverriddenLocations() {
610  return $this->overriden_locations;
611  }
612 
621  public function setOverriddenLocations(array $locations) {
622  $this->overriden_locations = $locations;
623  }
624 }
$view
Definition: crop.php:68
autoregisterViews($view_base, $folder, $base_location_path, $viewtype)
private
isCacheableView($view)
private
_elgg_is_valid_viewtype($viewtype)
Checks if $viewtype is a string suitable for use as a viewtype name.
Definition: views.php:158
registerCacheableView($view)
private
doesViewtypeFallback($viewtype)
private
renderView($view, array $vars=array(), $bypass=false, $viewtype= '', $issue_missing_notice=true)
private
__construct(\Elgg\PluginHooksService $hooks,\Elgg\Logger $logger)
Constructor.
registerViewtypeFallback($viewtype)
private
viewExists($view, $viewtype= '', $recurse=true)
private
getUserWrapper()
Get the user object in a wrapper.
$params
Definition: login.php:72
getViewLocation($view, $viewtype= '')
private
Save menu items.
elgg_get_viewtype()
Return the current view type.
Definition: views.php:91
registerPluginViews($path, &$failed_dir= '')
Register a plugin&#39;s views.
_elgg_services()
Definition: autoloader.php:14
global $CONFIG
$user
Definition: ban.php:13
getOverriddenLocations()
Get views overridden by setViewLocation() calls.
extendView($view, $view_extension, $priority=501, $viewtype= '')
private
elgg_deprecated_notice($msg, $dep_version, $backtrace_level=1)
Log a notice about deprecated use of a function, view, etc.
Definition: elgglib.php:1006
elgg global
Pointer to the global context.
Definition: elgglib.js:12
elgg_get_site_url($site_guid=0)
Get the URL for the current (or specified) site.
setViewLocation($view, $location, $viewtype= '')
private
fileExists($path)
Wrapper for file_exists() that caches false results (the stat cache only caches true results)...
$content
Set robots.txt action.
Definition: set_robots.php:6
unextendView($view, $view_extension)
private
renderDeprecatedView($view, array $vars, $suggestion, $version)
Display a view with a deprecation notice.
$version
Definition: version.php:14
$path
Definition: invalid.php:17
setOverriddenLocations(array $locations)
Set views overridden by setViewLocation() calls.
$viewtype
Definition: start.php:76
$priority
if(file_exists($welcome)) $vars
Definition: upgrade.php:93
elgg_register_viewtype($viewtype)
Register a viewtype.
Definition: views.php:116