diff options
Diffstat (limited to 'engine/lib/views.php')
-rw-r--r-- | engine/lib/views.php | 1657 |
1 files changed, 1657 insertions, 0 deletions
diff --git a/engine/lib/views.php b/engine/lib/views.php new file mode 100644 index 000000000..01291b889 --- /dev/null +++ b/engine/lib/views.php @@ -0,0 +1,1657 @@ +<?php +/** + * Elgg's view system. + * + * The view system is the primary templating engine in Elgg and renders + * all output. Views are short, parameterised PHP scripts for displaying + * output that can be regsitered, overridden, or extended. The view type + * determines the output format and location of the files that renders the view. + * + * Elgg uses a two step process to render full output: first + * content-specific elements are rendered, then the resulting + * content is inserted into a layout and displayed. This makes it + * easy to maintain a consistent look on all pages. + * + * A view corresponds to a single file on the filesystem and the views + * name is its directory structure. A file in + * <code>mod/plugins/views/default/myplugin/example.php</code> + * is called by saying (with the default viewtype): + * <code>echo elgg_view('myplugin/example');</code> + * + * View names that are registered later override those that are + * registered earlier. For plugins this corresponds directly + * to their load order: views in plugins lower in the list override + * those higher in the list. + * + * Plugin views belong in the views/ directory under an appropriate + * viewtype. Views are automatically registered. + * + * Views can be embedded-you can call a view from within a view. + * Views can also be prepended or extended by any other view. + * + * Any view can extend any other view if registered with + * {@link elgg_extend_view()}. + * + * View types are set by passing $_REQUEST['view']. The view type + * 'default' is a standard HTML view. Types can be defined on the fly + * and you can get the current view type with {@link get_current_view()}. + * + * @internal Plugin views are autoregistered before their init functions + * are called, so the init order doesn't affect views. + * + * @internal The file that determines the output of the view is the last + * registered by {@link elgg_set_view_location()}. + * + * @package Elgg.Core + * @subpackage Views + * @link http://docs.elgg.org/Views + */ + +/** + * The view type override. + * + * @global string $CURRENT_SYSTEM_VIEWTYPE + * @see elgg_set_viewtype() + */ +global $CURRENT_SYSTEM_VIEWTYPE; +$CURRENT_SYSTEM_VIEWTYPE = ""; + +/** + * Manually set the viewtype. + * + * View types are detected automatically. This function allows + * you to force subsequent views to use a different viewtype. + * + * @tip Call elgg_set_viewtype() with no parameter to reset. + * + * @param string $viewtype The view type, e.g. 'rss', or 'default'. + * + * @return bool + * @link http://docs.elgg.org/Views/Viewtype + * @example views/viewtype.php + */ +function elgg_set_viewtype($viewtype = "") { + global $CURRENT_SYSTEM_VIEWTYPE; + + $CURRENT_SYSTEM_VIEWTYPE = $viewtype; + + return true; +} + +/** + * Return the current view type. + * + * View types are automatically detected and can be set with $_REQUEST['view'] + * or {@link elgg_set_viewtype()}. + * + * @internal View type is determined in this order: + * - $CURRENT_SYSTEM_VIEWTYPE Any overrides by {@link elgg_set_viewtype()} + * - $CONFIG->view The default view as saved in the DB. + * - $_SESSION['view'] + * + * @return string The view. + * @see elgg_set_viewtype() + * @link http://docs.elgg.org/Views + * @todo This function's sessions stuff needs rewritten, removed, or explained. + */ +function elgg_get_viewtype() { + global $CURRENT_SYSTEM_VIEWTYPE, $CONFIG; + + if ($CURRENT_SYSTEM_VIEWTYPE != "") { + return $CURRENT_SYSTEM_VIEWTYPE; + } + + $viewtype = get_input('view', '', false); + if (is_string($viewtype) && $viewtype !== '') { + // only word characters allowed. + if (!preg_match('/\W/', $viewtype)) { + return $viewtype; + } + } + + if (!empty($CONFIG->view)) { + return $CONFIG->view; + } + + return 'default'; +} + +/** + * Register a view type as valid. + * + * @param string $view_type The view type to register + * @return bool + */ +function elgg_register_viewtype($view_type) { + global $CONFIG; + + if (!isset($CONFIG->view_types) || !is_array($CONFIG->view_types)) { + $CONFIG->view_types = array(); + } + + if (!in_array($view_type, $CONFIG->view_types)) { + $CONFIG->view_types[] = $view_type; + } + + return true; +} + +/** + * Checks if $view_type is valid on this installation. + * + * @param string $view_type View type + * + * @return bool + * @since 1.7.2 + * @access private + */ +function elgg_is_valid_view_type($view_type) { + global $CONFIG; + + if (!isset($CONFIG->view_types) || !is_array($CONFIG->view_types)) { + return FALSE; + } + + return in_array($view_type, $CONFIG->view_types); +} + +/** + * Register a viewtype to fall back to a default view if a view isn't + * found for that viewtype. + * + * @tip This is useful for alternate html viewtypes (such as for mobile devices). + * + * @param string $viewtype The viewtype to register + * + * @return void + * @since 1.7.2 + * @example views/viewtype_fallback.php Fallback from mobile to default. + */ +function elgg_register_viewtype_fallback($viewtype) { + global $CONFIG; + + if (!isset($CONFIG->viewtype)) { + $CONFIG->viewtype = new stdClass; + } + + if (!isset($CONFIG->viewtype->fallback)) { + $CONFIG->viewtype->fallback = array(); + } + + $CONFIG->viewtype->fallback[] = $viewtype; +} + +/** + * Checks if a viewtype falls back to default. + * + * @param string $viewtype Viewtype + * + * @return boolean + * @since 1.7.2 + */ +function elgg_does_viewtype_fallback($viewtype) { + global $CONFIG; + + if (isset($CONFIG->viewtype) && isset($CONFIG->viewtype->fallback)) { + return in_array($viewtype, $CONFIG->viewtype->fallback); + } + + return FALSE; +} + +/** + * Register a view to be available for ajax calls + * + * @param string $view The view name + * @return void + * @since 1.8.3 + */ +function elgg_register_ajax_view($view) { + global $CONFIG; + + if (!isset($CONFIG->allowed_ajax_views)) { + $CONFIG->allowed_ajax_views = array(); + } + + $CONFIG->allowed_ajax_views[$view] = true; +} + +/** + * Unregister a view for ajax calls + * + * @param string $view The view name + * @return void + * @since 1.8.3 + */ +function elgg_unregister_ajax_view($view) { + global $CONFIG; + + if (isset($CONFIG->allowed_ajax_views[$view])) { + unset($CONFIG->allowed_ajax_views[$view]); + } +} + +/** + * Returns the file location for a view. + * + * @warning This doesn't check if the file exists, but only + * constructs (or extracts) the path and returns it. + * + * @param string $view The view. + * @param string $viewtype The viewtype + * + * @return string + */ +function elgg_get_view_location($view, $viewtype = '') { + global $CONFIG; + + if (empty($viewtype)) { + $viewtype = elgg_get_viewtype(); + } + + if (!isset($CONFIG->views->locations[$viewtype][$view])) { + if (!isset($CONFIG->viewpath)) { + return dirname(dirname(dirname(__FILE__))) . "/views/"; + } else { + return $CONFIG->viewpath; + } + } else { + return $CONFIG->views->locations[$viewtype][$view]; + } +} + +/** + * Set an alternative base location for a view. + * + * Views are expected to be in plugin_name/views/. This function can + * be used to change that location. + * + * @internal Core view locations are stored in $CONFIG->viewpath. + * + * @tip This is useful to optionally register views in a plugin. + * + * @param string $view The name of the view + * @param string $location The base location path + * @param string $viewtype The view type + * + * @return void + */ +function elgg_set_view_location($view, $location, $viewtype = '') { + global $CONFIG; + + if (empty($viewtype)) { + $viewtype = 'default'; + } + + if (!isset($CONFIG->views)) { + $CONFIG->views = new stdClass; + } + + if (!isset($CONFIG->views->locations)) { + $CONFIG->views->locations = array($viewtype => array($view => $location)); + + } else if (!isset($CONFIG->views->locations[$viewtype])) { + $CONFIG->views->locations[$viewtype] = array($view => $location); + + } else { + $CONFIG->views->locations[$viewtype][$view] = $location; + } +} + +/** + * Returns whether the specified view exists + * + * @note If $recurse is true, also checks if a view exists only as an extension. + * + * @param string $view The view name + * @param string $viewtype If set, forces the viewtype + * @param bool $recurse If false, do not check extensions + * + * @return bool + */ +function elgg_view_exists($view, $viewtype = '', $recurse = true) { + global $CONFIG; + + // Detect view type + if (empty($viewtype)) { + $viewtype = elgg_get_viewtype(); + } + + if (!isset($CONFIG->views->locations[$viewtype][$view])) { + if (!isset($CONFIG->viewpath)) { + $location = dirname(dirname(dirname(__FILE__))) . "/views/"; + } else { + $location = $CONFIG->viewpath; + } + } else { + $location = $CONFIG->views->locations[$viewtype][$view]; + } + + if (file_exists("{$location}{$viewtype}/{$view}.php")) { + 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($CONFIG->views->extensions[$view])) { + foreach ($CONFIG->views->extensions[$view] as $view_extension) { + // do not recursively check to stay away from infinite loops + if (elgg_view_exists($view_extension, $viewtype, false)) { + return true; + } + } + } + + // Now check if the default view exists if the view is registered as a fallback + if ($viewtype != 'default' && elgg_does_viewtype_fallback($viewtype)) { + return elgg_view_exists($view, 'default'); + } + + return false; +} + +/** + * Return a parsed view. + * + * Views are rendered by a template handler and returned as strings. + * + * Views are called with a special $vars variable set, + * which includes any variables passed as the second parameter. + * For backward compatbility, the following variables are also set but we + * recommend that you do not use them: + * - $vars['config'] The $CONFIG global. (Use {@link elgg_get_config()} instead). + * - $vars['url'] The site URL. (use {@link elgg_get_site_url()} instead). + * - $vars['user'] The logged in user. (use {@link elgg_get_logged_in_user_entity()} instead). + * + * Custom template handlers can be set with {@link set_template_handler()}. + * + * The output of views can be intercepted by registering for the + * view, $view_name plugin hook. + * + * @warning Any variables in $_SESSION will override passed vars + * upon name collision. See {@trac #2124}. + * + * @param string $view The name and location of the view to use + * @param array $vars Variables to pass to the view. + * @param boolean $bypass If set to true, elgg_view will bypass any specified + * alternative template handler; by default, it will + * hand off to this if requested (see set_template_handler) + * @param boolean $ignored This argument is ignored and will be removed eventually + * @param string $viewtype If set, forces the viewtype for the elgg_view call to be + * this value (default: standard detection) + * + * @return string The parsed view + * @see set_template_handler() + * @example views/elgg_view.php + * @link http://docs.elgg.org/View + */ +function elgg_view($view, $vars = array(), $bypass = false, $ignored = false, $viewtype = '') { + global $CONFIG; + + if (!is_string($view) || !is_string($viewtype)) { + elgg_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)) { + elgg_log("Vars in views must be an array: $view", 'ERROR'); + $vars = array(); + } + + // Get the current viewtype + if ($viewtype === '') { + $viewtype = elgg_get_viewtype(); + } elseif (preg_match('/\W/', $viewtype)) { + // Viewtypes can only be alphanumeric + return ''; + } + + $view_orig = $view; + + // Trigger the pagesetup event + if (!isset($CONFIG->pagesetupdone) && $CONFIG->boot_complete) { + $CONFIG->pagesetupdone = true; + elgg_trigger_event('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'] = elgg_get_logged_in_user_entity(); + } + if (!isset($vars['config'])) { + $vars['config'] = $CONFIG; + } + if (!isset($vars['url'])) { + $vars['url'] = elgg_get_site_url(); + } + + // 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($CONFIG->template_handler) && !empty($CONFIG->template_handler)) { + $template_handler = $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($CONFIG->views->extensions[$view])) { + $viewlist = $CONFIG->views->extensions[$view]; + } else { + $viewlist = array(500 => $view); + } + + // Start the output buffer, find the requested view file, and execute it + ob_start(); + + foreach ($viewlist as $priority => $view) { + + $view_location = elgg_get_view_location($view, $viewtype); + $view_file = "$view_location$viewtype/$view.php"; + + // try to include view + if (!file_exists($view_file) || !include($view_file)) { + // requested view does not exist + $error = "$viewtype/$view view does not exist."; + + // attempt to load default view + if ($viewtype !== 'default' && elgg_does_viewtype_fallback($viewtype)) { + + $default_location = elgg_get_view_location($view, 'default'); + $default_view_file = "{$default_location}default/$view.php"; + + if (file_exists($default_view_file) && include($default_view_file)) { + // default view found + $error .= " Using default/$view instead."; + } else { + // no view found at all + $error = "Neither $viewtype/$view nor default/$view view exists."; + } + } + + // log warning + elgg_log($error, 'NOTICE'); + } + } + + // Save the output buffer into the $content variable + $content = ob_get_clean(); + + // Plugin hook + $params = array('view' => $view_orig, 'vars' => $vars, 'viewtype' => $viewtype); + $content = elgg_trigger_plugin_hook('view', $view_orig, $params, $content); + + // backward compatibility with less granular hook will be gone in 2.0 + $content_tmp = elgg_trigger_plugin_hook('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; +} + +/** + * Extends a view with another view. + * + * The output of any view can be prepended or appended to any other view. + * + * The default action is to append a view. If the priority is less than 500, + * the output of the extended view will be appended to the original view. + * + * Priority can be specified and affects the order in which extensions + * are appended or prepended. + * + * @internal View extensions are stored in + * $CONFIG->views->extensions[$view][$priority] = $view_extension + * + * @param string $view The view to extend. + * @param string $view_extension This view is added to $view + * @param int $priority The priority, from 0 to 1000, + * to add at (lowest numbers displayed first) + * + * @return void + * @since 1.7.0 + * @link http://docs.elgg.org/Views/Extend + * @example views/extend.php + */ +function elgg_extend_view($view, $view_extension, $priority = 501) { + global $CONFIG; + + if (!isset($CONFIG->views)) { + $CONFIG->views = (object) array( + 'extensions' => array(), + ); + $CONFIG->views->extensions[$view][500] = (string)$view; + } else { + if (!isset($CONFIG->views->extensions[$view])) { + $CONFIG->views->extensions[$view][500] = (string)$view; + } + } + + // raise priority until it doesn't match one already registered + while (isset($CONFIG->views->extensions[$view][$priority])) { + $priority++; + } + + $CONFIG->views->extensions[$view][$priority] = (string)$view_extension; + ksort($CONFIG->views->extensions[$view]); +} + +/** + * Unextends a view. + * + * @param string $view The view that was extended. + * @param string $view_extension This view that was added to $view + * + * @return bool + * @since 1.7.2 + */ +function elgg_unextend_view($view, $view_extension) { + global $CONFIG; + + if (!isset($CONFIG->views->extensions[$view])) { + return FALSE; + } + + $priority = array_search($view_extension, $CONFIG->views->extensions[$view]); + if ($priority === FALSE) { + return FALSE; + } + + unset($CONFIG->views->extensions[$view][$priority]); + + return TRUE; +} + +/** + * Assembles and outputs a full page. + * + * A "page" in Elgg is determined by the current view type and + * can be HTML for a browser, RSS for a feed reader, or + * Javascript, PHP and a number of other formats. + * + * @param string $title Title + * @param string $body Body + * @param string $page_shell Optional page shell to use. See page/shells view directory + * @param array $vars Optional vars array to pass to the page + * shell. Automatically adds title, body, and sysmessages + * + * @return string The contents of the page + * @since 1.8 + */ +function elgg_view_page($title, $body, $page_shell = 'default', $vars = array()) { + + $messages = null; + if (count_messages()) { + // get messages - try for errors first + $messages = system_messages(NULL, "error"); + if (count($messages["error"]) == 0) { + // no errors so grab rest of messages + $messages = system_messages(null, ""); + } else { + // we have errors - clear out remaining messages + system_messages(null, ""); + } + } + + $vars['title'] = $title; + $vars['body'] = $body; + $vars['sysmessages'] = $messages; + + $vars = elgg_trigger_plugin_hook('output:before', 'page', null, $vars); + + // check for deprecated view + if ($page_shell == 'default' && elgg_view_exists('pageshells/pageshell')) { + elgg_deprecated_notice("pageshells/pageshell is deprecated by page/$page_shell", 1.8); + $output = elgg_view('pageshells/pageshell', $vars); + } else { + $output = elgg_view("page/$page_shell", $vars); + } + + $vars['page_shell'] = $page_shell; + + // Allow plugins to mod output + return elgg_trigger_plugin_hook('output', 'page', $vars, $output); +} + +/** + * Displays a layout with optional parameters. + * + * Layouts provide consistent organization of pages and other blocks of content. + * There are a few default layouts in core: + * - admin A special layout for the admin area. + * - one_column A single content column. + * - one_sidebar A content column with sidebar. + * - two_sidebar A content column with two sidebars. + * - widgets A widget canvas. + * + * The layout views take the form page/layouts/$layout_name + * See the individual layouts for what options are supported. The three most + * common layouts have these parameters: + * one_column + * content => string + * one_sidebar + * content => string + * sidebar => string (optional) + * content + * content => string + * sidebar => string (optional) + * buttons => string (override the default add button) + * title => string (override the default title) + * filter_context => string (selected content filter) + * See the content layout view for more parameters + * + * @param string $layout_name The name of the view in page/layouts/. + * @param array $vars Associative array of parameters for the layout view + * + * @return string The layout + */ +function elgg_view_layout($layout_name, $vars = array()) { + + if (is_string($vars) || $vars === null) { + elgg_deprecated_notice("The use of unlimited optional string arguments in elgg_view_layout() was deprecated in favor of an options array", 1.8); + $arg = 1; + $param_array = array(); + while ($arg < func_num_args()) { + $param_array['area' . $arg] = func_get_arg($arg); + $arg++; + } + } else { + $param_array = $vars; + } + + $params = elgg_trigger_plugin_hook('output:before', 'layout', null, $param_array); + + // check deprecated location + if (elgg_view_exists("canvas/layouts/$layout_name")) { + elgg_deprecated_notice("canvas/layouts/$layout_name is deprecated by page/layouts/$layout_name", 1.8); + $output = elgg_view("canvas/layouts/$layout_name", $params); + } elseif (elgg_view_exists("page/layouts/$layout_name")) { + $output = elgg_view("page/layouts/$layout_name", $params); + } else { + $output = elgg_view("page/layouts/default", $params); + } + + return elgg_trigger_plugin_hook('output:after', 'layout', $params, $output); +} + +/** + * Render a menu + * + * @see elgg_register_menu_item() for documentation on adding menu items and + * navigation.php for information on the different menus available. + * + * This function triggers a 'register', 'menu:<menu name>' plugin hook that enables + * plugins to add menu items just before a menu is rendered. This is used by + * dynamic menus (menus that change based on some input such as the user hover + * menu). Using elgg_register_menu_item() in response to the hook can cause + * incorrect links to show up. See the blog plugin's blog_owner_block_menu() + * for an example of using this plugin hook. + * + * An additional hook is the 'prepare', 'menu:<menu name>' which enables plugins + * to modify the structure of the menu (sort it, remove items, set variables on + * the menu items). + * + * elgg_view_menu() uses views in navigation/menu + * + * @param string $menu_name The name of the menu + * @param array $vars An associative array of display options for the menu. + * Options include: + * sort_by => string or php callback + * string options: 'name', 'priority', 'title' (default), + * 'register' (registration order) or a + * php callback (a compare function for usort) + * handler: string the page handler to build action URLs + * entity: ElggEntity to use to build action URLs + * class: string the class for the entire menu. + * show_section_headers: bool show headers before menu sections. + * + * @return string + * @since 1.8.0 + */ +function elgg_view_menu($menu_name, array $vars = array()) { + global $CONFIG; + + $vars['name'] = $menu_name; + + $sort_by = elgg_extract('sort_by', $vars, 'text'); + + if (isset($CONFIG->menus[$menu_name])) { + $menu = $CONFIG->menus[$menu_name]; + } else { + $menu = array(); + } + + // Give plugins a chance to add menu items just before creation. + // This supports dynamic menus (example: user_hover). + $menu = elgg_trigger_plugin_hook('register', "menu:$menu_name", $vars, $menu); + + $builder = new ElggMenuBuilder($menu); + $vars['menu'] = $builder->getMenu($sort_by); + $vars['selected_item'] = $builder->getSelected(); + + // Let plugins modify the menu + $vars['menu'] = elgg_trigger_plugin_hook('prepare', "menu:$menu_name", $vars, $vars['menu']); + + if (elgg_view_exists("navigation/menu/$menu_name")) { + return elgg_view("navigation/menu/$menu_name", $vars); + } else { + return elgg_view("navigation/menu/default", $vars); + } +} + +/** + * Returns a string of a rendered entity. + * + * Entity views are either determined by setting the view property on the entity + * or by having a view named after the entity $type/$subtype. Entities that have + * neither a view property nor a defined $type/$subtype view will fall back to + * using the $type/default view. + * + * The entity view is called with the following in $vars: + * - ElggEntity 'entity' The entity being viewed + * + * Other common view $vars paramters: + * - bool 'full_view' Whether to show a full or condensed view. + * + * @tip This function can automatically appends annotations to entities if in full + * view and a handler is registered for the entity:annotate. See {@trac 964} and + * {@link elgg_view_entity_annotations()}. + * + * @param ElggEntity $entity The entity to display + * @param array $vars Array of variables to pass to the entity view. + * In Elgg 1.7 and earlier it was the boolean $full_view + * @param boolean $bypass If false, will not pass to a custom template handler. + * {@see set_template_handler()} + * @param boolean $debug Complain if views are missing + * + * @return string HTML to display or false + * @link http://docs.elgg.org/Views/Entity + * @link http://docs.elgg.org/Entities + * @todo The annotation hook might be better as a generic plugin hook to append content. + */ +function elgg_view_entity(ElggEntity $entity, $vars = array(), $bypass = true, $debug = false) { + + // No point continuing if entity is null + if (!$entity || !($entity instanceof ElggEntity)) { + return false; + } + + global $autofeed; + $autofeed = true; + + $defaults = array( + 'full_view' => false, + ); + + if (is_array($vars)) { + $vars = array_merge($defaults, $vars); + } else { + elgg_deprecated_notice("Update your use of elgg_view_entity()", 1.8); + $vars = array( + 'full_view' => $vars, + ); + } + + $vars['entity'] = $entity; + + + // if this entity has a view defined, use it + $view = $entity->view; + if (is_string($view)) { + return elgg_view($view, $vars, $bypass, $debug); + } + + $entity_type = $entity->getType(); + + $subtype = $entity->getSubtype(); + if (empty($subtype)) { + $subtype = 'default'; + } + + $contents = ''; + if (elgg_view_exists("$entity_type/$subtype")) { + $contents = elgg_view("$entity_type/$subtype", $vars, $bypass, $debug); + } + if (empty($contents)) { + $contents = elgg_view("$entity_type/default", $vars, $bypass, $debug); + } + + // Marcus Povey 20090616 : Speculative and low impact approach for fixing #964 + if ($vars['full_view']) { + $annotations = elgg_view_entity_annotations($entity, $vars['full_view']); + + if ($annotations) { + $contents .= $annotations; + } + } + return $contents; +} + +/** + * View the icon of an entity + * + * Entity views are determined by having a view named after the entity $type/$subtype. + * Entities that do not have a defined icon/$type/$subtype view will fall back to using + * the icon/$type/default view. + * + * @param ElggEntity $entity The entity to display + * @param string $size The size: tiny, small, medium, large + * @param array $vars An array of variables to pass to the view. Some possible + * variables are img_class and link_class. See the + * specific icon view for more parameters. + * + * @return string HTML to display or false + */ +function elgg_view_entity_icon(ElggEntity $entity, $size = 'medium', $vars = array()) { + + // No point continuing if entity is null + if (!$entity || !($entity instanceof ElggEntity)) { + return false; + } + + $vars['entity'] = $entity; + $vars['size'] = $size; + + $entity_type = $entity->getType(); + + $subtype = $entity->getSubtype(); + if (empty($subtype)) { + $subtype = 'default'; + } + + $contents = ''; + if (elgg_view_exists("icon/$entity_type/$subtype")) { + $contents = elgg_view("icon/$entity_type/$subtype", $vars); + } + if (empty($contents)) { + $contents = elgg_view("icon/$entity_type/default", $vars); + } + if (empty($contents)) { + $contents = elgg_view("icon/default", $vars); + } + + return $contents; +} + +/** + * Returns a string of a rendered annotation. + * + * Annotation views are expected to be in annotation/$annotation_name. + * If a view is not found for $annotation_name, the default annotation/default + * will be used. + * + * @warning annotation/default is not currently defined in core. + * + * The annotation view is called with the following in $vars: + * - ElggEntity 'annotation' The annotation being viewed. + * + * @param ElggAnnotation $annotation The annotation to display + * @param array $vars Variable array for view. + * @param bool $bypass If false, will not pass to a custom + * template handler. {@see set_template_handler()} + * @param bool $debug Complain if views are missing + * + * @return string/false Rendered annotation + */ +function elgg_view_annotation(ElggAnnotation $annotation, array $vars = array(), $bypass = true, $debug = false) { + global $autofeed; + $autofeed = true; + + $defaults = array( + 'full_view' => true, + ); + + $vars = array_merge($defaults, $vars); + $vars['annotation'] = $annotation; + + // @todo setting the view on an annotation is not advertised anywhere + // do we want to keep this? + $view = $annotation->view; + if (is_string($view)) { + return elgg_view($view, $vars, $bypass, $debug); + } + + $name = $annotation->name; + if (empty($name)) { + return false; + } + + if (elgg_view_exists("annotation/$name")) { + return elgg_view("annotation/$name", $vars, $bypass, $debug); + } else { + return elgg_view("annotation/default", $vars, $bypass, $debug); + } +} + +/** + * Returns a rendered list of entities with pagination. This function should be + * called by wrapper functions. + * + * @see elgg_list_entities() + * @see list_user_friends_objects() + * @see elgg_list_entities_from_metadata() + * @see elgg_list_entities_from_relationships() + * @see elgg_list_entities_from_annotations() + * + * @param array $entities Array of entities + * @param array $vars Display variables + * 'count' The total number of entities across all pages + * 'offset' The current indexing offset + * 'limit' The number of entities to display per page + * 'full_view' Display the full view of the entities? + * 'list_class' CSS class applied to the list + * 'item_class' CSS class applied to the list items + * 'pagination' Display pagination? + * 'list_type' List type: 'list' (default), 'gallery' + * 'list_type_toggle' Display the list type toggle? + * + * @return string The rendered list of entities + * @access private + */ +function elgg_view_entity_list($entities, $vars = array(), $offset = 0, $limit = 10, $full_view = true, +$list_type_toggle = true, $pagination = true) { + + if (!is_int($offset)) { + $offset = (int)get_input('offset', 0); + } + + // list type can be passed as request parameter + $list_type = get_input('list_type', 'list'); + if (get_input('listtype')) { + elgg_deprecated_notice("'listtype' has been deprecated by 'list_type' for lists", 1.8); + $list_type = get_input('listtype'); + } + + if (is_array($vars)) { + // new function + $defaults = array( + 'items' => $entities, + 'list_class' => 'elgg-list-entity', + 'full_view' => true, + 'pagination' => true, + 'list_type' => $list_type, + 'list_type_toggle' => false, + 'offset' => $offset, + ); + + $vars = array_merge($defaults, $vars); + + } else { + // old function parameters + elgg_deprecated_notice("Please update your use of elgg_view_entity_list()", 1.8); + + $vars = array( + 'items' => $entities, + 'count' => (int) $vars, // the old count parameter + 'offset' => $offset, + 'limit' => (int) $limit, + 'full_view' => $full_view, + 'pagination' => $pagination, + 'list_type' => $list_type, + 'list_type_toggle' => $list_type_toggle, + 'list_class' => 'elgg-list-entity', + ); + } + + if ($vars['list_type'] != 'list') { + return elgg_view('page/components/gallery', $vars); + } else { + return elgg_view('page/components/list', $vars); + } +} + +/** + * Returns a rendered list of annotations, plus pagination. This function + * should be called by wrapper functions. + * + * @param array $annotations Array of annotations + * @param array $vars Display variables + * 'count' The total number of annotations across all pages + * 'offset' The current indexing offset + * 'limit' The number of annotations to display per page + * 'full_view' Display the full view of the annotation? + * 'list_class' CSS Class applied to the list + * 'offset_key' The url parameter key used for offset + * + * @return string The list of annotations + * @access private + */ +function elgg_view_annotation_list($annotations, array $vars = array()) { + $defaults = array( + 'items' => $annotations, + 'list_class' => 'elgg-list-annotation elgg-annotation-list', // @todo remove elgg-annotation-list in Elgg 1.9 + 'full_view' => true, + 'offset_key' => 'annoff', + ); + + $vars = array_merge($defaults, $vars); + + return elgg_view('page/components/list', $vars); +} + +/** + * Display a plugin-specified rendered list of annotations for an entity. + * + * This displays the output of functions registered to the entity:annotation, + * $entity_type plugin hook. + * + * This is called automatically by the framework from {@link elgg_view_entity()} + * + * @param ElggEntity $entity Entity + * @param bool $full_view Display full view? + * + * @return mixed string or false on failure + * @todo Change the hook name. + */ +function elgg_view_entity_annotations(ElggEntity $entity, $full_view = true) { + if (!($entity instanceof ElggEntity)) { + return false; + } + + $entity_type = $entity->getType(); + + $annotations = elgg_trigger_plugin_hook('entity:annotate', $entity_type, + array( + 'entity' => $entity, + 'full_view' => $full_view, + ) + ); + + return $annotations; +} + +/** + * Renders a title. + * + * This is a shortcut for {@elgg_view page/elements/title}. + * + * @param string $title The page title + * @param array $vars View variables (was submenu be displayed? (deprecated)) + * + * @return string The HTML (etc) + */ +function elgg_view_title($title, $vars = array()) { + if (!is_array($vars)) { + elgg_deprecated_notice('setting $submenu in elgg_view_title() is deprecated', 1.8); + $vars = array('submenu' => $vars); + } + + $vars['title'] = $title; + + return elgg_view('page/elements/title', $vars); +} + +/** + * Displays a UNIX timestamp in a friendly way + * + * @see elgg_get_friendly_time() + * + * @param int $time A UNIX epoch timestamp + * + * @return string The friendly time HTML + * @since 1.7.2 + */ +function elgg_view_friendly_time($time) { + return elgg_view('output/friendlytime', array('time' => $time)); +} + + +/** + * Returns rendered comments and a comment form for an entity. + * + * @tip Plugins can override the output by registering a handler + * for the comments, $entity_type hook. The handler is responsible + * for formatting the comments and the add comment form. + * + * @param ElggEntity $entity The entity to view comments of + * @param bool $add_comment Include a form to add comments? + * @param array $vars Variables to pass to comment view + * + * @return string|false Rendered comments or false on failure + * @link http://docs.elgg.org/Entities/Comments + * @link http://docs.elgg.org/Annotations/Comments + */ +function elgg_view_comments($entity, $add_comment = true, array $vars = array()) { + if (!($entity instanceof ElggEntity)) { + return false; + } + + $vars['entity'] = $entity; + $vars['show_add_form'] = $add_comment; + $vars['class'] = elgg_extract('class', $vars, "{$entity->getSubtype()}-comments"); + + $output = elgg_trigger_plugin_hook('comments', $entity->getType(), $vars, false); + if ($output) { + return $output; + } else { + return elgg_view('page/elements/comments', $vars); + } +} + +/** + * Wrapper function for the image block display pattern. + * + * Fixed width media on the side (image, icon, flash, etc.). + * Descriptive content filling the rest of the column. + * + * This is a shortcut for {@elgg_view page/components/image_block}. + * + * @param string $image The icon and other information + * @param string $body Description content + * @param array $vars Additional parameters for the view + * + * @return string + * @since 1.8.0 + */ +function elgg_view_image_block($image, $body, $vars = array()) { + $vars['image'] = $image; + $vars['body'] = $body; + return elgg_view('page/components/image_block', $vars); +} + +/** + * Wrapper function for the module display pattern. + * + * Box with header, body, footer + * + * This is a shortcut for {@elgg_view page/components/module}. + * + * @param string $type The type of module (main, info, popup, aside, etc.) + * @param string $title A title to put in the header + * @param string $body Content of the module + * @param array $vars Additional parameters for the module + * + * @return string + * @since 1.8.0 + */ +function elgg_view_module($type, $title, $body, array $vars = array()) { + $vars['class'] = elgg_extract('class', $vars, '') . " elgg-module-$type"; + $vars['title'] = $title; + $vars['body'] = $body; + return elgg_view('page/components/module', $vars); +} + +/** + * Renders a human-readable representation of a river item + * + * @param ElggRiverItem $item A river item object + * @param array $vars An array of variables for the view + * + * @return string returns empty string if could not be rendered + */ +function elgg_view_river_item($item, array $vars = array()) { + if (!($item instanceof ElggRiverItem)) { + return ''; + } + // checking default viewtype since some viewtypes do not have unique views per item (rss) + $view = $item->getView(); + if (!$view || !elgg_view_exists($view, 'default')) { + return ''; + } + + $subject = $item->getSubjectEntity(); + $object = $item->getObjectEntity(); + if (!$subject || !$object) { + // subject is disabled or subject/object deleted + return ''; + } + // Don't hide objects in closed groups that a user can see. + // see http://trac.elgg.org/ticket/4789 +// else { +// // hide based on object's container +// $visibility = ElggGroupItemVisibility::factory($object->container_guid); +// if ($visibility->shouldHideItems) { +// return ''; +// } +// } + + $vars['item'] = $item; + + return elgg_view('river/item', $vars); +} + +/** + * Convenience function for generating a form from a view in a standard location. + * + * This function assumes that the body of the form is located at "forms/$action" and + * sets the action by default to "action/$action". Automatically wraps the forms/$action + * view with a <form> tag and inserts the anti-csrf security tokens. + * + * @tip This automatically appends elgg-form-action-name to the form's class. It replaces any + * slashes with dashes (blog/save becomes elgg-form-blog-save) + * + * @example + * <code>echo elgg_view_form('login');</code> + * + * This would assume a "login" form body to be at "forms/login" and would set the action + * of the form to "http://yoursite.com/action/login". + * + * If elgg_view('forms/login') is: + * <input type="text" name="username" /> + * <input type="password" name="password" /> + * + * Then elgg_view_form('login') generates: + * <form action="http://yoursite.com/action/login" method="post"> + * ...security tokens... + * <input type="text" name="username" /> + * <input type="password" name="password" /> + * </form> + * + * @param string $action The name of the action. An action name does not include + * the leading "action/". For example, "login" is an action name. + * @param array $form_vars $vars environment passed to the "input/form" view + * @param array $body_vars $vars environment passed to the "forms/$action" view + * + * @return string The complete form + */ +function elgg_view_form($action, $form_vars = array(), $body_vars = array()) { + global $CONFIG; + + $defaults = array( + 'action' => $CONFIG->wwwroot . "action/$action", + 'body' => elgg_view("forms/$action", $body_vars) + ); + + $form_class = 'elgg-form-' . preg_replace('/[^a-z0-9]/i', '-', $action); + + // append elgg-form class to any class options set + if (isset($form_vars['class'])) { + $form_vars['class'] = $form_vars['class'] . " $form_class"; + } else { + $form_vars['class'] = $form_class; + } + + return elgg_view('input/form', array_merge($defaults, $form_vars)); +} + +/** + * View an item in a list + * + * @param object $item ElggEntity or ElggAnnotation + * @param array $vars Additional parameters for the rendering + * + * @return string + * @since 1.8.0 + * @access private + */ +function elgg_view_list_item($item, array $vars = array()) { + global $CONFIG; + + $type = $item->getType(); + if (in_array($type, $CONFIG->entity_types)) { + return elgg_view_entity($item, $vars); + } else if ($type == 'annotation') { + return elgg_view_annotation($item, $vars); + } else if ($type == 'river') { + return elgg_view_river_item($item, $vars); + } + + return ''; +} + +/** + * View one of the elgg sprite icons + * + * Shorthand for <span class="elgg-icon elgg-icon-$name"></span> + * + * @param string $name The specific icon to display + * @param string $class Additional class: float, float-alt, or custom class + * + * @return string The html for displaying an icon + */ +function elgg_view_icon($name, $class = '') { + // @todo deprecate boolean in Elgg 1.9 + if ($class === true) { + $class = 'float'; + } + return "<span class=\"elgg-icon elgg-icon-$name $class\"></span>"; +} + +/** + * Displays a user's access collections, using the core/friends/collections view + * + * @param int $owner_guid The GUID of the owning user + * + * @return string A formatted rendition of the collections + * @todo Move to the friends/collection.php page. + * @access private + */ +function elgg_view_access_collections($owner_guid) { + if ($collections = get_user_access_collections($owner_guid)) { + foreach ($collections as $key => $collection) { + $collections[$key]->members = get_members_of_access_collection($collection->id, true); + $collections[$key]->entities = get_user_friends($owner_guid, "", 9999); + } + } + + return elgg_view('core/friends/collections', array('collections' => $collections)); +} + +/** + * Registers a function to handle templates. + * + * Alternative template handlers can be registered to handle + * all output functions. By default, {@link elgg_view()} will + * simply include the view file. If an alternate template handler + * is registered, the view name and passed $vars will be passed to the + * registered function, which is then responsible for generating and returning + * output. + * + * Template handlers need to accept two arguments: string $view_name and array + * $vars. + * + * @warning This is experimental. + * + * @param string $function_name The name of the function to pass to. + * + * @return bool + * @see elgg_view() + * @link http://docs.elgg.org/Views/TemplateHandlers + */ +function set_template_handler($function_name) { + global $CONFIG; + + if (is_callable($function_name)) { + $CONFIG->template_handler = $function_name; + return true; + } + return false; +} + +/** + * Returns the name of views for in a directory. + * + * Use this to get all namespaced views under the first element. + * + * @param string $dir The main directory that holds the views. (mod/profile/views/) + * @param string $base The root name of the view to use, without the viewtype. (profile) + * + * @return array + * @since 1.7.0 + * @todo Why isn't this used anywhere else but in elgg_view_tree()? + * Seems like a useful function for autodiscovery. + * @access private + */ +function elgg_get_views($dir, $base) { + $return = array(); + if (file_exists($dir) && is_dir($dir)) { + if ($handle = opendir($dir)) { + while ($view = readdir($handle)) { + if (!in_array($view, array('.', '..', '.svn', 'CVS'))) { + if (is_dir($dir . '/' . $view)) { + if ($val = elgg_get_views($dir . '/' . $view, $base . '/' . $view)) { + $return = array_merge($return, $val); + } + } else { + $view = str_replace('.php', '', $view); + $return[] = $base . '/' . $view; + } + } + } + } + } + + return $return; +} + +/** + * Returns all views below a partial view. + * + * Settings $view_root = 'profile' will show all available views under + * the "profile" namespace. + * + * @param string $view_root The root view + * @param string $viewtype Optionally specify a view type + * other than the current one. + * + * @return array A list of view names underneath that root view + * @todo This is used once in the deprecated get_activity_stream_data() function. + * @access private + */ +function elgg_view_tree($view_root, $viewtype = "") { + global $CONFIG; + static $treecache; + + // Get viewtype + if (!$viewtype) { + $viewtype = elgg_get_viewtype(); + } + + // Has the treecache been initialised? + if (!isset($treecache)) { + $treecache = array(); + } + // A little light internal caching + if (!empty($treecache[$view_root])) { + return $treecache[$view_root]; + } + + // Examine $CONFIG->views->locations + if (isset($CONFIG->views->locations[$viewtype])) { + foreach ($CONFIG->views->locations[$viewtype] as $view => $path) { + $pos = strpos($view, $view_root); + if ($pos === 0) { + $treecache[$view_root][] = $view; + } + } + } + + // Now examine core + $location = $CONFIG->viewpath; + $viewtype = elgg_get_viewtype(); + $root = $location . $viewtype . '/' . $view_root; + + if (file_exists($root) && is_dir($root)) { + $val = elgg_get_views($root, $view_root); + if (!is_array($treecache[$view_root])) { + $treecache[$view_root] = array(); + } + $treecache[$view_root] = array_merge($treecache[$view_root], $val); + } + + return $treecache[$view_root]; +} + +/** + * Auto-registers views from a location. + * + * @note Views in plugin/views/ are automatically registered for active plugins. + * Plugin authors would only need to call this if optionally including + * an entire views structure. + * + * @param string $view_base Optional The base of the view name without the view type. + * @param string $folder Required The folder to begin looking in + * @param string $base_location_path The base views directory to use with elgg_set_view_location() + * @param string $viewtype The type of view we're looking at (default, rss, etc) + * + * @return bool returns false if folder can't be read + * @since 1.7.0 + * @see elgg_set_view_location() + * @todo This seems overly complicated. + * @access private + */ +function autoregister_views($view_base, $folder, $base_location_path, $viewtype) { + if ($handle = opendir($folder)) { + while ($view = readdir($handle)) { + if (!in_array($view, array('.', '..', '.svn', 'CVS')) && !is_dir($folder . "/" . $view)) { + // this includes png files because some icons are stored within view directories. + // See commit [1705] + if ((substr_count($view, ".php") > 0) || (substr_count($view, ".png") > 0)) { + if (!empty($view_base)) { + $view_base_new = $view_base . "/"; + } else { + $view_base_new = ""; + } + + elgg_set_view_location($view_base_new . str_replace('.php', '', $view), + $base_location_path, $viewtype); + } + } else if (!in_array($view, array('.', '..', '.svn', 'CVS')) && is_dir($folder . "/" . $view)) { + if (!empty($view_base)) { + $view_base_new = $view_base . "/"; + } else { + $view_base_new = ""; + } + autoregister_views($view_base_new . $view, $folder . "/" . $view, + $base_location_path, $viewtype); + } + } + return TRUE; + } + + return FALSE; +} + +/** + * Add the rss link to the extras when if needed + * + * @return void + * @access private + */ +function elgg_views_add_rss_link() { + global $autofeed; + if (isset($autofeed) && $autofeed == true) { + $url = current_page_url(); + if (substr_count($url, '?')) { + $url .= "&view=rss"; + } else { + $url .= "?view=rss"; + } + + $url = elgg_format_url($url); + elgg_register_menu_item('extras', array( + 'name' => 'rss', + 'text' => elgg_view_icon('rss'), + 'href' => $url, + 'title' => elgg_echo('feed:rss'), + )); + } +} + +/** + * Registers deprecated views to avoid making some pages from older plugins + * completely empty. + * + * @access private + */ +function elgg_views_handle_deprecated_views() { + $location = elgg_get_view_location('page_elements/contentwrapper'); + if ($location === "/var/www/views/") { + elgg_extend_view('page_elements/contentwrapper', 'page/elements/wrapper'); + } +} + +/** + * Initialize viewtypes on system boot event + * This ensures simplecache is cleared during upgrades. See #2252 + * + * @return void + * @access private + * @elgg_event_handler boot system + */ +function elgg_views_boot() { + global $CONFIG; + + elgg_register_simplecache_view('css/ie'); + elgg_register_simplecache_view('css/ie6'); + elgg_register_simplecache_view('css/ie7'); + + elgg_register_js('jquery', '/vendors/jquery/jquery-1.6.4.min.js', 'head'); + elgg_register_js('jquery-ui', '/vendors/jquery/jquery-ui-1.8.16.min.js', 'head'); + elgg_register_js('jquery.form', '/vendors/jquery/jquery.form.js'); + + elgg_register_simplecache_view('js/elgg'); + $elgg_js_url = elgg_get_simplecache_url('js', 'elgg'); + elgg_register_js('elgg', $elgg_js_url, 'head'); + + elgg_load_js('jquery'); + elgg_load_js('jquery-ui'); + elgg_load_js('elgg'); + + elgg_register_simplecache_view('js/lightbox'); + $lightbox_js_url = elgg_get_simplecache_url('js', 'lightbox'); + elgg_register_js('lightbox', $lightbox_js_url); + + elgg_register_simplecache_view('css/lightbox'); + $lightbox_css_url = elgg_get_simplecache_url('css', 'lightbox'); + elgg_register_css('lightbox', $lightbox_css_url); + + elgg_register_simplecache_view('css/elgg'); + $elgg_css_url = elgg_get_simplecache_url('css', 'elgg'); + elgg_register_css('elgg', $elgg_css_url); + + elgg_load_css('elgg'); + + elgg_register_ajax_view('js/languages'); + + elgg_register_plugin_hook_handler('output:before', 'layout', 'elgg_views_add_rss_link'); + + // discover the built-in view types + // @todo the cache is loaded in load_plugins() but we need to know view_types earlier + $view_path = $CONFIG->viewpath; + + $views = scandir($view_path); + + foreach ($views as $view) { + if ($view[0] !== '.' && is_dir($view_path . $view)) { + elgg_register_viewtype($view); + } + } + + // set default icon sizes - can be overridden in settings.php or with plugin + if (!$CONFIG->icon_sizes) { + $icon_sizes = array( + 'topbar' => array('w' => 16, 'h' => 16, 'square' => TRUE, 'upscale' => TRUE), + 'tiny' => array('w' => 25, 'h' => 25, 'square' => TRUE, 'upscale' => TRUE), + 'small' => array('w' => 40, 'h' => 40, 'square' => TRUE, 'upscale' => TRUE), + 'medium' => array('w' => 100, 'h' => 100, 'square' => TRUE, 'upscale' => TRUE), + 'large' => array('w' => 200, 'h' => 200, 'square' => FALSE, 'upscale' => FALSE), + 'master' => array('w' => 550, 'h' => 550, 'square' => FALSE, 'upscale' => FALSE), + ); + elgg_set_config('icon_sizes', $icon_sizes); + } +} + +elgg_register_event_handler('boot', 'system', 'elgg_views_boot'); +elgg_register_event_handler('init', 'system', 'elgg_views_handle_deprecated_views'); |