diff --git a/panels.install b/panels.install index 6e83d44..67e7acf 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,61 @@ function panels_update_7301() { return t('panels_pane.lock field already existed, update skipped.'); } + +/** + * Adding UUIDs to panels. + */ +function panels_update_7302() { + // Load the schema. + $schema = panels_schema_5(); + $msg = array(); + + // Add the uuid field to the pane table. + $table = 'panels_pane'; + $field = 'uuid'; + // Due to a previous failure, the field may already exist: + if (!db_field_exists($table, $field)) { + $spec = $schema[$table]['fields'][$field]; + + // Re-define the column. + db_add_field($table, $field, $spec); + $msg[] = t('Added panels_pane.uuid field.'); + } + + // Add the uuid field to the display table. + $table = 'panels_display'; + $field = 'uuid'; + // Due to a previous failure, the field may already exist: + if (!db_field_exists($table, $field)) { + $spec = $schema[$table]['fields'][$field]; + + // Re-define the column. + db_add_field($table, $field, $spec); + $msg[] = t('Added panels_display.uuid field.'); + } + + if (empty($msg)) { + return t('Panels uuid support already available, update skipped.'); + } + else { + return implode("\n", $msg); + } + + // Update all DB-based panes & displays to contain an uuid. + $dids = db_select('panels_display') + ->fields('panels_display', array('did')) + ->condition(db_or() + ->condition('uuid', '') + ->isNull('uuid') + ) + ->execute() + ->fetchCol(); + $displays = panels_load_displays($dids); + foreach ($displays as $display) { + // A display save also triggers pane saves. + panels_save_display($display); + } + if (count($displays)) { + return t('Added uuids to displays and panes stored in the db.'); + } +} diff --git a/panels.module b/panels.module index 5a78b8c..207ff45 100644 --- a/panels.module +++ b/panels.module @@ -652,8 +652,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; @@ -667,23 +669,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. * @@ -867,10 +856,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 @@ -880,6 +872,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(); @@ -910,6 +905,11 @@ 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()); if ($pane->pid != $old_pid) { @@ -987,9 +987,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. * @@ -1011,10 +1013,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"; @@ -1024,7 +1028,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/task_handlers/panel_context.inc b/plugins/task_handlers/panel_context.inc index 3d3f308..f0171bc 100644 --- a/plugins/task_handlers/panel_context.inc +++ b/plugins/task_handlers/panel_context.inc @@ -386,7 +386,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; }