diff --git a/UPGRADE.txt b/UPGRADE.txt index d1a42f2afb77b004b95e7c972135d83e0bebb0e6..05c0c989cd3279e46fbd079b2246fb24a090eb08 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -11,7 +11,7 @@ Upgrading from Panels-6.x-3.x to Panels-7.x-3.x - panels_plugin_get_function() deprecated. - - panels_required_context removed. These were deprecated long ago and + - panels_required_context removed. These were deprecated long ago and existed only to prevent crashes. - panels_optional_context removed. @@ -20,3 +20,13 @@ Upgrading from Panels-6.x-3.x to Panels-7.x-3.x - display_renderer class is now in 'renderer', not 'handler'. +Upgrading task handlers from Panels 7.x-3.5 or older to Panels 7.x-3.6 and newer: + + - You must specify a storage type for any panels display using your custom task handler. + For examples, see panels_update_7306. + + - When creating whatever stores the panel, a storage id and storage type must be defined. + See panels_mini.module for examples inside panels_mini_save and panels_mini_panels_cache_get. + + - A display access plugin must be defined. + See panels_mini/plugins/panels_storage/panels_mini.inc for an example plugin. diff --git a/includes/plugins.inc b/includes/plugins.inc index 00f28c44b81534df6c6fe580b9ebc27f2a6faa5a..be5cd25754a043d9c149955e52b0f2cce408dcf0 100644 --- a/includes/plugins.inc +++ b/includes/plugins.inc @@ -478,6 +478,31 @@ function panels_get_renderer_pipelines($sort = TRUE) { return $pipelines; } +/** + * Fetch metadata on a specific panels_storage plugin. + * + * @param $storage + * Name of a panel_storage plugin. + * + * @return + * An array with information about the requested panels_storage plugin + */ +function panels_get_panels_storage_plugin($storage) { + ctools_include('plugins'); + return ctools_get_plugins('panels', 'panels_storage', $storage); +} + +/** + * Fetch metadata for all panels_storage plugins. + * + * @return + * An array of arrays with information about all available panels_storage plugins. + */ +function panels_get_panels_storage_plugins() { + ctools_include('plugins'); + return ctools_get_plugins('panels', 'panels_storage'); +} + /** * Get a function from a plugin, if it exists. * diff --git a/panels.install b/panels.install index 7a9e7f75d326ec866dfc7bc1d9be44fd35816033..ebb93e04143801be54433dbc018a1b13e47c23fc 100644 --- a/panels.install +++ b/panels.install @@ -47,7 +47,25 @@ function panels_requirements_install() { function panels_schema() { // This should always point to our 'current' schema. This makes it relatively // easy to keep a record of schema as we make changes to it. - return panels_schema_7(); + return panels_schema_8(); +} + +function panels_schema_8() { + $schema = panels_schema_7(); + + // Add the storage type and id columns. + $schema['panels_display']['fields']['storage_type'] = array( + 'type' => 'varchar', + 'length' => 255, + 'default' => '', + ); + $schema['panels_display']['fields']['storage_id'] = array( + 'type' => 'varchar', + 'length' => 255, + 'default' => '', + ); + + return $schema; } function panels_schema_7() { @@ -520,3 +538,53 @@ function panels_update_7304() { } } } + +/** + * Add the "storage_type" and "storage_id" columns to "panels_display". + */ +function panels_update_7305() { + $schema = panels_schema_8(); + + $new_fields = array( + 'panels_display' => array('storage_type', 'storage_id'), + ); + + foreach ($new_fields as $table => $fields) { + foreach ($fields as $field_name) { + db_add_field($table, $field_name, $schema[$table]['fields'][$field_name]); + } + } +} + +/** + * Set the storage type and id on existing page manager panels displays. + */ +function panels_update_7306() { + if (!db_table_exists('page_manager_handlers')) { + return t('Skipping update - page_manager is not installed.'); + } + + // Get all page_manager_handlers that have a panels context. + $result = db_query("SELECT pm.name, pm.conf FROM {page_manager_handlers} pm WHERE pm.handler = 'panel_context'"); + $page_manager_panels = array(); + foreach ($result as $row) { + $conf = unserialize($row->conf); + if (isset($conf['did'])) { + $page_manager_panels[$conf['did']] = $row->name; + } + } + + if (!empty($page_manager_panels)) { + // Check panels displays that only have empty storage types + $result = db_query("SELECT pd.did FROM {panels_display} pd WHERE pd.did IN (:dids) AND storage_type = ''", array(':dids' => array_keys($page_manager_panels))); + foreach ($result as $row) { + db_update('panels_display') + ->fields(array( + 'storage_type' => 'page_manager', + 'storage_id' => $page_manager_panels[$row->did], + )) + ->condition('did', $row->did) + ->execute(); + } + } +} diff --git a/panels.module b/panels.module index 019315363ebd92432b0206d8409740e20d11ee2d..45ca03143d21d5164cbd1d4867747284197323c8 100644 --- a/panels.module +++ b/panels.module @@ -409,6 +409,7 @@ function panels_ctools_plugin_type() { 'display_renderers' => array( 'classes' => array('renderer'), ), + 'panels_storage' => array(), ); } @@ -775,6 +776,55 @@ class panels_display { } return $output; } + + /** + * Determine if the given user can perform the requested operation. + * + * @param string $op + * An operation like: create, read, update, or delete. + * @param object $account + * (optional) The account to check access for. + * + * @return bool + * TRUE if access is granted; otherwise FALSE. + */ + function access($op, $account = NULL) { + global $user; + + if (!$account) { + $account = $user; + } + + // Even administrators need to go through the access system. However, to + // support legacy plugins, user 1 gets full access no matter what. + if ($account->uid == 1) { + return TRUE; + } + + if (!in_array($op, array('create', 'read', 'update', 'delete', 'change layout'))) { + return FALSE; + } + + if (empty($this->storage_type) || empty($this->storage_id)) { + return FALSE; + } + + if ($this->storage_type == 'unknown') { + return FALSE; + } + + $storage_plugin = panels_get_panels_storage_plugin($this->storage_type); + if (!$storage_plugin) { + return FALSE; + } + + $access_callback = panels_plugin_get_function('panels_storage', $storage_plugin, 'access callback'); + if (!$access_callback) { + return FALSE; + } + + return $access_callback($this->storage_type, $this->storage_id, $op, $account); + } } /** @@ -1415,6 +1465,11 @@ function panels_ajax_router() { ctools_include('cleanstring'); $renderer->clean_key = ctools_cleanstring($cache_key); + $op = $renderer->get_panels_storage_op_for_ajax($method); + if (!$cache->display->access($op)) { + return MENU_ACCESS_DENIED; + } + $output = call_user_func_array(array($renderer, $method), $args); if (empty($output) && !empty($renderer->commands)) { @@ -1726,6 +1781,38 @@ function panels_page_wizard_panels_cache_set($key, $cache) { page_manager_set_wizard_cache($wizard_cache); } +/** + * Alter the page wizard basic page, when panels is selected, to inject page + * manager as the storage plugin for panels. + * @param $form + * @param $form_state + */ +function panels_form_page_manager_page_form_basic_alter(&$form, &$form_state) { + $form['#validate'][] = 'panels_page_manager_handler_add_validate'; +} + +/** + * Alter the variant add page, so when panels is selected, page manager is the + * storage plugin for panels. + * @param $form + * @param $form_state + */ +function panels_form_page_manager_handler_add_alter(&$form, &$form_state) { + $form['#validate'][] = 'panels_page_manager_handler_add_validate'; +} + +/** + * Perform the validation check to see if panel context is selected to use + * page manager as the storage plugin. + * @param $form + * @param $form_state + */ +function panels_page_manager_handler_add_validate($form, &$form_state) { + if($form_state['values']['handler'] == 'panel_context') { + $form_state['page']->storage_type = 'page_manager'; + } +} + // -------------------------------------------------------------------------- // General utility functions diff --git a/panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php b/panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php index abaf6dadf91d165f6077e97d36bc6bbffee5efe8..0d7198fa4ce3ca119441f776eb73cca5cf5c594d 100644 --- a/panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php +++ b/panels_ipe/plugins/display_renderers/panels_renderer_ipe.class.php @@ -259,6 +259,19 @@ class panels_renderer_ipe extends panels_renderer_editor { // Break the lock. panels_edit_cache_break_lock($this->cache); } + } + + function get_panels_storage_op_for_ajax($method) { + switch ($method) { + case 'ajax_unlock_ipe': + case 'ajax_save_form': + return 'update'; + case 'ajax_change_layout': + case 'ajax_set_layout': + return 'change layout'; + } + + return parent::get_panels_storage_op_for_ajax($method); } /** diff --git a/panels_mini/panels_mini.install b/panels_mini/panels_mini.install index 235e88e10400fb9e946c97fcf0fcb1cfa31b6324..b3239ddea620cbb758af1bf22deb067ea954cf1a 100644 --- a/panels_mini/panels_mini.install +++ b/panels_mini/panels_mini.install @@ -122,3 +122,61 @@ function panels_mini_uninstall() { ->execute(); } } + +/** + * Implements hook_update_dependencies(). + */ +function panels_mini_update_dependencies() { + // Update 7301 requires panels storage support + $dependencies['panels_mini'][7301] = array( + 'panels' => 7305, + ); + + return $dependencies; +} + +/** + * Set the storage type and id on existing mini panels. + */ +function panels_mini_update_7301() { + if (!isset($sandbox['progress'])) { + // Initialize batch update information. + $sandbox['progress'] = (float)0; + $sandbox['current_did'] = -1; + $sandbox['max'] = db_query("SELECT COUNT(pd.did) + FROM {panels_display} pd + JOIN {panels_mini} pm ON pm.did = pd.did + WHERE pd.storage_type = ''")->fetchField(); + } + + // Set a limit of how many rows to process per batch. + $limit = 1000; + + // Run the query + $result = db_query_range("SELECT pd.did, pm.name + FROM {panels_display} pd + JOIN {panels_mini} pm ON pm.did = pd.did + WHERE pd.storage_type = '' AND pd.did > :current_did", 0, $limit, array(':current_did' => $sandbox['current_did'])); + + foreach ($result as $row) { + db_update('panels_display') + ->fields(array( + 'storage_type' => 'panels_mini', + 'storage_id' => $row->name, + )) + ->condition('did', $row->did) + ->execute(); + + // Update our progress information. + $sandbox['progress']++; + $sandbox['current_did'] = $row->did; + } + + // Set the "finished" status, to tell batch engine whether this function + // needs to run again. + $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']); + + if ($sandbox['#finished']) { + return t('Added the storage type for panels_mini to relevant panels displays'); + } +} diff --git a/panels_mini/panels_mini.module b/panels_mini/panels_mini.module index 4820f21a2f67e7b4ee2c2928f0f45b50de7379fb..e55c673655068cf6cbad068f3ee3a384b1f17aa7 100644 --- a/panels_mini/panels_mini.module +++ b/panels_mini/panels_mini.module @@ -365,6 +365,7 @@ function panels_mini_load_all($reset = FALSE) { */ function panels_mini_save(&$mini) { if (!empty($mini->display)) { + $mini->display->storage_id = $mini->name; $display = panels_save_display($mini->display); $mini->did = $display->did; } @@ -424,6 +425,9 @@ function panels_mini_ctools_plugin_directory($module, $plugin) { if ($module == 'ctools' && ($plugin == 'content_types' || $plugin == 'export_ui')) { return 'plugins/' . $plugin; } + if ($module == 'panels' && $plugin == 'panels_storage') { + return 'plugins/' . $plugin; + } } /** @@ -462,6 +466,9 @@ function panels_mini_panels_cache_get($key) { $cache->display = $item->display; $cache->display->context = ctools_context_load_contexts($item); $cache->display->cache_key = 'panels_mini:' . $key; + $cache->display->storage_type = 'panels_mini'; + // Temporary storage id that's replaced in panels_mini_save(). + $cache->display->storage_id = 'panels_mini'; $cache->content_types = panels_common_get_allowed_types('panels_mini', $cache->display->context); $cache->display_title = TRUE; diff --git a/panels_mini/plugins/panels_storage/panels_mini.inc b/panels_mini/plugins/panels_storage/panels_mini.inc new file mode 100644 index 0000000000000000000000000000000000000000..c9ba35d7172a1dc8f26dfebba0dc513bb98929b7 --- /dev/null +++ b/panels_mini/plugins/panels_storage/panels_mini.inc @@ -0,0 +1,22 @@ + 'panels_mini_panels_storage_access', +); + +/** + * Access callback for panels storage. + */ +function panels_mini_panels_storage_access($storage_type, $storage_id, $op, $account) { + if ($op == 'create') { + return user_access('create mini panels', $account); + } + + return user_access('administer mini panels', $account); +} diff --git a/panels_node/panels_node.install b/panels_node/panels_node.install index f1b24f96d1a09164ff7bc764378beda4e915d01c..94f7eb4d9170770dabcd9b23ed28d49c5b7bf635 100644 --- a/panels_node/panels_node.install +++ b/panels_node/panels_node.install @@ -56,6 +56,18 @@ function panels_node_uninstall() { drupal_uninstall_schema('panels_node'); } +/** + * Implements hook_update_dependencies(). + */ +function panels_node_update_dependencies() { + // Update 7301 requires panels storage support + $dependencies['panels_node'][7301] = array( + 'panels' => 7305, + ); + + return $dependencies; +} + /** * Implementation of hook_update to handle adding a pipeline */ @@ -73,7 +85,7 @@ function panels_node_update_6001() { /** * Migrate legacy Drupal 6 permissions to Drupal 7. */ -function panels_node_update_6002() { +function panels_node_update_7301() { $permissions = array( 'create panel-nodes' => 'create panel content', 'edit any panel-nodes' => 'edit any panel content', @@ -107,3 +119,49 @@ function panels_node_update_6002() { ->execute(); } } + +/* + * Set the storage type and id on existing panels nodes. + */ +function panels_node_update_7302() { + if (!isset($sandbox['progress'])) { + // Initialize batch update information. + $sandbox['progress'] = (float)0; + $sandbox['current_did'] = -1; + $sandbox['max'] = db_query("SELECT COUNT(pd.did) + FROM {panels_display} pd + JOIN {panels_node} pn ON pn.did = pd.did + WHERE pd.storage_type = ''")->fetchField(); + } + + // Set a limit of how many rows to process per batch. + $limit = 1000; + + // Run the query + $result = db_query_range("SELECT pd.did, pn.nid + FROM {panels_display} pd + JOIN {panels_node} pn ON pn.did = pd.did + WHERE pd.storage_type = '' AND pd.did > :current_did", 0, $limit, array(':current_did' => $sandbox['current_did'])); + + foreach ($result as $row) { + db_update('panels_display') + ->fields(array( + 'storage_type' => 'panels_node', + 'storage_id' => $row->nid, + )) + ->condition('did', $row->did) + ->execute(); + + // Update our progress information. + $sandbox['progress']++; + $sandbox['current_did'] = $row->did; + } + + // Set the "finished" status, to tell batch engine whether this function + // needs to run again. + $sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']); + + if ($sandbox['#finished']) { + return t('Added the storage type for panels_node to relevant panels displays'); + } +} diff --git a/panels_node/panels_node.module b/panels_node/panels_node.module index bcdb6709fdf720b34917d9176e76fbcdd36b91bc..4b33e7f36b6ec9b94c68713131bae46da9109d7a 100644 --- a/panels_node/panels_node.module +++ b/panels_node/panels_node.module @@ -24,6 +24,15 @@ function panels_node_permission() { ); } +/** + * Implementation of hook_ctools_plugin_directory(). + */ +function panels_node_ctools_plugin_directory($module, $plugin) { + if ($module == 'panels' && $plugin == 'panels_storage') { + return 'plugins/' . $plugin; + } +} + /** * Implementation of hook_menu(). */ @@ -236,6 +245,8 @@ function panels_node_hook_insert(&$node) { // Create a new display and record that. $display = panels_new_display(); $display->layout = $node->panels_node['layout']; + $display->storage_type = 'panels_node'; + $display->storage_id = $node->nid; // Special handling for nodes being imported from an export.module data dump. if (!empty($node->export_display)) { diff --git a/panels_node/plugins/panels_storage/panels_node.inc b/panels_node/plugins/panels_storage/panels_node.inc new file mode 100644 index 0000000000000000000000000000000000000000..a7a7a3b2e40bc11178ba72e78ad2b10b62861465 --- /dev/null +++ b/panels_node/plugins/panels_storage/panels_node.inc @@ -0,0 +1,25 @@ + 'panels_node_panels_storage_access', +); + +/** + * Access callback for panels storage. + */ +function panels_node_panels_storage_access($storage_type, $storage_id, $op, $account) { + if ($node = node_load($storage_id)) { + if ($op == 'read') { + $op = 'view'; + } + return node_access($op, $node, $account); + } + + return FALSE; +} diff --git a/plugins/display_renderers/panels_renderer_editor.class.php b/plugins/display_renderers/panels_renderer_editor.class.php index f6f0b1646c265313185844f568f6734aeb5fc4b2..d8947dc5e7d9e7e91b48eef7321807a7e522cb78 100644 --- a/plugins/display_renderers/panels_renderer_editor.class.php +++ b/plugins/display_renderers/panels_renderer_editor.class.php @@ -514,6 +514,40 @@ class panels_renderer_editor extends panels_renderer_standard { return $url; } + /** + * Get the Panels storage oparation for a given renderer AJAX method. + * + * @param string $method + * The method name. + * + * @return string + * The Panels storage op. + */ + function get_panels_storage_op_for_ajax($method) { + switch ($method) { + case 'ajax_show': + case 'ajax_hide': + case 'ajax_select_content': + case 'ajax_add_pane': + case 'ajax_edit_pane': + case 'ajax_panel_title': + case 'ajax_cache_method': + case 'ajax_cache_settings': + case 'ajax_style_type': + case 'ajax_style_settings': + case 'ajax_pane_css': + case 'ajax_lock': + case 'ajax_access_settings': + case 'ajax_access_add_test': + case 'ajax_access_configure_test': + case 'ajax_layout': + case 'ajax_style': + return 'update'; + } + + return parent::get_panels_storage_op($method); + } + /** * AJAX command to show a pane. */ diff --git a/plugins/display_renderers/panels_renderer_standard.class.php b/plugins/display_renderers/panels_renderer_standard.class.php index 5f134a98193219921f1c8c75804f5924bd74fa08..b97153f2797ae97095347421a07de2886b288dbc 100644 --- a/plugins/display_renderers/panels_renderer_standard.class.php +++ b/plugins/display_renderers/panels_renderer_standard.class.php @@ -185,6 +185,19 @@ class panels_renderer_standard { } } + /** + * Get the Panels storage oparation for a given renderer AJAX method. + * + * @param string $method + * The method name. + * + * @return string + * The Panels storage op. + */ + function get_panels_storage_op_for_ajax($method) { + return 'read'; + } + /** * Prepare the attached display for rendering. * diff --git a/plugins/panels_storage/page_manager.inc b/plugins/panels_storage/page_manager.inc new file mode 100644 index 0000000000000000000000000000000000000000..d543600b91646ec8140bee9bebc84a2619223b73 --- /dev/null +++ b/plugins/panels_storage/page_manager.inc @@ -0,0 +1,19 @@ + 'page_manager_panels_storage_access', +); + +/** + * Access callback for panels storage. + */ +function page_manager_panels_storage_access($storage_type, $storage_id, $op, $account) { + // Only users with the 'use page manager' or administer page manager perms. + return user_access('use page manager', $account) || user_access('administer page manager', $account); +} diff --git a/plugins/task_handlers/panel_context.inc b/plugins/task_handlers/panel_context.inc index 2c65518d2f38601ff4a6f5556e673d77a4c80b07..b7f4780e6753cae4cd26ca6a80febf166dea7d2b 100644 --- a/plugins/task_handlers/panel_context.inc +++ b/plugins/task_handlers/panel_context.inc @@ -586,6 +586,19 @@ function panels_panel_context_edit_choose($form, &$form_state) { $form_state['display'] = &panels_panel_context_get_display($form_state['handler']); + // Grab the storage_type and storage_id and inject it into the display. + if (empty($form_state['display']->storage_type)) { + if (!isset($form_state[$form_state['task_id']]->storage_type)) { + watchdog('panels', "Unable to find the storage type for specified storage. Read 'Upgrading task handlers' in CHANGELOG.txt", array(), WATCHDOG_ERROR); + $form_state['display']->storage_type = 'unknown'; + } + else { + $form_state['display']->storage_type = $form_state[$form_state['task_id']]->storage_type; + } + // When adding variants, we don't know the handler id yet. In that case, + // Mark it as new. We'll assign it later. + $form_state['display']->storage_id = !empty($form_state['handler_id']) ? $form_state['handler_id'] : 'new'; + } // Tell the Panels form not to display buttons. $form_state['no buttons'] = TRUE; @@ -743,6 +756,10 @@ function panels_panel_context_edit_content_validate(&$form, &$form_state) { } function panels_panel_context_edit_content_submit(&$form, &$form_state) { + // Update the storage_id if this is a new variant before saving. + if ($form_state['display']->storage_id == 'new') { + $form_state['display']->storage_id = $form_state['handler_id']; + } panels_edit_display_form_submit($form, $form_state); $handler = &$form_state['handler'];