'; $output .= t('You may peruse a list of your current panels layouts and edit them, or click add to create a new page.'); $output .= '

'; break; case 'admin/panels/panel-page/add': $output = '

'; $output .= t('Choose a layout for your new page from the list below.'); $output .= '

'; break; } return $output; } /** * Implementation of hook_theme(). */ function panels_page_theme() { $theme = array(); $theme['panels_page_render_form'] = array( 'arguments' => array('form'), ); return $theme; } /** * Implementation of hook_perm(). */ function panels_page_perm() { return array('create panel-pages', 'access all panel-pages'); } function panels_page_menu() { panels_page_load_include('menu'); panels_page_load_include('read'); $items = panels_page_admin_static_menu_items(); $items = panels_page_admin_dynamic_menu_items($items); return $items; } /** * Implementation of hook_menu_alter(). * * Delegates to an .inc file to reduce code load overhead. */ function panels_page_menu_alter(&$callbacks) { return _panels_page_menu_alter($callbacks); } /*function panels_page_menu_link_alter(&$item, &$menu) { }*/ /** * Menu loader for some panels_page admin screens. Loads the panels_page * specified by the name arg, induces a 404 when a bad arg is provided. * * Use the empty string so that the behavior of callers that don't pass arg(6) * will be the same as callers who do pass arg(6), but arg(6) is empty. * * @param string $name * @param string $which * @return mixed */ function panels_page_admin_load() { // Lets us tell if this is the first time through the function. Fugly hack to // do it this way, but this gets called numerous times by theme(), which makes // it the only way to avoid generating superfluous object cache items due to // later indirect calls to this function made by access checking within // theme(). They entries get get cleared later, but it's still icky, so we // head it off here. static $first = TRUE; panels_page_load_include('write'); panels_load_include('plugins'); $args = func_get_args(); $name = array_shift($args); $which = array_shift($args); if (function_exists($which) && $first) { return $which($name, array_shift($args)); } $first = FALSE; $panel_page = panels_page_load($name); panels_page_fetch_display($panel_page, $which); panels_page_set_current($panel_page); return is_object($panel_page) && !empty($panel_page->pid) ? $panel_page : FALSE; } /** * Menu loader for some panels_page admin screens. Loads the panels_page * specified by the name arg from the edit cache, or induces a 404 when a bad * arg is provided. * * Use the empty string so that the behavior of callers that don't pass arg(6) * will be identical to callers who do pass arg(6), but arg(6) is empty. * * @param string $name * @param string $which * @return mixed */ function panels_page_admin_cache_load($name, $which = '') { static $panel_page; if (empty($panel_page)) { if (!empty($_POST)) { $panel_page = panels_cache_get('panel_object:panel_page', $name); } else { $panel_page = panels_page_load($name); panels_page_fetch_display($panel_page, $which); panels_cache_set('panel_object:panel_page', $name, $panel_page); } } if (is_object($panel_page) && !empty($panel_page->pid)) { panels_page_set_current($panel_page); return $panel_page; } return FALSE; } /** * Kluges local tasks to basically work like menu links instead of router items. * * ...like they should. This function is headdesking's raison d'etre. */ function panels_page_concession_to_borked_tabs($name) { static $first = TRUE; if (!user_access('create panel-pages')) { return FALSE; } if (!$first || panels_page_climb_stack()) { $panel_page = panels_page_get_current(); return $panel_page->name == $name; } $first = FALSE; return TRUE; } /** * Checks to see if theme() is in the current call stack. * * Let there be kluge. Horrible, awful, kitten-mass-murdering KLUGE. * */ function panels_page_climb_stack() { $backtrace = debug_backtrace(); $result = array_filter($backtrace, '_panels_page_climb_stack'); return !empty($result); } /** * Kluge's little helper. Kinda like Santa's little helper. Except not. */ function _panels_page_climb_stack($call) { return $call['function'] == 'theme'; } /** * Check whether the current page request is allowed. * * Note that this handler is ONLY used by static panel_pages; the rest are all * handled internally by the master loader. * * TODO this is still fairly hackish, and we also really may need to add support * for these checks for dynamic panels_pages... * * @return boolean */ function panels_page_access_handler() { panels_page_load_include('read'); $args = func_get_args(); $name = array_shift($args); return (bool) panels_page_access($name); } /** * Execute the active page rendering callback. * * This is the unified handler through which ALL (non-admin) panels_page-touched * callbacks pass. It takes the callback and arguments calculated by the main * brain, panels_page_get_loader_data(), and fires out the callback with its * arguments. * * @return mixed */ function panels_page_render_handler() { $args = func_get_args(); // FIXME all of these are wrapped in if statements b/c of the 403/404 possibility if ($loader_data = panels_page_master_loader($args)) { return call_user_func_array($loader_data->page_callback, $loader_data->page_arguments); } } function panels_page_static_render_handler() { $args = func_get_args(); if ($loader_data = panels_page_master_loader($args)) { return panels_page_render_page_normal($loader_data->panel_page, array()); } } function panels_page_title_handler() { $args = func_get_args(); if ($loader_data = panels_page_master_loader($args)) { return $loader_data->fallback ? _menu_item_localize($loader_data->router_item, $loader_data->map) : $loader_data->title; } } /** * Wrapper function for the actual panels_page master loader, * _panels_page_master_loader(). * * @param array $args * Any additional argument data; varies significantly from one panel_page to * the next. * @return array * The $loader_data corresponding to the request data we've passed in. */ function panels_page_master_loader($args) { // Get the unique name of the panel_page, which is always the arg on top of // the $args array stack $name = array_shift($args); return _panels_page_master_loader($name, $args); } /** * Determine the render-time behavior of panels_page. * * This function is basically the brains of the dynamic override system. * * @param string $name * @param array $args * @return array $load */ function _panels_page_master_loader($name, $args) { static $loader_data = array(); if (!empty($loader_data[$name])) { return $loader_data[$name]; } $load = new stdClass(); $loader_data[$name] = &$load; $load->panel_args = array(); panels_load_include('plugins'); panels_page_load_include('read'); $panel_page = panels_page_load($name); $panel_page->context = array(); // Handle static pages then duck out early. if (!($panel_page->load_flags & PANELS_IS_DYNAMIC)) { if ((bool) panels_page_access($panel_page->name)) { panels_page_load_include('render'); panels_page_fetch_primary_display($panel_page); panels_page_prepare_panels_render($load, $panel_page); } return $load; } // Construct $panel_page->context and determine if we fall back. _panels_page_construct_argument_contexts($load, $panel_page, $args); // If we've determined that we're falling back... if ($load->fallback === TRUE) { // ...then bail out and do it. return panels_page_prepare_fallback_render($load, _panels_page_rebuild_menu_map($args, array_keys(explode('/', $panel_page->path), '%'))); } // By now we are 100% certain that a panel_page render should occur, so check // the panels_page native access function. If that passes, then include the // the render inc file and proceed inside there. $load->access = panels_page_access($panel_page->name); if (empty($load->access)) { return drupal_access_denied(); } panels_page_load_include('render'); return panels_page_prepare_panels_render($load, $panel_page); } /** * Extracts context data from provided URLs; helper function for * _panels_page_master_loader(). * * @param object $load * @param object $panel_page * @param array $args */ function _panels_page_construct_argument_contexts(&$load, &$panel_page, $args) { // At this point, we know we're handling a dynamic/override panels_page. // Start off by assuming that we won't fall back. $load->fallback = FALSE; // TODO Multiple p-pages per path will necessitate more complex logic here $load_objects = array(); foreach ($panel_page->arguments as $id => $argument) { $ignore = ($argument['default'] == 'ignore'); // FIXME The directness of this association is questionable $load_objects[$id] = array_shift($args); $context = !empty($load_objects[$id]) ? panels_argument_get_context($argument, $load_objects[$id]) : PANELS_ARG_IS_BAD; if (!is_a($context, 'panels_required_context') && !is_a($context, 'panels_context')) { if ($context & PANELS_ARG_USE_FALLBACK) { if ($panel_page->load_flags & PANELS_HAS_FALLBACK_ROUTER) { $load->fallback = TRUE; break; } else if ($ignore) { continue; } } // TODO Are there other cases? If not, this else is an unnecessary overspecification else if ($context & PANELS_ARG_IS_BAD && $ignore) { continue; } // Prep a 404 and bail out if we get this far. return _panels_page_not_found($load); } $panel_page->context[panels_context_id($argument, 'argument')] = $context; $load->panel_args[] = $context->get_original_argument(); } } function _panels_page_not_found(&$load) { $load->page_callback = 'drupal_not_found'; $load->page_arguments = array(); return FALSE; } /** * Rebuild a drupal menu system-style $map using data passed in to the panels * callback handlers from the menu system. * * @param array $map * A incomplete menu map - it has only $_GET['q'] data initially - that this * function will rebuild. * @param array $load_objects * The set of menu-loader-returned objects provided by the menu system to the * panels callback handlers. * @param array $positions * The positions within the path map (i.e., arg(0), arg(1), etc...) that the * loader objects correspond to. * @return array $map * The rebuilt menu map. */ function _panels_page_rebuild_menu_map($load_objects, $positions) { $map = explode('/', $_GET['q']); foreach ($positions as $key => $position) { $map[$position] = $load_objects[$key]; } return $map; } /** * Prepare the fallback router, update the menu cache with it, then load up * our loader item and initiate fallback. * * Partially lifted from menu_get_item(). * * @param object $load * @param array $map * @return object $load */ function panels_page_prepare_fallback_render(&$load, $map) { $original_map = arg(NULL, $_GET['q']); $default_menu = menu_get_item($_GET['q']); $parts = array_slice($original_map, 0, MENU_MAX_PARTS); list($ancestors, $placeholders) = menu_get_ancestors($parts); if (!$router_item = db_fetch_array(db_query_range('SELECT * FROM {panels_page_router_store} WHERE path IN ('. implode (',', $placeholders) .') ORDER BY fit DESC', $ancestors, 0, 1))) { return _panels_page_not_found($load); } // FIXME calling _menu_translate would result in an incomplete load // $map = _menu_translate($router_item, $original_map); $load->map = $map; $load->page_arguments = array_merge(menu_unserialize($router_item['page_arguments'], $map), array_slice($map, $router_item['number_parts'])); $load->page_callback = $router_item['page_callback']; $load->router_item = $router_item; // If we did not override the default menu callback, stop here. if ($router_item['page_callback'] != $default_menu['page_callback']) { return $load; } // Fix for lack of the _menu_translate() function. We crib some of that function here. _menu_check_access($router_item, $map); // For performance, don't localize an item the user can't access. if ($router_item['access']) { _menu_item_localize($router_item, $map); } menu_set_item($_GET['q'], $router_item); return $load; } /** * Figure out if a panel is the current page; mostly useful in theming. * * This function will return NULL until panels_page_set_current() has been * called and loaded with data. */ function panels_page_get_current() { // Take advantage of our .inc organization to know if it's at all possible // that there's a current page to be retrieved. if (!function_exists('panels_page_set_current')) { return FALSE; } $fubar = NULL; // PHP4 compatibility return panels_page_set_current($fubar); } /** * Theme function to render our panel as a form. * * We need to do this so that the entire display is inside the form. */ function theme_panels_page_render_form($form) { $form['#children'] = panels_render_display($form['#display']); return theme('form', $form); } /** * Wrapper for panels_load_include() that specifically targets panels_page * include files. * * @param string $include * The name of the panels_page include file, without the .inc extension. */ function panels_page_load_include($include) { panels_load_include($include, 'panels_page/panels_page.'); } /** * Implementation of hook_panels_exportables(). */ function panels_page_panels_exportables($op = 'list', $panels = NULL, $name = 'foo') { static $all_panels = NULL; if ($op == 'list') { if (empty($all_panels)) { panels_page_load_include('read'); $all_panels = panels_page_load_all(); } foreach ($all_panels as $name => $panel) { $return[$name] = check_plain($name) . ' (' . check_plain(panels_page_get_title($panel)) . ')'; } return $return; } if ($op == 'export') { panels_page_load_include('write'); $code = "/**\n"; $code .= " * Implementation of hook_default_panel_pages()\n"; $code .= " */\n"; $code .= "function " . $name . "_default_panel_pages() {\n"; foreach ($panels as $panel => $truth) { $code .= panels_page_export($all_panels[$panel], ' '); $code .= ' $pages[\'' . check_plain($panel) . '\'] = $page;' . "\n\n\n"; } $code .= " return \$pages;\n"; $code .= "}\n"; return $code; } } /** * Sanitize a panel with safe, empty values. * * Used by various panels_page CRUD-type functions. */ function panels_page_sanitize($page) { foreach (array('arguments', 'displays', 'contexts', 'relationships', 'switcher_options') as $id) { if (!isset($page->$id) || !is_array($page->$id)) { $page->$id = array(); } } return $page; }