? 433656-export-cvs.patch ? 433656-export.patch ? tw_exportable.patch Index: tw.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/tw/tw.module,v retrieving revision 1.1.2.32 diff -u -p -r1.1.2.32 tw.module --- tw.module 18 May 2009 02:42:23 -0000 1.1.2.32 +++ tw.module 18 May 2009 18:39:37 -0000 @@ -465,6 +465,13 @@ function tw_menu() { 'type' => MENU_CALLBACK, 'file' => 'tw_pages.inc', ); + $items['admin/content/tw/export'] = array( + 'title' => 'Code for hook_views_data()', + 'page callback' => 'tw_export', + 'access arguments' => array(TW_ACCESS), + 'type' => MENU_CALLBACK, + 'file' => 'tw_pages.inc', + ); return $items; } Index: tw.views.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/tw/tw.views.inc,v retrieving revision 1.1.2.13 diff -u -p -r1.1.2.13 tw.views.inc --- tw.views.inc 3 May 2009 18:51:00 -0000 1.1.2.13 +++ tw.views.inc 18 May 2009 18:39:37 -0000 @@ -6,140 +6,15 @@ * Views hooks */ -/** - * Implementation of hook_views_data() - */ -function tw_views_data() { - $tables = array(); - - // Create table definitions for each import table - $sql = "SELECT * FROM {tw_tables}"; - $tblresult = db_query($sql); - while ($tblrow = db_fetch_object($tblresult)) { - // Table Wizard stores the true DB table name - Drupal knows the name without - // the table prefix (if there is one). We'll use the true name for display - $tablename = $tblrow->tablename; - $dbconnection = $tblrow->dbconnection; - if ($dbconnection == 'default') { - $rawtablename = schema_unprefix_table($tablename); - $disptablename = $tablename; - } - else { - $rawtablename = $tablename; - $disptablename = $dbconnection . '.' . $tablename; - } - $twtid = $tblrow->twtid; - $table = array(); - // Add each viewable column to the table definition - // Note that we include the primary key column even if it's empty - this means - // the table is totally empty, and we need to have at least one column present - // to prevent errors in the view - $sql = "SELECT twcid, colname, primarykey, secure, coltype - FROM {tw_columns} - WHERE twtid=%d AND ignorecol=0 - ORDER BY weight"; - $colresult = db_query($sql, $twtid); - $pk = array(); - while ($colrow = db_fetch_object($colresult)) { - $colname = $colrow->colname; - $table[$colname] = array( - 'title' => $colname, - 'help' => $colname, - // TODO: Truncate text at 80 characters. - // TODO: Better yet, do some jQuery magic to expand to the full text - 'field' => array( - 'handler' => 'views_handler_field', - 'click sortable' => TRUE, - ), - 'filter' => array( - 'handler' => _tw_views_handler_type($colrow->coltype), - 'allow empty' => TRUE, - ), - // TODO: Add support for arguments - //'argument' - 'sort' => array( - 'handler' => 'views_handler_sort' - ), - ); - if ($colrow->primarykey) { - $pk[] = $colname; - } - } - // Any table with a single primary key column can be a base table - if (count($pk) == 1) { - $table['table'] = array( - 'group' => t($disptablename), - 'base' => array( - 'field' => $pk[0], - 'title' => t('Database table @tablename', array('@tablename' => $disptablename)), - 'help' => t('Table managed by the Table Wizard'), - 'weight' => 10, - 'database' => $dbconnection, - ), - ); - } - $tables[$rawtablename] = $table; - } - - // Now that all tables are present, fill in relationships defined by foreign keys - $sql = "SELECT twt1.tablename tbl1, twc1.colname col1, twt2.tablename tbl2, twc2.colname col2 - FROM {tw_relationships} twr - INNER JOIN {tw_columns} twc1 ON twr.leftcol=twc1.twcid - INNER JOIN {tw_tables} twt1 ON twc1.twtid=twt1.twtid - INNER JOIN {tw_columns} twc2 ON twr.rightcol=twc2.twcid - INNER JOIN {tw_tables} twt2 ON twc2.twtid=twt2.twtid - ORDER BY tbl1, col1, tbl2, col2"; - $result = db_query($sql); - - // To allow multiple joins from one tbl/col, must use an alias and - // 'relationship field' for the left side - $i = 0; - while ($row = db_fetch_array($result)) { - extract($row); - $rawtbl1 = schema_unprefix_table($tbl1); - $rawtbl2 = schema_unprefix_table($tbl2); - if (!isset($tables[$rawtbl1][$col1]['relationship'])) { - $tables[$rawtbl1][$col1]['title'] = t("$col1 (joins to $tbl2)"); - $tables[$rawtbl1][$col1]['relationship'] = array( - 'base' => $rawtbl2, - 'base field' => $col2, - 'label' => t("Join $tbl1 to $tbl2"), - ); - } - else { - $i++; - $mungedcol = $col1 . '_' . $i; - $tables[$rawtbl1][$mungedcol] = $tables[$rawtbl1][$col1]; - $tables[$rawtbl1][$mungedcol]['title'] = t("$col1 (joins to $tbl2)"); - $tables[$rawtbl1][$mungedcol]['real field'] = $col1; - $tables[$rawtbl1][$mungedcol]['relationship'] = array( - 'base' => $rawtbl2, - 'base field' => $col2, - 'relationship field' => $col1, - 'label' => t("Join $tbl1 to $tbl2"), - ); - } - } - return $tables; -} +require_once drupal_get_path('module', 'tw') . '/tw_tablebuild.inc'; /** - * Return a views handler based on the column type. + * Implementation of hook_views_data(). + * + * Utilizes shared table building functions to generate the full views data + * array. */ -function _tw_views_handler_type($sqltype) { - preg_match('/^[a-zA-Z]+/', $sqltype, $matches); - $type = tw_column_type($matches[0]); - switch ($type) { - case 'numeric': - $filter = 'views_handler_filter_numeric'; - break; - case 'datetime': - $filter = 'views_handler_filter_date'; - break; - default: - $filter = 'views_handler_filter_string'; - break; - } - - return $filter; +function tw_views_data() { + $tables = _tw_generate_views_table_data(); + return _tw_generate_views_columnar_data($tables); } Index: tw_pages.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/tw/tw_pages.inc,v retrieving revision 1.1.2.36 diff -u -p -r1.1.2.36 tw_pages.inc --- tw_pages.inc 18 May 2009 02:43:17 -0000 1.1.2.36 +++ tw_pages.inc 18 May 2009 18:39:38 -0000 @@ -48,10 +48,10 @@ function tw_sources_form($form_state) { '#type' => 'value', '#value' => array( array('data' => '', 'class' => 'select-all'), - array('data' => ''), $connheader, $nameheader, array('data' => t('Row count')), + array('data' => t('Table Analysis')), ), ); } @@ -59,10 +59,10 @@ function tw_sources_form($form_state) { $form['header'] = array( '#type' => 'value', '#value' => array( - array('data' => t('Delete'), 'class' => 'select-all'), - array('data' => ''), + array('data' => t('Select'), 'class' => 'select-all'), $nameheader, array('data' => t('Row count')), + array('data' => t('Table Analysis')), ), ); } @@ -86,7 +86,7 @@ function tw_sources_form($form_state) { $last_connection = 'default'; } $form['analyze'][$row->twtid] = array('#value' => - l(t('Analysis'), 'admin/content/tw/analyze/' . $row->twtid, array('html' => TRUE))); + l(t('analyze'), 'admin/content/tw/analyze/' . $row->twtid, array('html' => TRUE))); if ($extconns >= 1) { $form['dbconnection'][$row->twtid] = array('#value' => $row->dbconnection); } @@ -120,6 +120,10 @@ function tw_sources_form($form_state) { '#type' => 'submit', '#value' => t('Remove selected tables'), ); + $form['export'] = array( + '#type' => 'submit', + '#value' => t('Export selected tables'), + ); // Keep each type's fields apart $form['#tree'] = TRUE; @@ -142,12 +146,12 @@ function theme_tw_sources($form) { // Don't show the table id $null = drupal_render($form['twtid']); $row[] = drupal_render($form['checks'][$twtid]); - $row[] = drupal_render($form['analyze'][$twtid]); if (isset($form['dbconnection'][$twtid])) { $row[] = drupal_render($form['dbconnection'][$twtid]); } $row[] = drupal_render($form['tablename'][$twtid]); $row[] = drupal_render($form['rowcount'][$twtid]); + $row[] = drupal_render($form['analyze'][$twtid]); $rows[] = $row; } } @@ -158,6 +162,7 @@ function theme_tw_sources($form) { } $output .= theme('table', $header, $rows); $output .= drupal_render($form['delete']); + $output .= drupal_render($form['export']); $output .= drupal_render($form); return $output; } @@ -177,7 +182,11 @@ function tw_sources_form_submit($form, & array('%tablename' => $row->tablename, '%connection' => $row->dbconnection))); } } - } + } + elseif ($type == 'export') { + $twtids = array_values(array_filter($form_state['values']['checks'])); + $form_state['redirect'] = 'admin/content/tw/export/' . implode(',', $twtids); + } else { // Submit hooks return arrays of tablenames they're bringing in $values = $form_state['values'][$type]; @@ -815,3 +824,109 @@ function tw_relationships_form_submit($f } return; } + +function tw_export($twtids = array()) { + require_once drupal_get_path('module', 'tw') . '/tw_tablebuild.inc'; + if (empty($twtids)) { + return drupal_access_denied(); + } + $twtids = explode(',', check_plain($twtids)); + foreach ($twtids as $twtid) { + if (!is_numeric($twtid)) { + // Crude but effecive security measure + return drupal_not_found(); + } + } + return drupal_get_form('tw_export_form', $twtids); +} + +function tw_export_form(&$form_state, $twtids) { + $tables = _tw_generate_views_table_data($twtids, TRUE); + $tables = _tw_generate_views_columnar_data($tables, $twtids, TRUE); + $code = tw_data_export($tables); + $lines = substr_count($code, "\n"); + $form['code'] = array( + '#type' => 'textarea', + '#title' => t('Table Wizard generated code for hook_views_data() implementation'), + '#default_value' => $code, + '#rows' => $lines + 1, + ); + return $form; +} + +function tw_data_export($tables) { + $output = ' $data = array();' . "\n"; + foreach ($tables as $table => $data) { + $output .= ' $data[\'' . $table . '\'] = '; + $output .= _tw_data_export($data, ' '); + $output .= ";\n"; + } + $output .= ' return $data;'; + return $output; +} + +function _tw_data_export($var, $prefix = '') { + if (is_array($var)) { + if (empty($var)) { + $output = 'array()'; + } + else { + $output = "array(\n"; + foreach ($var as $key => $value) { + $output .= " '$key' => " . _tw_data_export($value, ' ') . ",\n"; + } + $output .= ')'; + } + } + else if (is_bool($var)) { + $output = $var ? 'TRUE' : 'FALSE'; + } + // Special case for the t() function. + else if (substr($var, 0, 2) == 't(') { + $output = $var; + } + else { + $output = var_export($var, TRUE); + } + + if ($prefix) { + $output = str_replace("\n", "\n$prefix", $output); + } + + return $output; +} + +/** + * Wraps strings that would normally be immediately translated via t() with the + * t() call as a string, in a format appropriate for export. + * + * @param string $string + * @param array $args + * @return string + */ +function _tw_t_wrap($string, $args = array()) { + if (empty($args)) { + return "t('$string')"; + } + else { + // Transform arguments before inserting them. + foreach ($args as $key => $value) { + switch ($key[0]) { + case '@': + // Escaped only. + $args[$key] = check_plain($value); + break; + + case '%': + default: + // Escaped and placeholder. + $args[$key] = theme('placeholder', $value); + break; + + case '!': + // Pass-through. + } + } + return "t('". strtr($string, $args) ."')"; + } +} Index: tw_tablebuild.inc =================================================================== RCS file: tw_tablebuild.inc diff -N tw_tablebuild.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ tw_tablebuild.inc 18 May 2009 18:39:38 -0000 @@ -0,0 +1,164 @@ +tablename; + // Load the schema so we can get table comments. + $schema = drupal_get_schema($tablename); + $dbconnection = $tblrow->dbconnection; + if ($dbconnection == 'default') { + $rawtablename = schema_unprefix_table($tablename); + $disptablename = $tablename; + } + else { + $rawtablename = $tablename; + $disptablename = $dbconnection . '.' . $tablename; + } + $twtid = $tblrow->twtid; + $table = array(); + // Add each viewable column to the table definition + // Note that we include the primary key column even if it's empty - this means + // the table is totally empty, and we need to have at least one column present + // to prevent errors in the view + $sql = "SELECT twcid, colname, primarykey, secure, coltype + FROM {tw_columns} + WHERE twtid=%d AND ignorecol=0 + ORDER BY weight"; + $colresult = db_query($sql, $twtid); + $pk = array(); + while ($colrow = db_fetch_object($colresult)) { + $colname = $colrow->colname; + $table[$colname] = array( + 'title' => $t($colname), + 'help' => $t($schema['fields'][$colname]['description']), + // TODO: Truncate text at 80 characters. + // TODO: Better yet, do some jQuery magic to expand to the full text + 'field' => array( + 'handler' => 'views_handler_field', + 'click sortable' => TRUE, + ), + 'filter' => array( + 'handler' => _tw_views_handler_type($colrow->coltype), + 'allow empty' => TRUE, + ), + // TODO: Add support for arguments + //'argument' + 'sort' => array( + 'handler' => 'views_handler_sort' + ), + ); + if ($colrow->primarykey) { + $pk[] = $colname; + } + } + // Any table with a single primary key column can be a base table + if (count($pk) == 1) { + $table['table'] = array( + 'group' => $t($disptablename), + 'base' => array( + 'field' => $pk[0], + 'title' => $t('Database table @tablename', array('@tablename' => $disptablename)), + 'help' => (!empty($schema['description'])) ? $t($schema['description']) : $help, + 'weight' => 10, + 'database' => $dbconnection, + ), + ); + } + $tables[$rawtablename] = $table; + } + return $tables; +} + +function _tw_generate_views_columnar_data($tables, $twtids = NULL, $export = FALSE) { + $t = $export ? '_tw_t_wrap' : 't'; + $where = is_null($twtids) ? '' : 'WHERE twt1.twtid IN (' . db_placeholders($twtids) . ') '; + // Now that all tables are present, fill in relationships defined by foreign keys + $sql = "SELECT twt1.tablename tbl1, twc1.colname col1, twt2.tablename tbl2, twc2.colname col2 + FROM {tw_relationships} twr + INNER JOIN {tw_columns} twc1 ON twr.leftcol=twc1.twcid + INNER JOIN {tw_tables} twt1 ON twc1.twtid=twt1.twtid + INNER JOIN {tw_columns} twc2 ON twr.rightcol=twc2.twcid + INNER JOIN {tw_tables} twt2 ON twc2.twtid=twt2.twtid + {$where}ORDER BY tbl1, col1, tbl2, col2"; + $result = is_null($twtids) ? db_query($sql) : db_query($sql, $twtids); + + // To allow multiple joins from one tbl/col, must use an alias and + // 'relationship field' for the left side + $i = 0; + while ($row = db_fetch_array($result)) { + extract($row); + $rawtbl1 = schema_unprefix_table($tbl1); + $rawtbl2 = schema_unprefix_table($tbl2); + if (!isset($tables[$rawtbl1][$col1]['relationship'])) { + $tables[$rawtbl1][$col1]['title'] = $t("@column (joins to @table)", array('@column' => $col1, '@table' => $tbl2)); + $tables[$rawtbl1][$col1]['relationship'] = array( + 'base' => $rawtbl2, + 'base field' => $col2, + 'label' => $t("Join @table1 to @table2", array('@table1' => $tbl1, '@table2' => $tbl2)), + ); + } + else { + $i++; + $mungedcol = $col1 . '_' . $i; + $tables[$rawtbl1][$mungedcol] = $tables[$rawtbl1][$col1]; + $tables[$rawtbl1][$mungedcol]['title'] = $t("@column (joins to @table)", array('@column' => $col1, '@table' => $tbl2)); + $tables[$rawtbl1][$mungedcol]['real field'] = $col1; + $tables[$rawtbl1][$mungedcol]['relationship'] = array( + 'base' => $rawtbl2, + 'base field' => $col2, + 'relationship field' => $col1, + 'label' => $t("Join @table1 to @table2", array('@table1' => $tbl1, '@table2' => $tbl2)), + ); + } + } + return $tables; +}