diff --git a/panels.install b/panels.install index 6e83d442978d7c7d1426731e909a33b1bcbde92f..70faae844bf6867db765a93775ddaecabec9cef9 100644 --- a/panels.install +++ b/panels.install @@ -47,7 +47,27 @@ 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_4(); + return panels_schema_5(); +} + +function panels_schema_5() { + $schema = panels_schema_4(); + + $schema['panels_display']['fields']['uuid'] = array( + 'type' => 'char', + 'length' => '36', + ); + $schema['panels_display']['export']['key'] = 'uuid'; + $schema['panels_display']['export']['key name'] = 'UUID'; + + $schema['panels_pane']['fields']['uuid'] = array( + 'type' => 'char', + 'length' => '36', + ); + $schema['panels_pane']['export']['key'] = 'uuid'; + $schema['panels_pane']['export']['key name'] = 'UUID'; + + return $schema; } function panels_schema_4() { @@ -375,3 +395,72 @@ function panels_update_7301() { return t('panels_pane.lock field already existed, update skipped.'); } + +/** + * Adding universally unique identifiers to panels. + */ +function panels_update_7302() { + // Load the schema. + $schema = panels_schema_5(); + $msg = array(); + + // Add the uuid column to the pane table. + $table = 'panels_pane'; + $field = 'uuid'; + // Due to a previous failure, the column may already exist: + if (!db_field_exists($table, $field)) { + $spec = $schema[$table]['fields'][$field]; + db_add_field($table, $field, $spec); + $msg[] = t('Added panels_pane.uuid column.'); + } + + // Add the uuid column to the display table. + $table = 'panels_display'; + $field = 'uuid'; + // Due to a previous failure, the column may already exist: + if (!db_field_exists($table, $field)) { + $spec = $schema[$table]['fields'][$field]; + db_add_field($table, $field, $spec); + $msg[] = t('Added panels_display.uuid column.'); + } + + if (empty($msg)) { + $msg[] = t('UUID column already present in the panels_display & panels_pane tables.'); + } + + // Update all DB-based panes & displays to ensure that they all contain a UUID. + $display_dids = db_select('panels_display') + ->fields('panels_display', array('did')) + ->condition(db_or() + ->condition('uuid', '') + ->isNull('uuid') + ) + ->execute() + ->fetchCol(); + + // Check the panes as well, for paranoia. + $pane_dids = db_select('panels_pane') + ->distinct() + ->fields('panels_pane', array('did')) + ->condition(db_or() + ->condition('uuid', '') + ->isNull('uuid') + ) + ->execute() + ->fetchCol(); + + $dids = array_unique(array_merge($display_dids, $pane_dids)); + + if ($displays = panels_load_displays($dids)) { + foreach ($displays as $display) { + // A display save also triggers pane saves. + panels_save_display($display); + } + $msg[] = t('Generated UUIDs for database-based panel displays and panes.'); + } + else { + $msg[] = t('No database-based panel displays or panes for which to generate UUIDs.'); + } + + return implode("\n", $msg); +} diff --git a/panels.module b/panels.module index 3263732b09e0eaa942c2553c2546e88908dd7ea3..c67557a04b539f35146508d91e9d2fc4dc2bf1b6 100644 --- a/panels.module +++ b/panels.module @@ -655,8 +655,10 @@ class panels_display { $pane->panel = $location; } - // Get a temporary pid for this pane. - $pane->pid = "new-" . $this->next_new_pid(); + // Generate a permanent uuid for this pane, and use + // it as a temporary pid. + $pane->uuid = ctools_uuid_generate(); + $pane->pid = 'new-' . $pane->uuid; // Add the pane to the approprate spots. $this->content[$pane->pid] = &$pane; @@ -670,23 +672,10 @@ class panels_display { function clone_pane($pid) { $pane = clone $this->content[$pid]; + $pane->uuid = ctools_uuid_generate(); return $pane; } - function next_new_pid() { - // We don't use static vars to record the next new pid because - // temporary pids can last for years in exports and in caching - // during editing. - $id = array(0); - foreach (array_keys($this->content) as $pid) { - if (!is_numeric($pid)) { - $id[] = substr($pid, 4); - } - } - $next_id = max($id); - return ++$next_id; - } - /** * Get the title from a display. * @@ -870,10 +859,13 @@ function panels_load_display($did) { * * @ingroup mainapi * - * Note a new $display only receives a real did once it is run through this function. - * Until then, it uses a string placeholder, 'new', in place of a real did. The same - * applies to all new panes (whether on a new $display or not); in addition, - * panes have sequential numbers appended, of the form 'new-1', 'new-2', etc. + * Note that a new $display only receives a real did once it is run through + * this function, and likewise for the pid of any new pane. + * + * Until then, a new display uses a string placeholder, 'new', in place of + * a real did, and a new pane (whether on a new $display or not) appends a + * universally-unique identifier (which is stored permanently in the 'uuid' + * field). This format is also used in place of the real pid for exports. * * @param object $display instanceof panels_display \n * The display object to be saved. Passed by reference so the caller need not use @@ -883,6 +875,9 @@ function panels_load_display($did) { */ function panels_save_display(&$display) { $update = (isset($display->did) && is_numeric($display->did)) ? array('did') : array(); + if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) { + $display->uuid = ctools_uuid_generate(); + } drupal_write_record('panels_display', $display, $update); $pids = array(); @@ -913,8 +908,21 @@ function panels_save_display(&$display) { $pane->did = $display->did; $old_pid = $pane->pid; + + if (empty($pane->uuid) || !ctools_uuid_is_valid($pane->uuid)) { + $pane->uuid = ctools_uuid_generate(); + } + drupal_write_record('panels_pane', $pane, is_numeric($pid) ? array('pid') : array()); + // Allow other modules to take action after a pane is saved. + if ($pane->pid == $old_pid) { + module_invoke_all('panels_pane_update', $pane); + } + else { + module_invoke_all('panels_pane_insert', $pane); + } + if ($pane->pid != $old_pid) { // Remove the old new-* entry from the displays content. unset($display->content[$pid]); @@ -950,6 +958,8 @@ function panels_save_display(&$display) { $display->panels[$id] = $new_panes; } if (!empty($pids)) { + // Allow other modules to take action before a panes are deleted. + module_invoke_all('panels_pane_delete', $pids); db_delete('panels_pane')->condition('pid', $pids)->execute(); } @@ -983,6 +993,7 @@ function panels_delete_display($display) { else { $did = $display; } + module_invoke_all('panels_delete_display', $did); db_delete('panels_display')->condition('did', $did)->execute(); db_delete('panels_pane')->condition('did', $did)->execute(); } @@ -992,9 +1003,11 @@ function panels_delete_display($display) { * * This function is primarily intended as a mechanism for cloning displays. * It generates an exact replica (in code) of the provided $display, with - * the exception that it replaces all ids (dids and pids) with 'new-*' values. - * Only once panels_save_display() is called on the code version of $display will - * the exported display written to the database and permanently saved. + * the exception that it replaces all ids (dids and pids) with place-holder + * values (consisting of the display or pane's uuid, with a 'new-' prefix). + * + * Only once panels_save_display() is called on the code version of $display + * will the exported display be written to the database and permanently saved. * * @see panels_page_export() or _panels_page_fetch_display() for sample implementations. * @@ -1016,10 +1029,12 @@ function panels_delete_display($display) { */ function panels_export_display($display, $prefix = '') { ctools_include('export'); + if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) { + $display->uuid = ctools_uuid_generate(); + } + $display->did = 'new-' . $display->uuid; $output = ctools_export_object('panels_display', $display, $prefix); - $pid_counter = &drupal_static(__FUNCTION__, 0); - // Initialize empty properties. $output .= $prefix . '$display->content = array()' . ";\n"; $output .= $prefix . '$display->panels = array()' . ";\n"; @@ -1029,7 +1044,12 @@ function panels_export_display($display, $prefix = '') { if (!empty($display->content)) { $region_counters = array(); foreach ($display->content as $pane) { - $pid = 'new-' . ++$pid_counter; + + if (empty($pane->uuid) || !ctools_uuid_is_valid($pane->uuid)) { + $pane->uuid = ctools_uuid_generate(); + } + $pid = 'new-' . $pane->uuid; + if ($pane->pid == $display->title_pane) { $title_pid = $pid; } diff --git a/plugins/display_renderers/panels_renderer_standard.class.php b/plugins/display_renderers/panels_renderer_standard.class.php index b2ca1de425704527892458b7979b5fc3657bfb5c..362c558305ab027b36d329f720a199b4cb8eff56 100644 --- a/plugins/display_renderers/panels_renderer_standard.class.php +++ b/plugins/display_renderers/panels_renderer_standard.class.php @@ -489,6 +489,8 @@ class panels_renderer_standard { * A Panels pane object, as loaded from the database. */ function render_pane(&$pane) { + module_invoke_all('panels_pane_prerender', $pane); + $content = $this->render_pane_content($pane); if ($this->display->hide_title == PANELS_TITLE_PANE && !empty($this->display->title_pane) && $this->display->title_pane == $pane->pid) { diff --git a/plugins/task_handlers/panel_context.inc b/plugins/task_handlers/panel_context.inc index b7b9e42bba6f362e9fa80097c9474b6e470f52dc..c3bb07fb3bea73545ec1cf879d72bc2c68e369fa 100644 --- a/plugins/task_handlers/panel_context.inc +++ b/plugins/task_handlers/panel_context.inc @@ -393,7 +393,10 @@ function panels_panel_context_export(&$handler, $indent) { unset($handler->conf[$item]); } } - $display->did = 'new'; + $display = (object) array( + 'did' => 'new', + 'uuid' => ctools_uuid_generate(), + ); $handler->conf['display'] = $display; }