Elgg  Version 1.10
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 
44  public function __construct(\Elgg\PluginHooksService $hooks, \Elgg\Logger $logger) {
46  $this->CONFIG = $CONFIG;
47  $this->hooks = $hooks;
48  $this->logger = $logger;
49  }
50 
56  protected function getUserWrapper() {
57  $user = _elgg_services()->session->getLoggedInUser();
58  if ($user) {
59  if ($user !== $this->user_wrapped) {
60  $warning = 'Use elgg_get_logged_in_user_entity() rather than assuming elgg_view() '
61  . 'populates $vars["user"]';
62  $this->user_wrapper = new \Elgg\DeprecationWrapper($user, $warning, 1.8);
63  }
64  $user = $this->user_wrapper;
65  }
66  return $user;
67  }
68 
72  public function autoregisterViews($view_base, $folder, $base_location_path, $viewtype) {
73  $handle = opendir($folder);
74  if ($handle) {
75  while ($view = readdir($handle)) {
76  if (!empty($view_base)) {
77  $view_base_new = $view_base . "/";
78  } else {
79  $view_base_new = "";
80  }
81 
82  if (substr($view, 0, 1) !== '.') {
83  if (is_dir($folder . "/" . $view)) {
84  $this->autoregisterViews($view_base_new . $view, $folder . "/" . $view,
85  $base_location_path, $viewtype);
86  } else {
87  $this->setViewLocation($view_base_new . basename($view, '.php'),
88  $base_location_path, $viewtype);
89  }
90  }
91  }
92  return true;
93  }
94  return false;
95  }
96 
100  public function getViewLocation($view, $viewtype = '') {
101 
102 
103  if (empty($viewtype)) {
105  }
106 
107  if (!isset($this->CONFIG->views->locations[$viewtype][$view])) {
108  if (!isset($this->CONFIG->viewpath)) {
109  return dirname(dirname(dirname(__FILE__))) . "/views/";
110  } else {
111  return $this->CONFIG->viewpath;
112  }
113  } else {
114  return $this->CONFIG->views->locations[$viewtype][$view];
115  }
116  }
117 
121  public function setViewLocation($view, $location, $viewtype = '') {
122 
123 
124  if (empty($viewtype)) {
125  $viewtype = 'default';
126  }
127 
128  if (!isset($this->CONFIG->views)) {
129  $this->CONFIG->views = new \stdClass;
130  }
131 
132  if (!isset($this->CONFIG->views->locations)) {
133  $this->CONFIG->views->locations = array($viewtype => array($view => $location));
134 
135  } else if (!isset($this->CONFIG->views->locations[$viewtype])) {
136  $this->CONFIG->views->locations[$viewtype] = array($view => $location);
137 
138  } else {
139  $this->CONFIG->views->locations[$viewtype][$view] = $location;
140  }
141  }
142 
147 
148 
149  if (!isset($this->CONFIG->viewtype)) {
150  $this->CONFIG->viewtype = new \stdClass;
151  }
152 
153  if (!isset($this->CONFIG->viewtype->fallback)) {
154  $this->CONFIG->viewtype->fallback = array();
155  }
156 
157  $this->CONFIG->viewtype->fallback[] = $viewtype;
158  }
159 
163  public function doesViewtypeFallback($viewtype) {
164 
165 
166  if (isset($this->CONFIG->viewtype) && isset($this->CONFIG->viewtype->fallback)) {
167  return in_array($viewtype, $this->CONFIG->viewtype->fallback);
168  }
169 
170  return false;
171  }
172 
186  public function renderDeprecatedView($view, array $vars, $suggestion, $version) {
187  $rendered = $this->renderView($view, $vars, false, '', false);
188  if ($rendered) {
189  elgg_deprecated_notice("The $view view has been deprecated. $suggestion", $version, 3);
190  }
191  return $rendered;
192  }
193 
197  public function renderView($view, array $vars = array(), $bypass = false, $viewtype = '', $issue_missing_notice = true) {
198 
199 
200  if (!is_string($view) || !is_string($viewtype)) {
201  $this->logger->log("View and Viewtype in views must be a strings: $view", 'NOTICE');
202  return '';
203  }
204  // basic checking for bad paths
205  if (strpos($view, '..') !== false) {
206  return '';
207  }
208 
209  if (!is_array($vars)) {
210  $this->logger->log("Vars in views must be an array: $view", 'ERROR');
211  $vars = array();
212  }
213 
214  // Get the current viewtype
215  if ($viewtype === '' || !_elgg_is_valid_viewtype($viewtype)) {
217  }
218 
219  $view_orig = $view;
220 
221  // Trigger the pagesetup event
222  if (!isset($this->CONFIG->pagesetupdone) && $this->CONFIG->boot_complete) {
223  $this->CONFIG->pagesetupdone = true;
224  _elgg_services()->events->trigger('pagesetup', 'system');
225  }
226 
227  // @warning - plugin authors: do not expect user, config, and url to be
228  // set by elgg_view() in the future. Instead, use elgg_get_logged_in_user_entity(),
229  // elgg_get_config(), and elgg_get_site_url() in your views.
230  if (!isset($vars['user'])) {
231  $vars['user'] = $this->getUserWrapper();
232  }
233  if (!isset($vars['config'])) {
234  if (!$this->config_wrapper) {
235  $warning = 'Do not rely on $vars["config"] or $CONFIG being available in views';
236  $this->config_wrapper = new \Elgg\DeprecationWrapper($this->CONFIG, $warning, 1.8);
237  }
238  $vars['config'] = $this->config_wrapper;
239  }
240  if (!isset($vars['url'])) {
241  if (!$this->site_url_wrapper) {
242  $warning = 'Do not rely on $vars["url"] being available in views';
243  $this->site_url_wrapper = new \Elgg\DeprecationWrapper(elgg_get_site_url(), $warning, 1.8);
244  }
245  $vars['url'] = $this->site_url_wrapper;
246  }
247 
248  // full_view is the new preferred key for full view on entities @see elgg_view_entity()
249  // check if full_view is set because that means we've already rewritten it and this is
250  // coming from another view passing $vars directly.
251  if (isset($vars['full']) && !isset($vars['full_view'])) {
252  elgg_deprecated_notice("Use \$vars['full_view'] instead of \$vars['full']", 1.8, 2);
253  $vars['full_view'] = $vars['full'];
254  }
255  if (isset($vars['full_view'])) {
256  $vars['full'] = $vars['full_view'];
257  }
258 
259  // internalname => name (1.8)
260  if (isset($vars['internalname']) && !isset($vars['__ignoreInternalname']) && !isset($vars['name'])) {
261  elgg_deprecated_notice('You should pass $vars[\'name\'] now instead of $vars[\'internalname\']', 1.8, 2);
262  $vars['name'] = $vars['internalname'];
263  } elseif (isset($vars['name'])) {
264  if (!isset($vars['internalname'])) {
265  $vars['__ignoreInternalname'] = '';
266  }
267  $vars['internalname'] = $vars['name'];
268  }
269 
270  // internalid => id (1.8)
271  if (isset($vars['internalid']) && !isset($vars['__ignoreInternalid']) && !isset($vars['name'])) {
272  elgg_deprecated_notice('You should pass $vars[\'id\'] now instead of $vars[\'internalid\']', 1.8, 2);
273  $vars['id'] = $vars['internalid'];
274  } elseif (isset($vars['id'])) {
275  if (!isset($vars['internalid'])) {
276  $vars['__ignoreInternalid'] = '';
277  }
278  $vars['internalid'] = $vars['id'];
279  }
280 
281  // If it's been requested, pass off to a template handler instead
282  if ($bypass == false && isset($this->CONFIG->template_handler) && !empty($this->CONFIG->template_handler)) {
283  $template_handler = $this->CONFIG->template_handler;
284  if (is_callable($template_handler)) {
285  return call_user_func($template_handler, $view, $vars);
286  }
287  }
288 
289  // Set up any extensions to the requested view
290  if (isset($this->CONFIG->views->extensions[$view])) {
291  $viewlist = $this->CONFIG->views->extensions[$view];
292  } else {
293  $viewlist = array(500 => $view);
294  }
295 
296  $content = '';
297  foreach ($viewlist as $view) {
298 
299  $rendering = $this->renderViewFile($view, $vars, $viewtype, $issue_missing_notice);
300  if ($rendering !== false) {
301  $content .= $rendering;
302  continue;
303  }
304 
305  // attempt to load default view
306  if ($viewtype !== 'default' && $this->doesViewtypeFallback($viewtype)) {
307 
308  $rendering = $this->renderViewFile($view, $vars, 'default', $issue_missing_notice);
309  if ($rendering !== false) {
310  $content .= $rendering;
311  }
312  }
313  }
314 
315  // Plugin hook
316  $params = array('view' => $view_orig, 'vars' => $vars, 'viewtype' => $viewtype);
317  $content = _elgg_services()->hooks->trigger('view', $view_orig, $params, $content);
318 
319  // backward compatibility with less granular hook will be gone in 2.0
320  $content_tmp = _elgg_services()->hooks->trigger('display', 'view', $params, $content);
321 
322  if ($content_tmp !== $content) {
323  $content = $content_tmp;
324  elgg_deprecated_notice('The display:view plugin hook is deprecated by view:view_name', 1.8);
325  }
326 
327  return $content;
328  }
329 
337  protected function fileExists($path) {
338  if (!isset($this->file_exists_cache[$path])) {
339  $this->file_exists_cache[$path] = file_exists($path);
340  }
341  return $this->file_exists_cache[$path];
342  }
343 
354  private function renderViewFile($view, array $vars, $viewtype, $issue_missing_notice) {
355  $view_location = $this->getViewLocation($view, $viewtype);
356 
357  // @warning - plugin authors: do not expect $CONFIG to be available in views
358  // in the future. Instead, use elgg_get_config() in your views.
359  // Note: this is intentionally a local var.
360  $CONFIG = $this->config_wrapper;
361 
362  if ($this->fileExists("{$view_location}$viewtype/$view.php")) {
363  ob_start();
364  include("{$view_location}$viewtype/$view.php");
365  return ob_get_clean();
366  } else if ($this->fileExists("{$view_location}$viewtype/$view")) {
367  return file_get_contents("{$view_location}$viewtype/$view");
368  } else {
369  if ($issue_missing_notice) {
370  $this->logger->log("$viewtype/$view view does not exist.", 'NOTICE');
371  }
372  return false;
373  }
374  }
375 
379  public function viewExists($view, $viewtype = '', $recurse = true) {
380 
381 
382  // Detect view type
383  if ($viewtype === '' || !_elgg_is_valid_viewtype($viewtype)) {
385  }
386 
387  if (!isset($this->CONFIG->views->locations[$viewtype][$view])) {
388  if (!isset($this->CONFIG->viewpath)) {
389  $location = dirname(dirname(dirname(__FILE__))) . "/views/";
390  } else {
391  $location = $this->CONFIG->viewpath;
392  }
393  } else {
394  $location = $this->CONFIG->views->locations[$viewtype][$view];
395  }
396 
397  if ($this->fileExists("{$location}$viewtype/$view.php") ||
398  $this->fileExists("{$location}$viewtype/$view")) {
399  return true;
400  }
401 
402  // If we got here then check whether this exists as an extension
403  // We optionally recursively check whether the extended view exists also for the viewtype
404  if ($recurse && isset($this->CONFIG->views->extensions[$view])) {
405  foreach ($this->CONFIG->views->extensions[$view] as $view_extension) {
406  // do not recursively check to stay away from infinite loops
407  if ($this->viewExists($view_extension, $viewtype, false)) {
408  return true;
409  }
410  }
411  }
412 
413  // Now check if the default view exists if the view is registered as a fallback
414  if ($viewtype != 'default' && $this->doesViewtypeFallback($viewtype)) {
415  return $this->viewExists($view, 'default');
416  }
417 
418  return false;
419 
420  }
421 
425  public function extendView($view, $view_extension, $priority = 501, $viewtype = '') {
426 
427 
428  if (!isset($this->CONFIG->views)) {
429  $this->CONFIG->views = (object) array(
430  'extensions' => array(),
431  );
432  $this->CONFIG->views->extensions[$view][500] = (string) $view;
433  } else {
434  if (!isset($this->CONFIG->views->extensions[$view])) {
435  $this->CONFIG->views->extensions[$view][500] = (string) $view;
436  }
437  }
438 
439  // raise priority until it doesn't match one already registered
440  while (isset($this->CONFIG->views->extensions[$view][$priority])) {
441  $priority++;
442  }
443 
444  $this->CONFIG->views->extensions[$view][$priority] = (string) $view_extension;
445  ksort($this->CONFIG->views->extensions[$view]);
446 
447  }
448 
452  public function unextendView($view, $view_extension) {
453 
454 
455  if (!isset($this->CONFIG->views)) {
456  return false;
457  }
458 
459  if (!isset($this->CONFIG->views->extensions)) {
460  return false;
461  }
462 
463  if (!isset($this->CONFIG->views->extensions[$view])) {
464  return false;
465  }
466 
467  $priority = array_search($view_extension, $this->CONFIG->views->extensions[$view]);
468  if ($priority === false) {
469  return false;
470  }
471 
472  unset($this->CONFIG->views->extensions[$view][$priority]);
473 
474  return true;
475  }
476 
480  public function registerCacheableView($view) {
481 
482 
483  if (!isset($this->CONFIG->views)) {
484  $this->CONFIG->views = new \stdClass;
485  }
486 
487  if (!isset($this->CONFIG->views->simplecache)) {
488  $this->CONFIG->views->simplecache = array();
489  }
490 
491  $this->CONFIG->views->simplecache[$view] = true;
492  }
493 
497  public function isCacheableView($view) {
498 
499 
500  if (!isset($this->CONFIG->views)) {
501  $this->CONFIG->views = new \stdClass;
502  }
503 
504  if (!isset($this->CONFIG->views->simplecache)) {
505  $this->CONFIG->views->simplecache = array();
506  }
507 
508  if (isset($this->CONFIG->views->simplecache[$view])) {
509  return true;
510  } else {
511  $currentViewtype = elgg_get_viewtype();
512  $viewtypes = array($currentViewtype);
513 
514  if ($this->doesViewtypeFallback($currentViewtype) && $currentViewtype != 'default') {
515  $viewtypes[] = 'defaut';
516  }
517 
518  // If a static view file is found in any viewtype, it's considered cacheable
519  foreach ($viewtypes as $viewtype) {
520  $view_file = $this->getViewLocation($view, $viewtype) . "$viewtype/$view";
521  if ($this->fileExists($view_file)) {
522  return true;
523  }
524  }
525 
526  // Assume not-cacheable by default
527  return false;
528  }
529  }
530 }
$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
if(!$autoload_available) _elgg_services()
Definition: autoloader.php:20
__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
global $CONFIG
$user
Definition: ban.php:13
extendView($view, $view_extension, $priority=501, $viewtype= '')
private
elgg_deprecated_notice($msg, $dep_version, $backtrace_level=1)
Sends a notice about deprecated use of a function, view, etc.
Definition: elgglib.php:1055
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
$viewtype
Definition: start.php:76
$priority
if(file_exists($welcome)) $vars
Definition: upgrade.php:93