all contrib module security updates done
|
Before Width: | Height: | Size: 637 B After Width: | Height: | Size: 272 B |
|
Before Width: | Height: | Size: 549 B After Width: | Height: | Size: 252 B |
|
Before Width: | Height: | Size: 362 B After Width: | Height: | Size: 228 B |
|
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 262 B |
|
Before Width: | Height: | Size: 568 B After Width: | Height: | Size: 254 B |
@@ -63,8 +63,12 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
$options = parent::option_definition();
|
||||
$options['use_batch'] = array('default' => 'no_batch');
|
||||
$options['items_per_page'] = array('default' => '0');
|
||||
$options['return_path'] = array('default' => '');
|
||||
$options['style_plugin']['default'] = 'views_data_export_csv';
|
||||
|
||||
// This is the default size of a segment when doing a batched export.
|
||||
$options['segment_size']['default'] = 100;
|
||||
|
||||
if (isset($options['defaults']['default']['items_per_page'])) {
|
||||
$options['defaults']['default']['items_per_page'] = FALSE;
|
||||
}
|
||||
@@ -113,6 +117,27 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
'batch' => t('Export data in small segments to build a complete export. Recommended for large exports sets (1000+ rows)'),
|
||||
),
|
||||
);
|
||||
// Allow the administrator to configure the number of items exported per batch.
|
||||
$form['segment_size'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Segment size'),
|
||||
'#description' => t('If each row of your export consumes a lot of memory to render, then reduce this value. Higher values will generally mean that the export completes in less time but will have a higher peak memory usage.'),
|
||||
'#options' => drupal_map_assoc(range(1, 500)),
|
||||
'#default_value' => $this->get_option('segment_size'),
|
||||
'#process' => array('ctools_dependent_process'),
|
||||
'#dependency' => array(
|
||||
'radio:use_batch' => array('batch')
|
||||
),
|
||||
);
|
||||
$form['return_path'] = array(
|
||||
'#title' => t('Return path'),
|
||||
'#type' => 'textfield',
|
||||
'#description' => t('Return path after the batched operation, leave empty for default. This path will only be used if the export URL is visited directly, and not by following a link when attached to another view display.'),
|
||||
'#default_value' => $this->get_option('return_path'),
|
||||
'#dependency' => array(
|
||||
'radio:use_batch' => array('batch')
|
||||
),
|
||||
);
|
||||
if (!$this->is_compatible()) {
|
||||
$form['use_batch']['#disabled'] = TRUE;
|
||||
$form['use_batch']['#default_value'] = 'no_batch';
|
||||
@@ -123,9 +148,35 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'cache':
|
||||
// We're basically going to disable using cache plugins, by disabling
|
||||
// the UI.
|
||||
if (isset($form['cache']['type']['#options'])) {
|
||||
foreach ($form['cache']['type']['#options'] as $id => $v) {
|
||||
if ($id != 'none') {
|
||||
unset($form['cache']['type']['#options'][$id]);
|
||||
}
|
||||
$form['cache']['type']['#description'] = t("Views data export isn't currently compatible with caching plugins.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function get_option($option) {
|
||||
// Force people to never use caching with Views data export. Sorry folks,
|
||||
// but it causes too many issues for our workflow. If you really want to add
|
||||
// caching back, then you can subclass this display handler and override
|
||||
// this method to add it back.
|
||||
if ($option == 'cache') {
|
||||
return array('type' => 'none');
|
||||
}
|
||||
|
||||
return parent::get_option($option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the options from the options form.
|
||||
*/
|
||||
@@ -135,6 +186,8 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
switch ($form_state['section']) {
|
||||
case 'use_batch':
|
||||
$this->set_option('use_batch', $form_state['values']['use_batch']);
|
||||
$this->set_option('segment_size', $form_state['values']['segment_size']);
|
||||
$this->set_option('return_path', $form_state['values']['return_path']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -185,8 +238,17 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
}
|
||||
|
||||
// Try and get a batch context if possible.
|
||||
$eid = !empty($_GET['eid']) ? $_GET['eid'] :
|
||||
(!empty($this->batched_execution_state->eid) ? $this->batched_execution_state->eid : FALSE);
|
||||
|
||||
if (!empty($_GET['eid']) && !empty($_GET['token']) && drupal_valid_token($_GET['token'], 'views_data_export/' . $_GET['eid'])) {
|
||||
$eid = $_GET['eid'];
|
||||
}
|
||||
elseif (!empty($this->batched_execution_state->eid)) {
|
||||
$eid = $this->batched_execution_state->eid;
|
||||
}
|
||||
else {
|
||||
$eid = FALSE;
|
||||
}
|
||||
|
||||
if ($eid) {
|
||||
$this->batched_execution_state = views_data_export_get($eid);
|
||||
}
|
||||
@@ -240,30 +302,46 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
$this->batched_execution_state = views_data_export_new($this->view->name, $this->view->current_display, $this->outputfile_create());
|
||||
views_data_export_view_store($this->batched_execution_state->eid, $this->view);
|
||||
|
||||
// Record a usage of our file, so we can identify our exports later.
|
||||
file_usage_add(file_load($this->batched_execution_state->fid), 'views_data_export', 'eid', $this->batched_execution_state->eid);
|
||||
|
||||
// We need to build the index right now, before we lose $_GET etc.
|
||||
$this->initialize_index();
|
||||
//$this->batched_execution_state->fid = $this->outputfile_create();
|
||||
|
||||
// Initialize the progress counter
|
||||
$this->batched_execution_state->sandbox['max'] = db_query('SELECT COUNT(*) FROM {' . $this->index_tablename() . '}')->fetchField();
|
||||
// Initialize the progress counter.
|
||||
if (db_table_exists($this->index_tablename())) {
|
||||
$this->batched_execution_state->sandbox['max'] = db_query('SELECT COUNT(*) FROM {' . $this->index_tablename() . '}')->fetchField();
|
||||
}
|
||||
|
||||
// Record the time we started.
|
||||
list($usec, $sec) = explode(' ', microtime());
|
||||
$this->batched_execution_state->sandbox['started'] = (float) $usec + (float) $sec;
|
||||
|
||||
// Pop something into the session to ensure it stays aorund.
|
||||
$_SESSION['views_data_export'][$this->batched_execution_state->eid] = TRUE;
|
||||
|
||||
// Build up our querystring for the final page callback.
|
||||
$querystring = array(
|
||||
'eid' => $this->batched_execution_state->eid,
|
||||
'token' => drupal_get_token('views_data_export/' . $this->batched_execution_state->eid),
|
||||
'return-url' => NULL,
|
||||
);
|
||||
// If we were attached to another view, grab that for the final URL.
|
||||
if (!empty($_GET['attach']) && isset($this->view->display[$_GET['attach']])) {
|
||||
|
||||
// If we have a configured return path, use that.
|
||||
if ($this->get_option('return_path')) {
|
||||
$querystring['return-url'] = $this->get_option('return_path');
|
||||
}
|
||||
// Else if we were attached to another view, grab that for the final URL.
|
||||
else if (!empty($_GET['attach']) && isset($this->view->display[$_GET['attach']])) {
|
||||
// Get the path of the attached display:
|
||||
$querystring['return-url'] = $this->view->display[$_GET['attach']]->handler->get_path();
|
||||
$querystring['return-url'] = $this->view->get_url(NULL, $this->view->display[$_GET['attach']]->handler->get_path());
|
||||
}
|
||||
|
||||
//Set the batch off
|
||||
$batch = array(
|
||||
'operations' => array (
|
||||
array('_views_data_export_batch_process', array($this->batched_execution_state->eid, $this->view->current_display)),
|
||||
array('_views_data_export_batch_process', array($this->batched_execution_state->eid, $this->view->current_display, $this->view->get_exposed_input())),
|
||||
),
|
||||
'title' => t('Building export'),
|
||||
'init_message' => t('Export is starting up.'),
|
||||
@@ -280,6 +358,7 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
// e.g. Drush to grab this batch and yet execute it in
|
||||
// it's own special way
|
||||
$batch['view_name'] = $this->view->name;
|
||||
$batch['exposed_filters'] = $this->view->get_exposed_input();
|
||||
$batch['display_id'] = $this->view->current_display;
|
||||
$batch['eid'] = $this->batched_execution_state->eid;
|
||||
$batch_redirect = array($final_destination, array('query' => $querystring));
|
||||
@@ -341,6 +420,9 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
break;
|
||||
|
||||
case VIEWS_DATA_EXPORT_FOOTER:
|
||||
// Update the temporary file size, otherwise we would get a problematic
|
||||
// "Content-Length: 0" HTTP header, that may break the export download.
|
||||
$this->outputfile_update_size();
|
||||
$sandbox['finished'] = 1;
|
||||
$state->batch_state = VIEWS_DATA_EXPORT_FINISHED;
|
||||
break;
|
||||
@@ -358,6 +440,13 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
function execute_final() {
|
||||
// Should we download the file.
|
||||
if (!empty($_GET['download'])) {
|
||||
// Clean up our session, if we need to.
|
||||
if (isset($_SESSION)) {
|
||||
unset($_SESSION['views_data_export'][$this->batched_execution_state->eid]);
|
||||
if (empty($_SESSION['views_data_export'])) {
|
||||
unset($_SESSION['views_data_export']);
|
||||
}
|
||||
}
|
||||
// This next method will exit.
|
||||
$this->transfer_file();
|
||||
}
|
||||
@@ -433,22 +522,32 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
|
||||
// Grab our results from the index, and push them into the view result.
|
||||
// TODO: Handle external databases.
|
||||
$result = db_query_range('SELECT * FROM {' . $this->index_tablename() . '} ORDER BY ' . $this->batched_execution_state->sandbox['weight_field_alias'] . ' ASC', 0, 100);
|
||||
$result = db_query_range('SELECT * FROM {' . $this->index_tablename() . '} ORDER BY ' . $this->batched_execution_state->sandbox['weight_field_alias'] . ' ASC', 0, $this->get_option('segment_size'));
|
||||
$this->view->result = array();
|
||||
foreach ($result as $item_hashed) {
|
||||
$item = new stdClass();
|
||||
// We had to shorten some of the column names in the index, restore
|
||||
// those now.
|
||||
foreach ($item_hashed as $hash => $value) {
|
||||
if (isset($this->batched_execution_state->sandbox['field_aliases'][$hash])) {
|
||||
$item->{$this->batched_execution_state->sandbox['field_aliases'][$hash]} = $value;
|
||||
}
|
||||
else {
|
||||
$item->{$hash} = $value;
|
||||
$query_plugin = get_class($this->view->query);
|
||||
if ($query_plugin == 'views_plugin_query_default') {
|
||||
foreach ($result as $item_hashed) {
|
||||
$item = new stdClass();
|
||||
// We had to shorten some of the column names in the index, restore
|
||||
// those now.
|
||||
foreach ($item_hashed as $hash => $value) {
|
||||
if (isset($this->batched_execution_state->sandbox['field_aliases'][$hash])) {
|
||||
$item->{$this->batched_execution_state->sandbox['field_aliases'][$hash]} = $value;
|
||||
}
|
||||
else {
|
||||
$item->{$hash} = $value;
|
||||
}
|
||||
}
|
||||
// Push the restored $item in the views result array.
|
||||
$this->view->result[] = $item;
|
||||
}
|
||||
}
|
||||
elseif ($query_plugin == 'SearchApiViewsQuery') {
|
||||
foreach ($result as $row) {
|
||||
$item = unserialize($row->data);
|
||||
$item->{$this->batched_execution_state->sandbox['weight_field_alias']} = $row->{$this->batched_execution_state->sandbox['weight_field_alias']};
|
||||
$this->view->result[] = $item;
|
||||
}
|
||||
// Push the restored $item in the views result array.
|
||||
$this->view->result[] = $item;
|
||||
}
|
||||
$this->view->_post_execute();
|
||||
break;
|
||||
@@ -466,6 +565,7 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
$query = array(
|
||||
'download' => 1,
|
||||
'eid' => $this->batched_execution_state->eid,
|
||||
'token' => drupal_get_token('views_data_export/' . $this->batched_execution_state->eid),
|
||||
);
|
||||
|
||||
return theme('views_data_export_complete_page', array(
|
||||
@@ -493,7 +593,7 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
// Change the items per page.
|
||||
$this->view->set_items_per_page(20);
|
||||
// Force a pager to be used.
|
||||
$this->set_option('pager', array('type' => 'some'));
|
||||
$this->set_option('pager', array('type' => 'some', 'options' => array()));
|
||||
return '<p>' . t('A maximum of 20 items will be shown here, all results will be shown on export.') . '</p><pre>' . check_plain($this->view->render()) . '</pre>';
|
||||
}
|
||||
return $this->view->render();
|
||||
@@ -513,7 +613,10 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
}
|
||||
// Set the headers.
|
||||
$this->add_http_headers();
|
||||
file_transfer($this->outputfile_path(), array());
|
||||
$headers = array(
|
||||
'Content-Length' => $this->outputfile_entity()->filesize,
|
||||
);
|
||||
file_transfer($this->outputfile_path(), $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,16 +633,31 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
// Get views to build the query.
|
||||
$view->build();
|
||||
|
||||
// Change the query object to use our custom one.
|
||||
$query = new views_data_export_plugin_query_default_batched();
|
||||
// Copy the query over:
|
||||
foreach ($view->query as $property => $value) {
|
||||
$query->$property = $value;
|
||||
}
|
||||
// Replace the query object.
|
||||
$view->query = $query;
|
||||
$query_plugin = get_class($view->query);
|
||||
if ($query_plugin == 'views_plugin_query_default') {
|
||||
// Change the query object to use our custom one.
|
||||
switch ($this->_get_database_driver()) {
|
||||
case 'pgsql':
|
||||
$query_class = 'views_data_export_plugin_query_pgsql_batched';
|
||||
break;
|
||||
|
||||
$view->execute();
|
||||
default:
|
||||
$query_class = 'views_data_export_plugin_query_default_batched';
|
||||
break;
|
||||
}
|
||||
$query = new $query_class();
|
||||
// Copy the query over:
|
||||
foreach ($view->query as $property => $value) {
|
||||
$query->$property = $value;
|
||||
}
|
||||
// Replace the query object.
|
||||
$view->query = $query;
|
||||
|
||||
$view->execute();
|
||||
}
|
||||
elseif ($query_plugin == 'SearchApiViewsQuery') {
|
||||
$this->store_search_api_result(clone($view));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -596,15 +714,29 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
return VIEWS_DATA_EXPORT_INDEX_TABLE_PREFIX . $this->batched_execution_state->eid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output file entity.
|
||||
*/
|
||||
public function outputfile_entity() {
|
||||
if (empty($this->_output_file)) {
|
||||
if (!empty($this->batched_execution_state->fid)) {
|
||||
// Return the filename associated with this file.
|
||||
$this->_output_file = $this->file_load($this->batched_execution_state->fid);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return $this->_output_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output file path.
|
||||
*/
|
||||
function outputfile_path() {
|
||||
if (empty($this->_output_file) && !empty($this->batched_execution_state->fid)) {
|
||||
// Return the filename associated with this file.
|
||||
$this->_output_file = $this->file_load($this->batched_execution_state->fid);
|
||||
public function outputfile_path() {
|
||||
if ($file = $this->outputfile_entity()) {
|
||||
return $file->uri;
|
||||
}
|
||||
return $this->_output_file->uri;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -614,7 +746,7 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
*/
|
||||
protected function outputfile_create() {
|
||||
|
||||
$dir = 'temporary://views_plugin_display';
|
||||
$dir = variable_get('views_data_export_directory', 'temporary://views_plugin_display');
|
||||
|
||||
// Make sure the directory exists first.
|
||||
if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
|
||||
@@ -626,6 +758,11 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
// Save the file into the DB.
|
||||
$file = $this->file_save_file($path);
|
||||
|
||||
// Make sure the file is marked as temporary.
|
||||
// There is no FILE_STATUS_TEMPORARY constant.
|
||||
$file->status = 0;
|
||||
file_save($file);
|
||||
|
||||
return $file->fid;
|
||||
}
|
||||
|
||||
@@ -639,6 +776,16 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the file size in the file entity.
|
||||
*/
|
||||
protected function outputfile_update_size() {
|
||||
if ($file = $this->outputfile_entity()) {
|
||||
$file->filesize = filesize($file->uri);
|
||||
file_save($file);
|
||||
}
|
||||
}
|
||||
|
||||
function abort_export($errors) {
|
||||
// Just cause the next batch to do the clean-up
|
||||
if (!is_array($errors)) {
|
||||
@@ -675,7 +822,7 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
* An array containing the file information, or 0 in the event of an error.
|
||||
*/
|
||||
function file_save_file($filepath) {
|
||||
return file_save_data('', $filepath);
|
||||
return file_save_data('', $filepath, FILE_EXISTS_REPLACE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -704,7 +851,7 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
*/
|
||||
function is_compatible() {
|
||||
$incompatible_drivers = array (
|
||||
'pgsql',
|
||||
//'pgsql',
|
||||
);
|
||||
$db_driver = $this->_get_database_driver();
|
||||
return !in_array($db_driver, $incompatible_drivers);
|
||||
@@ -713,7 +860,56 @@ class views_data_export_plugin_display_export extends views_plugin_display_feed
|
||||
function _get_database_driver() {
|
||||
$name = !empty($this->view->base_database) ? $this->view->base_database : 'default';
|
||||
$conn_info = Database::getConnectionInfo($name);
|
||||
return $conn_info[$name]['driver'];
|
||||
return $conn_info['default']['driver'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on views_data_export_plugin_query_default_batched::execute().
|
||||
*/
|
||||
function store_search_api_result($view) {
|
||||
$display_handler = &$view->display_handler;
|
||||
$start = microtime(TRUE);
|
||||
|
||||
try {
|
||||
// Get all the view results.
|
||||
$view->query->set_limit(NULL);
|
||||
$view->query->set_offset(0);
|
||||
$view->query->execute($view);
|
||||
$weight_alias = 'vde_weight';
|
||||
$display_handler->batched_execution_state->sandbox['weight_field_alias'] = $weight_alias;
|
||||
|
||||
$schema = array(
|
||||
'fields' => array(
|
||||
$weight_alias => array('type' => 'int'),
|
||||
'data' => array('type' => 'blob'),
|
||||
));
|
||||
|
||||
db_create_table($display_handler->index_tablename(), $schema);
|
||||
|
||||
if (!empty($view->result)) {
|
||||
$insert_query = db_insert($display_handler->index_tablename())->fields(array($weight_alias, 'data'));
|
||||
$weight = 0;
|
||||
foreach ($view->result as $item) {
|
||||
$insert_query->values(array(
|
||||
$weight_alias => $weight,
|
||||
'data' => serialize($item),
|
||||
));
|
||||
$weight++;
|
||||
}
|
||||
$insert_query->execute();
|
||||
}
|
||||
|
||||
$view->result = array();
|
||||
// Now create an index for the weight field, otherwise the queries on the
|
||||
// index will take a long time to execute.
|
||||
db_add_unique_key($display_handler->index_tablename(), $weight_alias, array($weight_alias));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$view->result = array();
|
||||
debug('Exception: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$view->execute_time = microtime(TRUE) - $start;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -808,6 +1004,123 @@ class views_data_export_plugin_query_default_batched extends views_plugin_query_
|
||||
db_query($insert_query, $args);
|
||||
|
||||
|
||||
$view->result = array();
|
||||
|
||||
$this->pager->post_execute($view->result);
|
||||
|
||||
if ($this->pager->use_pager()) {
|
||||
$view->total_rows = $this->pager->get_total_items();
|
||||
}
|
||||
|
||||
// Now create an index for the weight field, otherwise the queries on the
|
||||
// index will take a long time to execute.
|
||||
db_add_unique_key($display_handler->index_tablename(), $display_handler->batched_execution_state->sandbox['weight_field_alias'], array($display_handler->batched_execution_state->sandbox['weight_field_alias']));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$view->result = array();
|
||||
debug('Exception: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
$view->execute_time = microtime(TRUE) - $start;
|
||||
}
|
||||
}
|
||||
|
||||
class views_data_export_plugin_query_pgsql_batched extends views_data_export_plugin_query_default_batched {
|
||||
|
||||
|
||||
/**
|
||||
* Executes the query and fills the associated view object with according
|
||||
* values.
|
||||
*
|
||||
* Values to set: $view->result, $view->total_rows, $view->execute_time,
|
||||
* $view->current_page.
|
||||
*/
|
||||
function execute(&$view) {
|
||||
$display_handler = &$view->display_handler;
|
||||
$external = FALSE; // Whether this query will run against an external database.
|
||||
$query = $view->build_info['query'];
|
||||
$count_query = $view->build_info['count_query'];
|
||||
|
||||
$query->addMetaData('view', $view);
|
||||
$count_query->addMetaData('view', $view);
|
||||
|
||||
if (empty($this->options['disable_sql_rewrite'])) {
|
||||
$base_table_data = views_fetch_data($this->base_table);
|
||||
if (isset($base_table_data['table']['base']['access query tag'])) {
|
||||
$access_tag = $base_table_data['table']['base']['access query tag'];
|
||||
$query->addTag($access_tag);
|
||||
$count_query->addTag($access_tag);
|
||||
}
|
||||
}
|
||||
|
||||
$items = array();
|
||||
if ($query) {
|
||||
$additional_arguments = module_invoke_all('views_query_substitutions', $view);
|
||||
|
||||
// Count queries must be run through the preExecute() method.
|
||||
// If not, then hook_query_node_access_alter() may munge the count by
|
||||
// adding a distinct against an empty query string
|
||||
// (e.g. COUNT DISTINCT(1) ...) and no pager will return.
|
||||
// See pager.inc > PagerDefault::execute()
|
||||
// http://api.drupal.org/api/drupal/includes--pager.inc/function/PagerDefault::execute/7
|
||||
// See http://drupal.org/node/1046170.
|
||||
$count_query->preExecute();
|
||||
|
||||
// Build the count query.
|
||||
$count_query = $count_query->countQuery();
|
||||
|
||||
// Add additional arguments as a fake condition.
|
||||
// XXX: this doesn't work... because PDO mandates that all bound arguments
|
||||
// are used on the query. TODO: Find a better way to do this.
|
||||
if (!empty($additional_arguments)) {
|
||||
// $query->where('1 = 1', $additional_arguments);
|
||||
// $count_query->where('1 = 1', $additional_arguments);
|
||||
}
|
||||
|
||||
$start = microtime(TRUE);
|
||||
|
||||
if ($this->pager->use_count_query() || !empty($view->get_total_rows)) {
|
||||
$this->pager->execute_count_query($count_query);
|
||||
}
|
||||
|
||||
// Let the pager modify the query to add limits.
|
||||
$this->pager->pre_execute($query);
|
||||
|
||||
if (!empty($this->limit) || !empty($this->offset)) {
|
||||
// We can't have an offset without a limit, so provide a very large limit instead.
|
||||
$limit = intval(!empty($this->limit) ? $this->limit : 999999);
|
||||
$offset = intval(!empty($this->offset) ? $this->offset : 0);
|
||||
$query->range($offset, $limit);
|
||||
}
|
||||
|
||||
try {
|
||||
// The $query is final and ready to go, we are going to redirect it to
|
||||
// become an insert into our table, sneaky!
|
||||
// Our query will look like:
|
||||
// CREATE TABLE {idx} SELECT @row := @row + 1 AS weight_alias, cl.* FROM
|
||||
// (-query-) AS cl, (SELECT @row := 0) AS r
|
||||
// We do some magic to get the row count.
|
||||
|
||||
$display_handler->batched_execution_state->sandbox['weight_field_alias'] = $display_handler->_weight_alias_create($view);
|
||||
|
||||
$display_handler->batched_execution_state->sandbox['field_aliases'] = $display_handler->field_aliases_create($view);
|
||||
$select_aliases = array();
|
||||
foreach ($display_handler->batched_execution_state->sandbox['field_aliases'] as $hash => $alias) {
|
||||
$select_aliases[] = "cl.$alias AS $hash";
|
||||
}
|
||||
|
||||
// TODO: this could probably be replaced with a query extender and new query type.
|
||||
$query->preExecute();
|
||||
$args = $query->getArguments();
|
||||
// Create temporary sequence
|
||||
$seq_name = $display_handler->index_tablename() . '_seq';
|
||||
db_query('CREATE TEMP sequence ' . $seq_name);
|
||||
// Query uses sequence to create row number
|
||||
$insert_query = 'CREATE TABLE {' . $display_handler->index_tablename() . "} AS SELECT nextval('". $seq_name . "') AS " . $display_handler->batched_execution_state->sandbox['weight_field_alias'] . ', ' . implode(', ', $select_aliases) . ' FROM (' . (string)$query . ') AS cl';
|
||||
db_query($insert_query, $args);
|
||||
|
||||
|
||||
$view->result = array();
|
||||
|
||||
$this->pager->post_execute($view->result);
|
||||
|
||||
@@ -149,29 +149,33 @@ class views_data_export_plugin_style_export extends views_plugin_style {
|
||||
* feed image link.
|
||||
*/
|
||||
function attach_to($display_id, $path, $title) {
|
||||
|
||||
$type = $this->definition['export feed type'];
|
||||
$theme_pattern = array(
|
||||
'views_data_export_feed_icon__' . $this->view->name . '__' . $display_id . '__' . $type,
|
||||
'views_data_export_feed_icon__' . $this->view->name . '__' . $display_id,
|
||||
'views_data_export_feed_icon__' . $this->view->name . '__' . $type,
|
||||
'views_data_export_feed_icon__' . $display_id . '__' . $type,
|
||||
'views_data_export_feed_icon__' . $display_id,
|
||||
'views_data_export_feed_icon__' . $type,
|
||||
'views_data_export_feed_icon',
|
||||
);
|
||||
$query = $this->view->get_exposed_input();
|
||||
// Stash the display id we're coming form in the url so we can hijack it later.
|
||||
if ($this->options['parent_sort']) {
|
||||
$query['attach'] = $display_id;
|
||||
if ($this->display->handler->access()) {
|
||||
$type = $this->definition['export feed type'];
|
||||
$theme_pattern = array(
|
||||
'views_data_export_feed_icon__' . $this->view->name . '__' . $display_id . '__' . $type,
|
||||
'views_data_export_feed_icon__' . $this->view->name . '__' . $display_id,
|
||||
'views_data_export_feed_icon__' . $this->view->name . '__' . $type,
|
||||
'views_data_export_feed_icon__' . $display_id . '__' . $type,
|
||||
'views_data_export_feed_icon__' . $display_id,
|
||||
'views_data_export_feed_icon__' . $type,
|
||||
'views_data_export_feed_icon',
|
||||
);
|
||||
$query = $this->view->get_exposed_input();
|
||||
// Stash the display id we're coming form in the url so we can hijack it later.
|
||||
if ($this->options['parent_sort']) {
|
||||
$query['attach'] = $display_id;
|
||||
}
|
||||
if (!isset($this->view->feed_icon)) {
|
||||
$this->view->feed_icon = '';
|
||||
}
|
||||
$this->view->feed_icon .= theme($theme_pattern, array(
|
||||
'image_path' => $this->definition['export feed icon'],
|
||||
'url' => $this->view->get_url(NULL, $path),
|
||||
'query' => $query,
|
||||
'text' => $this->options['attach_text'],
|
||||
)
|
||||
);
|
||||
}
|
||||
$this->view->feed_icon .= theme($theme_pattern, array(
|
||||
'image_path' => $this->definition['export feed icon'],
|
||||
'url' => $this->view->get_url(NULL, $path),
|
||||
'query' => $query,
|
||||
'text' => $this->options['attach_text'],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function build_sort() {
|
||||
@@ -291,7 +295,6 @@ class views_data_export_plugin_style_export extends views_plugin_style {
|
||||
* Add any HTTP headers that this style plugin wants to.
|
||||
*/
|
||||
function add_http_headers() {
|
||||
$view = &$this->view;
|
||||
|
||||
drupal_add_http_header('Cache-Control', 'max-age=60, must-revalidate');
|
||||
|
||||
@@ -301,6 +304,22 @@ class views_data_export_plugin_style_export extends views_plugin_style {
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->options['filename']) && !empty($this->options['provide_file'])) {
|
||||
$filename = $this->generate_filename();
|
||||
|
||||
if ($filename) {
|
||||
drupal_add_http_header('Content-Disposition', 'attachment; filename="'. $filename .'"');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the filename for the export.
|
||||
*/
|
||||
function generate_filename() {
|
||||
$view = $this->view;
|
||||
$filename = '';
|
||||
|
||||
if (isset($this->options['filename']) && !empty($this->options['provide_file'])) {
|
||||
// General tokens.
|
||||
$tokens = array(
|
||||
@@ -311,7 +330,7 @@ class views_data_export_plugin_style_export extends views_plugin_style {
|
||||
$count = 0;
|
||||
foreach ($view->display_handler->get_handlers('argument') as $arg => $handler) {
|
||||
$token = '%' . ++$count;
|
||||
$tokens[$token . '-title'] = check_plain($handler->title());
|
||||
$tokens[$token . '-title'] = $handler->get_title();
|
||||
$tokens[$token . '-value'] = isset($view->args[$count - 1]) ? check_plain($view->args[$count - 1]) : '';
|
||||
}
|
||||
|
||||
@@ -322,7 +341,25 @@ class views_data_export_plugin_style_export extends views_plugin_style {
|
||||
continue;
|
||||
}
|
||||
if (!empty($view->exposed_input[$handler->options['expose']['identifier']])) {
|
||||
$exposed[] = check_plain($handler->options['expose']['identifier']) . '_' . check_plain($view->exposed_input[$handler->options['expose']['identifier']]);
|
||||
$identifier = $handler->options['expose']['identifier'];
|
||||
$option = $view->exposed_input[$identifier];
|
||||
// The option may be a string or an array or even an array of arrays,
|
||||
// depending on whether the widget is a text box/area, a select box
|
||||
// or a date range.
|
||||
if (is_array($option)) {
|
||||
// loop over each option
|
||||
foreach ($option as $key => $item) {
|
||||
if (!is_array($item)) {
|
||||
continue;
|
||||
}
|
||||
// if its an array implode it first
|
||||
else {
|
||||
$option[$key] = implode('--', $item);
|
||||
}
|
||||
}
|
||||
$option = implode('--', $option);
|
||||
}
|
||||
$exposed[] = check_plain($identifier) . '_' . check_plain($option);
|
||||
}
|
||||
}
|
||||
if (!empty($exposed)) {
|
||||
@@ -351,11 +388,8 @@ class views_data_export_plugin_style_export extends views_plugin_style {
|
||||
}
|
||||
|
||||
$filename = strtr($this->options['filename'], $tokens);
|
||||
|
||||
|
||||
if ($filename) {
|
||||
drupal_add_http_header('Content-Disposition', 'attachment; filename="'. $filename .'"');
|
||||
}
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,16 +40,23 @@ class views_data_export_plugin_style_export_csv extends views_data_export_plugin
|
||||
'default' => ', ',
|
||||
'translatable' => FALSE,
|
||||
);
|
||||
$options['newline_token'] = array(
|
||||
'default' => 1,
|
||||
'translatable' => FALSE,
|
||||
);
|
||||
$options['header'] = array(
|
||||
'default' => TRUE,
|
||||
'translatable' => FALSE,
|
||||
);
|
||||
$options['keep_html'] = array(
|
||||
'default' => FALSE,
|
||||
'translatable' => FALSE,
|
||||
);
|
||||
$options['encoding'] = array(
|
||||
'default' => '',
|
||||
'translatable' => FALSE,
|
||||
);
|
||||
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
@@ -93,23 +100,37 @@ class views_data_export_plugin_style_export_csv extends views_data_export_plugin
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Replacement'),
|
||||
'#default_value' => $this->options['newline_replacement'],
|
||||
'#process' => array('form_process_checkboxes', 'ctools_dependent_process'),
|
||||
'#process' => array('ctools_dependent_process'),
|
||||
'#dependency' => array('edit-style-options-replace-newlines' => array(TRUE)),
|
||||
);
|
||||
$form['newline_token'] = array(
|
||||
'#prefix' => '<div><div id="edit-options-newline-token">',
|
||||
'#suffix' => '</div></div>',
|
||||
'#type' => 'radios',
|
||||
'#default_value' => !empty($this->options['newline_token']) ? $this->options['newline_token'] : '0',
|
||||
'#title' => t('What to replace?'),
|
||||
'#options' => array(
|
||||
t('Replace only Linefeed ("\n")'),
|
||||
t('Replace Carriage Return plus Linefeed ("\r\n") and single Linefeed ("\n") as well')
|
||||
),
|
||||
'#dependency' => array('edit-style-options-replace-newlines' => array(TRUE)),
|
||||
);
|
||||
$form['header'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Make first row a list of column headers.'),
|
||||
'#default_value' => !empty($this->options['header']),
|
||||
);
|
||||
$form['keep_html'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => !empty($this->options['keep_html']),
|
||||
'#title' => t('Keep HTML tags.'),
|
||||
);
|
||||
$form['encoding'] = array(
|
||||
'#type' => 'select',
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => !empty($this->options['encoding']) ? $this->options['encoding'] : '',
|
||||
'#title' => t('Character encoding conversion'),
|
||||
'#options' => array (
|
||||
'' => t('No conversion'),
|
||||
'ASCII' => t('ASCII'),
|
||||
),
|
||||
'#description' => t('Optionally specify a character conversion that some CSV file readers need. Note, using an external tool is always preferred and you should only use this option as a last resort. This feature requires the "iconv" PHP extension.'),
|
||||
'#description' => t('Optionally specify a character conversion that some CSV file readers need. Use "utf8_decode" for ISO-8859-1 via <a href="@utf8_decode">utf8_decode PHP function</a>, other values will be passed <a href="@iconv">iconv</a> (this requires the iconv PHP extension), empty or illegal values will leave the output as UTF-8.',
|
||||
array('@iconv' => 'http://www.php.net/manual/en/function.iconv.php', '@utf8_decode' => 'http://www.php.net/manual/en/function.utf8-decode.php')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,26 @@
|
||||
*/
|
||||
class views_data_export_plugin_style_export_xml extends views_data_export_plugin_style_export {
|
||||
|
||||
/**
|
||||
* Initialize a style plugin.
|
||||
*/
|
||||
function init(&$view, &$display, $options = NULL) {
|
||||
// View is not set in option_definition(), so we fill defaults here if
|
||||
// options are empty.
|
||||
if (empty($options['root_node']) || empty($options['item_node'])) {
|
||||
$base_object_name = rtrim($view->base_table, 's');
|
||||
|
||||
if (empty($options['root_node'])) {
|
||||
$options['root_node'] = $base_object_name . 's';
|
||||
}
|
||||
if (empty($options['item_node'])) {
|
||||
$options['item_node'] = $base_object_name;
|
||||
}
|
||||
}
|
||||
|
||||
parent::init($view, $display, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set options fields and default values.
|
||||
*
|
||||
@@ -28,6 +48,22 @@ class views_data_export_plugin_style_export_xml extends views_data_export_plugin
|
||||
'default' => 'dash',
|
||||
'translatable' => FALSE,
|
||||
);
|
||||
$options['root_node'] = array(
|
||||
'default' => '',
|
||||
'translatable' => FALSE,
|
||||
);
|
||||
$options['item_node'] = array(
|
||||
'default' => '',
|
||||
'translatable' => FALSE,
|
||||
);
|
||||
$options['no_entity_encode'] = array(
|
||||
'default' => array(),
|
||||
'translatable' => FALSE,
|
||||
);
|
||||
$options['cdata_wrapper'] = array(
|
||||
'default' => array(),
|
||||
'translatable' => FALSE,
|
||||
);
|
||||
|
||||
return $options;
|
||||
}
|
||||
@@ -66,5 +102,68 @@ class views_data_export_plugin_style_export_xml extends views_data_export_plugin
|
||||
'edit-style-options-transform' => array(TRUE),
|
||||
),
|
||||
);
|
||||
$form['root_node'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Root node'),
|
||||
'#default_value' => $this->options['root_node'],
|
||||
'#description' => t('The XML tag for the root node.'),
|
||||
);
|
||||
$form['item_node'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Item node'),
|
||||
'#default_value' => $this->options['item_node'],
|
||||
'#description' => t('The XML tag for an item node.'),
|
||||
);
|
||||
|
||||
$field_labels = $this->display->handler->get_field_labels();
|
||||
|
||||
if (!empty($field_labels)) {
|
||||
$options = $field_labels;
|
||||
if (empty($this->options['no_entity_encode'])) {
|
||||
$this->options['no_entity_encode'] = array();
|
||||
}
|
||||
|
||||
$form['no_entity_encode'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Disable encoding of XML entities for these fields'),
|
||||
'#options' => $options,
|
||||
'#default_value' => $this->options['no_entity_encode'],
|
||||
'#description' => t('If checked field contents will be outputted '.
|
||||
'<em>without encoding</em> of XML entities. This is '.
|
||||
'useful when when used in conjunction with a field '.
|
||||
'formatter that outputs properly formatted and '.
|
||||
'encoded XML data.'),
|
||||
);
|
||||
|
||||
if (empty($this->options['cdata_wrapper'])) {
|
||||
$this->options['cdata_wrapper'] = array();
|
||||
}
|
||||
|
||||
$form['cdata_wrapper'] = array(
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => t('Fields value to wrapped using CDATA'),
|
||||
'#options' => $options,
|
||||
'#default_value' => $this->options['cdata_wrapper'],
|
||||
'#description' => t('If checked the fields content will be wrapped using the CDATA tag.'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform any necessary changes to the form values prior to storage.
|
||||
* There is no need for this function to actually store the data.
|
||||
*
|
||||
* @param $form
|
||||
* @param $form_state
|
||||
*/
|
||||
function options_submit(&$form, &$form_state) {
|
||||
if (isset($form_state['values']['style_options']['no_entity_encode'])) {
|
||||
// Remove any options values set to 0
|
||||
$form_state['values']['style_options']['no_entity_encode'] = array_filter($form_state['values']['style_options']['no_entity_encode']);
|
||||
}
|
||||
if (isset($form_state['values']['style_options']['cdata_wrapper'])) {
|
||||
// Remove any options values set to 0
|
||||
$form_state['values']['style_options']['cdata_wrapper'] = array_filter($form_state['values']['style_options']['cdata_wrapper']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Test class for access checks for VDE downloads.
|
||||
*
|
||||
* Views Data Export enforces that a previously exported file may only be
|
||||
* re-downloaded by the user that created the export. We test for that with
|
||||
* this class.
|
||||
*/
|
||||
class ViewsDataExportAccessTest extends ViewsDataExportBaseTest {
|
||||
|
||||
protected $profile = 'testing';
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Access to temp files',
|
||||
'description' => 'Check access to created export files.',
|
||||
'group' => 'Views Data Export',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that VDE export can only be downloaded by the user that created them.
|
||||
*/
|
||||
public function testExportedTempFileAccess() {
|
||||
$this->admin_user1 = $this->drupalCreateUser();
|
||||
$this->admin_user2 = $this->drupalCreateUser();
|
||||
|
||||
// Run a batched export.
|
||||
$path = 'vde_test/' . $this->randomName();
|
||||
list($view, $expected) = $this->getExportView($path);
|
||||
$display = &$view->display['vde_test']->handler;
|
||||
// Set this view to be batched.
|
||||
$display->override_option('use_batch', 'batch');
|
||||
// Save this view so we can hit the path.
|
||||
$view->save();
|
||||
// Ensure that the menu router system is rebuilt on the next page load.
|
||||
variable_set('menu_rebuild_needed', TRUE);
|
||||
|
||||
$this->drupalLogin($this->admin_user1);
|
||||
// Catpure the session_id as the redirects in the request ditch it.
|
||||
$session_id = $this->session_id;
|
||||
$this->assertBatchedExportEqual($path, $expected, 'Batched access export matched expected output.');
|
||||
|
||||
// Remove all the test data, so future exports will be different.
|
||||
db_truncate('views_test')->execute();
|
||||
$this->resetAll();
|
||||
|
||||
// Assert that we can re-download directly when supplying the token.
|
||||
// We rely on this being the first export in this test class.
|
||||
// Restore the session_id from above so we can use drupalGetToken.
|
||||
$this->session_id = $session_id;
|
||||
$token = $this->drupalGetToken('views_data_export/1');
|
||||
$this->drupalGet($path, array('query' => array('eid' => 1, 'download' => 1, 'token' => $token)));
|
||||
$output = $this->drupalGetContent();
|
||||
$this->assertEqual($this->normaliseString($output), $expected, 'Re-download of export file by original user is possible with session token.');
|
||||
|
||||
// Assert that we cannot re-download directly without supplying the token.
|
||||
// We rely on this being the first export in this test class.
|
||||
$this->drupalGet($path, array('query' => array('eid' => 1, 'download' => 1)));
|
||||
$output = $this->drupalGetContent();
|
||||
$this->assertEqual($this->normaliseString($output), '', 'Re-download of export file by original user is not possible.');
|
||||
|
||||
// Assert that someone else can't download our file.
|
||||
// We rely on this being the first export in this test class.
|
||||
$this->drupalLogin($this->admin_user2);
|
||||
$this->drupalGet($path, array('query' => array('eid' => 1, 'download' => 1, 'token' => $token)));
|
||||
$output = $this->drupalGetContent();
|
||||
$this->assertEqual($this->normaliseString($output), '', 'Re-download of export file by different user is not possible.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides DrupalWebTestCase::drupalGetToken() to support the hash salt.
|
||||
*
|
||||
* @todo Remove when http://drupal.org/node/1555862 is fixed in core.
|
||||
*/
|
||||
protected function drupalGetToken($value = '') {
|
||||
$private_key = drupal_get_private_key();
|
||||
return drupal_hmac_base64($value, $this->session_id . $private_key . drupal_get_hash_salt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return a basic view of the views_test table.
|
||||
*
|
||||
* @return view
|
||||
*/
|
||||
protected function getBasicExportView() {
|
||||
views_include('view');
|
||||
|
||||
// Create the basic view.
|
||||
$view = new view();
|
||||
$view->vid = 'new';
|
||||
$view->base_table = 'views_test';
|
||||
|
||||
// Set up the fields we need.
|
||||
$display = $view->new_display('default', 'Master', 'default');
|
||||
|
||||
$display->override_option('fields', array(
|
||||
'id' => array(
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'name' => array(
|
||||
'id' => 'name',
|
||||
'table' => 'views_test',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'age' => array(
|
||||
'id' => 'age',
|
||||
'table' => 'views_test',
|
||||
'field' => 'age',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
|
||||
// Set up the sort order.
|
||||
$display->override_option('sorts', array(
|
||||
'id' => array(
|
||||
'order' => 'ASC',
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
|
||||
// Set up the pager.
|
||||
$display->override_option('pager', array(
|
||||
'type' => 'none',
|
||||
'options' => array('offset' => 0),
|
||||
));
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
protected function getStylePluginName() {
|
||||
return 'views_data_export_txt';
|
||||
}
|
||||
|
||||
protected function getExportView($path = 'vde_test') {
|
||||
// Create the basic view.
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$display = $view->new_display('views_data_export', 'Data export', 'vde_test');
|
||||
$display->override_option('style_plugin', $this->getStylePluginName());
|
||||
$display->override_option('path', $path);
|
||||
|
||||
$expected = '[ID]
|
||||
|
||||
1
|
||||
[Name]
|
||||
|
||||
John
|
||||
[Age]
|
||||
|
||||
25
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
2
|
||||
[Name]
|
||||
|
||||
George
|
||||
[Age]
|
||||
|
||||
27
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
3
|
||||
[Name]
|
||||
|
||||
Ringo
|
||||
[Age]
|
||||
|
||||
28
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
4
|
||||
[Name]
|
||||
|
||||
Paul
|
||||
[Age]
|
||||
|
||||
26
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
5
|
||||
[Name]
|
||||
|
||||
Meredith
|
||||
[Age]
|
||||
|
||||
30
|
||||
----------------------------------------';
|
||||
|
||||
return array(&$view, $expected);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,508 @@
|
||||
<?php
|
||||
|
||||
abstract class ViewsDataExportBaseTest extends ViewsTestCase {
|
||||
|
||||
protected function setUp() {
|
||||
$modules = func_get_args();
|
||||
if (isset($modules[0]) && is_array($modules[0])) {
|
||||
$modules = $modules[0];
|
||||
}
|
||||
|
||||
$modules[] = 'views_data_export';
|
||||
$modules[] = 'views_ui';
|
||||
$modules[] = 'views';
|
||||
parent::setUp($modules);
|
||||
|
||||
// Define the schema and views data variable before enabling the test module.
|
||||
variable_set('views_test_schema', $this->schemaDefinition());
|
||||
variable_set('views_test_views_data', $this->viewsData());
|
||||
variable_set('views_test_views_plugins', $this->viewsPlugins());
|
||||
|
||||
module_enable(array('views_test'));
|
||||
$this->resetAll();
|
||||
|
||||
// Load the test dataset.
|
||||
$data_set = $this->dataSet();
|
||||
$query = db_insert('views_test')
|
||||
->fields(array_keys($data_set[0]));
|
||||
foreach ($data_set as $record) {
|
||||
$query->values($record);
|
||||
}
|
||||
$query->execute();
|
||||
$this->checkPermissions(array(), TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function allows to enable views ui from a higher class which can't change the setup function anymore.
|
||||
*
|
||||
* @TODO
|
||||
* Convert existing setUp functions.
|
||||
*/
|
||||
function enableViewsUi() {
|
||||
module_enable(array('views_ui'));
|
||||
// @TODO Figure out why it's required to clear the cache here.
|
||||
views_module_include('views_default', TRUE);
|
||||
views_get_all_views(TRUE);
|
||||
menu_rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* The schema definition.
|
||||
*/
|
||||
protected function schemaDefinition() {
|
||||
$schema['views_test'] = array(
|
||||
'description' => 'Basic test table for Views tests.',
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'name' => array(
|
||||
'description' => "A person's name",
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'age' => array(
|
||||
'description' => "The person's age",
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0),
|
||||
'job' => array(
|
||||
'description' => "The person's job",
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => 'Undefined',
|
||||
),
|
||||
'created' => array(
|
||||
'description' => "The creation date of this record",
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('id'),
|
||||
'unique keys' => array(
|
||||
'name' => array('name')
|
||||
),
|
||||
'indexes' => array(
|
||||
'ages' => array('age'),
|
||||
),
|
||||
);
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* The views data definition.
|
||||
*/
|
||||
protected function viewsData() {
|
||||
// Declaration of the base table.
|
||||
$data['views_test']['table'] = array(
|
||||
'group' => t('Views test'),
|
||||
'base' => array(
|
||||
'field' => 'id',
|
||||
'title' => t('Views test'),
|
||||
'help' => t('Users who have created accounts on your site.'),
|
||||
),
|
||||
);
|
||||
|
||||
// Declaration of fields.
|
||||
$data['views_test']['id'] = array(
|
||||
'title' => t('ID'),
|
||||
'help' => t('The test data ID'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_numeric',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_numeric',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
);
|
||||
$data['views_test']['name'] = array(
|
||||
'title' => t('Name'),
|
||||
'help' => t('The name of the person'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_string',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
);
|
||||
$data['views_test']['age'] = array(
|
||||
'title' => t('Age'),
|
||||
'help' => t('The age of the person'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_numeric',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_numeric',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
);
|
||||
$data['views_test']['job'] = array(
|
||||
'title' => t('Job'),
|
||||
'help' => t('The job of the person'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_string',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
);
|
||||
$data['views_test']['created'] = array(
|
||||
'title' => t('Created'),
|
||||
'help' => t('The creation date of this record'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_date',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_date',
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_date',
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort_date',
|
||||
),
|
||||
);
|
||||
return $data;
|
||||
}
|
||||
|
||||
protected function viewsPlugins() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* A very simple test dataset.
|
||||
*/
|
||||
protected function dataSet() {
|
||||
return array(
|
||||
array(
|
||||
'name' => 'John',
|
||||
'age' => 25,
|
||||
'job' => "Singer\r\nSongwriter\r\nPianist",
|
||||
'created' => gmmktime(0, 0, 0, 1, 1, 2000),
|
||||
),
|
||||
array(
|
||||
'name' => 'George',
|
||||
'age' => 27,
|
||||
'job' => "Singer\nGuitar player\nSitar player",
|
||||
'created' => gmmktime(0, 0, 0, 1, 2, 2000),
|
||||
),
|
||||
array(
|
||||
'name' => 'Ringo',
|
||||
'age' => 28,
|
||||
'job' => 'Drummer',
|
||||
'created' => gmmktime(6, 30, 30, 1, 1, 2000),
|
||||
),
|
||||
array(
|
||||
'name' => 'Paul',
|
||||
'age' => 26,
|
||||
'job' => "Songwriter\rBass guitarist",
|
||||
'created' => gmmktime(6, 0, 0, 1, 1, 2000),
|
||||
),
|
||||
array(
|
||||
'name' => 'Meredith',
|
||||
'age' => 30,
|
||||
'job' => 'Speaker',
|
||||
'created' => gmmktime(6, 30, 10, 1, 1, 2000),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and return a basic view of the views_test table.
|
||||
*
|
||||
* @return view
|
||||
*/
|
||||
protected function getBasicView() {
|
||||
views_include('view');
|
||||
|
||||
// Create the basic view.
|
||||
$view = new view();
|
||||
$view->vid = 'test_view';
|
||||
$view->add_display('default');
|
||||
$view->base_table = 'views_test';
|
||||
|
||||
// Set up the fields we need.
|
||||
$display = $view->new_display('default', 'Master', 'default');
|
||||
$display->override_option('fields', array(
|
||||
'id' => array(
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'name' => array(
|
||||
'id' => 'name',
|
||||
'table' => 'views_test',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'age' => array(
|
||||
'id' => 'age',
|
||||
'table' => 'views_test',
|
||||
'field' => 'age',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
|
||||
// Set up the sort order.
|
||||
$display->override_option('sorts', array(
|
||||
'id' => array(
|
||||
'order' => 'ASC',
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
|
||||
// Set up the pager.
|
||||
$display->override_option('pager', array(
|
||||
'type' => 'none',
|
||||
'options' => array('offset' => 0),
|
||||
));
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
public function logViewResult($result, $prefix = 'View result:<br>') {
|
||||
$this->verbose($prefix . '<br><pre>' . check_plain($result) .'</pre>');
|
||||
}
|
||||
|
||||
public function assertBatchedExportEqual($path, $expected, $message) {
|
||||
$this->drupalGet($path);
|
||||
$output = $this->drupalGetContent();
|
||||
$this->logViewResult($output);
|
||||
$this->logViewResult($expected, 'Expected result:<br>');
|
||||
$this->assertEqual($this->normaliseString($output), $this->normaliseString($expected), $message);
|
||||
}
|
||||
|
||||
public function assertExportEqual($a, $b, $message) {
|
||||
$this->logViewResult($a);
|
||||
$this->logViewResult($b, 'Expected result:<br>');
|
||||
$this->assertEqual($this->normaliseString($a), $this->normaliseString($b), $message);
|
||||
}
|
||||
|
||||
protected function normaliseString($s) {
|
||||
// Normalize line endings
|
||||
// Convert all line-endings to UNIX format
|
||||
$s = str_replace("\r\n", "\n", $s);
|
||||
$s = str_replace("\r", "\n", $s);
|
||||
$s = trim($s);
|
||||
return $s;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class ViewsDataExportSimpleExportTest extends ViewsDataExportBaseTest {
|
||||
|
||||
protected $vde_export_type;
|
||||
|
||||
/**
|
||||
* Tests the non-batched export functionality for this style.
|
||||
*/
|
||||
public function testNonBatchedExport() {
|
||||
$path = 'vde_test/' . $this->randomName();
|
||||
list($view, $expected) = $this->getExportView($path);
|
||||
// Save this view so we can hit the path.
|
||||
$view->save();
|
||||
// Ensure that the menu router system is rebuilt on the next page load.
|
||||
variable_set('menu_rebuild_needed', TRUE);
|
||||
|
||||
$this->drupalGet($path);
|
||||
$result = $this->drupalGetContent();
|
||||
|
||||
$this->assertExportEqual($result, $expected, 'Non batched ' . $this->vde_export_type . ' export matched expected output.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the batched export functionality for this style.
|
||||
*/
|
||||
public function testBatchedExport() {
|
||||
$path = 'vde_test/' . $this->randomName();
|
||||
list($view, $expected) = $this->getExportView($path);
|
||||
$display = &$view->display['vde_test']->handler;
|
||||
// Set this view to be batched.
|
||||
$display->override_option('use_batch', 'batch');
|
||||
// Save this view so we can hit the path.
|
||||
$view->save();
|
||||
// Ensure that the menu router system is rebuilt on the next page load.
|
||||
variable_set('menu_rebuild_needed', TRUE);
|
||||
|
||||
$this->assertBatchedExportEqual($path, $expected, 'Batched ' . $this->vde_export_type . ' export matched expected output.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a very basic view and expected output for this style.
|
||||
*
|
||||
* @return
|
||||
* An array containing two elements:
|
||||
* - A View object, for the export.
|
||||
* - The expected out from that view, if is was executed without further
|
||||
* changes.
|
||||
*/
|
||||
abstract protected function getExportView($path = 'vde_test');
|
||||
|
||||
/**
|
||||
* Build and return a basic view of the views_test table.
|
||||
*
|
||||
* @return view
|
||||
*/
|
||||
protected function getBasicExportView() {
|
||||
views_include('view');
|
||||
|
||||
// Create the basic view.
|
||||
$view = new view();
|
||||
$view->vid = 'new';
|
||||
$view->base_table = 'views_test';
|
||||
|
||||
// Set up the fields we need.
|
||||
$display = $view->new_display('default', 'Master', 'default');
|
||||
|
||||
$display->override_option('fields', array(
|
||||
'id' => array(
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'name' => array(
|
||||
'id' => 'name',
|
||||
'table' => 'views_test',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'age' => array(
|
||||
'id' => 'age',
|
||||
'table' => 'views_test',
|
||||
'field' => 'age',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
|
||||
// Set up the sort order.
|
||||
$display->override_option('sorts', array(
|
||||
'id' => array(
|
||||
'order' => 'ASC',
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
|
||||
// Set up the pager.
|
||||
$display->override_option('pager', array(
|
||||
'type' => 'none',
|
||||
'options' => array('offset' => 0),
|
||||
));
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a view that's our basic view, but with hide if empty/0 support.
|
||||
*
|
||||
* We add this to the 'age' field.
|
||||
*/
|
||||
protected function getHideIfEmptyExportView() {
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$display = $view->display['default']->handler;
|
||||
|
||||
$display->override_option('fields', array(
|
||||
'id' => array(
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'name' => array(
|
||||
'id' => 'name',
|
||||
'table' => 'views_test',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
// Hide their name if its empty.
|
||||
'hide_empty' => TRUE,
|
||||
// But we don't hide it if it's: 0.
|
||||
'empty_zero' => FALSE,
|
||||
),
|
||||
'age' => array(
|
||||
'id' => 'age',
|
||||
'table' => 'views_test',
|
||||
'field' => 'age',
|
||||
'relationship' => 'none',
|
||||
// Hide their age if it's empty.
|
||||
'hide_empty' => TRUE,
|
||||
'empty_zero' => TRUE,
|
||||
),
|
||||
));
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a given view very simply.
|
||||
*
|
||||
* This will take a view, and add a display plugin of the correct export type,
|
||||
* and then run it and compare it with the expected output.
|
||||
*/
|
||||
protected function executeAndCompareGivenView(view $view, $expected, $message = '', $style_options = array()) {
|
||||
$path = 'vde_test/' . $this->randomName();
|
||||
|
||||
$display = $view->new_display('views_data_export', 'Data export', 'vde_test');
|
||||
$display->override_option('style_plugin', $this->getStylePluginName());
|
||||
$display->override_option('style_options', $style_options);
|
||||
$display->override_option('path', $path);
|
||||
|
||||
// Save this view so we can hit the path.
|
||||
$view->save();
|
||||
|
||||
// Ensure that the menu router system is rebuilt on the next page load.
|
||||
variable_set('menu_rebuild_needed', TRUE);
|
||||
|
||||
$this->drupalGet($path);
|
||||
$result = $this->drupalGetContent();
|
||||
|
||||
$this->assertExportEqual($result, $expected, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the style plugin represented by this test.
|
||||
*/
|
||||
abstract protected function getStylePluginName();
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
<?php
|
||||
|
||||
class CSVExportViewsDataExportTests extends ViewsDataExportSimpleExportTest {
|
||||
|
||||
protected $profile = 'testing';
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'CSV Export',
|
||||
'description' => 'Various tests for exporting to CSV.',
|
||||
'group' => 'Views Data Export',
|
||||
);
|
||||
}
|
||||
|
||||
protected $vde_export_type = 'CSV';
|
||||
|
||||
protected function getStylePluginName() {
|
||||
return 'views_data_export_csv';
|
||||
}
|
||||
|
||||
protected function getExportView($path = 'vde_test') {
|
||||
// Create the basic view.
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$display = $view->new_display('views_data_export', 'Data export', 'vde_test');
|
||||
$display->override_option('style_plugin', $this->getStylePluginName());
|
||||
$display->override_option('path', $path);
|
||||
|
||||
$expected = '"ID","Name","Age"
|
||||
"1","John","25"
|
||||
"2","George","27"
|
||||
"3","Ringo","28"
|
||||
"4","Paul","26"
|
||||
"5","Meredith","30"';
|
||||
|
||||
return array(&$view, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to ensure that HTML tags are kept in CSV files when requested.
|
||||
*/
|
||||
protected function testKeepHTML() {
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$display = $view->display['default']->handler;
|
||||
|
||||
$display->override_option('fields', array(
|
||||
'id' => array(
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
// Add a label to include HTML
|
||||
'label' => '<strong id="id">ID</strong>',
|
||||
),
|
||||
'name' => array(
|
||||
'id' => 'name',
|
||||
'table' => 'views_test',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
// Alter this field to include HTML.
|
||||
'alter' => array(
|
||||
'alter_text' => TRUE,
|
||||
'text' => '<em>[name]</em>',
|
||||
),
|
||||
),
|
||||
'age' => array(
|
||||
'id' => 'age',
|
||||
'table' => 'views_test',
|
||||
'field' => 'age',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
|
||||
$style_options = array(
|
||||
'keep_html' => TRUE,
|
||||
);
|
||||
|
||||
$expected = '"<strong id=""id"">ID</strong>","Name","Age"
|
||||
"1","<em>John</em>","25"
|
||||
"2","<em>George</em>","27"
|
||||
"3","<em>Ringo</em>","28"
|
||||
"4","<em>Paul</em>","26"
|
||||
"5","<em>Meredith</em>","30"';
|
||||
|
||||
$message = 'Keep HTML test in ' . $this->vde_export_type . ' export matched expected output.';
|
||||
|
||||
$this->executeAndCompareGivenView($view, $expected, $message, $style_options);
|
||||
|
||||
|
||||
// And now make sure that HTML tags are stripped correctly.
|
||||
$style_options = array(
|
||||
'keep_html' => FALSE,
|
||||
);
|
||||
|
||||
$expected = '"ID","Name","Age"
|
||||
"1","John","25"
|
||||
"2","George","27"
|
||||
"3","Ringo","28"
|
||||
"4","Paul","26"
|
||||
"5","Meredith","30"';
|
||||
|
||||
$message = 'Keep HTML reverse test in ' . $this->vde_export_type . ' export matched expected output.';
|
||||
|
||||
$this->executeAndCompareGivenView($view, $expected, $message, $style_options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to ensure that HTML tags are kept in CSV files when requested.
|
||||
*/
|
||||
protected function testTrimFields() {
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$display = $view->display['default']->handler;
|
||||
|
||||
$display->override_option('fields', array(
|
||||
'id' => array(
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
'label' => 'ID',
|
||||
),
|
||||
'name' => array(
|
||||
'id' => 'name',
|
||||
'table' => 'views_test',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
// Alter this field to include HTML.
|
||||
'alter' => array(
|
||||
'alter_text' => TRUE,
|
||||
'text' => ' [name] ',
|
||||
),
|
||||
),
|
||||
'age' => array(
|
||||
'id' => 'age',
|
||||
'table' => 'views_test',
|
||||
'field' => 'age',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
|
||||
$style_options = array(
|
||||
'trim' => FALSE,
|
||||
);
|
||||
|
||||
$expected = '"ID","Name","Age"
|
||||
"1"," John ","25"
|
||||
"2"," George ","27"
|
||||
"3"," Ringo ","28"
|
||||
"4"," Paul ","26"
|
||||
"5"," Meredith ","30"';
|
||||
|
||||
$message = 'Trim reverse test in ' . $this->vde_export_type . ' export matched expected output.';
|
||||
|
||||
$this->executeAndCompareGivenView($view, $expected, $message, $style_options);
|
||||
|
||||
// And now make sure that trimming works as expected.
|
||||
$style_options = array(
|
||||
'trim' => TRUE,
|
||||
);
|
||||
|
||||
$expected = '"ID","Name","Age"
|
||||
"1","John","25"
|
||||
"2","George","27"
|
||||
"3","Ringo","28"
|
||||
"4","Paul","26"
|
||||
"5","Meredith","30"';
|
||||
|
||||
$message = 'Trim test in ' . $this->vde_export_type . ' export matched expected output.';
|
||||
|
||||
$this->executeAndCompareGivenView($view, $expected, $message, $style_options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to ensure that Encoding options work.
|
||||
*/
|
||||
protected function testIconvEncoding() {
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
db_update('views_test')
|
||||
->fields(array('name' => 'Jo€hn'))
|
||||
->condition('name', 'John')
|
||||
->execute();
|
||||
|
||||
$encodings = array(
|
||||
'ISO-8859-1//TRANSLIT' => 'EUR',
|
||||
'utf8_decode' => '?',
|
||||
);
|
||||
|
||||
foreach ($encodings as $encoding => $conversion) {
|
||||
|
||||
$style_options = array(
|
||||
'encoding' => $encoding,
|
||||
);
|
||||
|
||||
$expected = '"ID","Name","Age"
|
||||
"1","Jo' . $conversion . 'hn","25"
|
||||
"2","George","27"
|
||||
"3","Ringo","28"
|
||||
"4","Paul","26"
|
||||
"5","Meredith","30"';
|
||||
|
||||
$message = 'Character encoding ' . $encoding . ' worked correctly.';
|
||||
|
||||
$this->executeAndCompareGivenView($view, $expected, $message, $style_options);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to ensure that all new line characters are replaced in CSV files when requested.
|
||||
*/
|
||||
protected function testReplaceNewLines() {
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$display = $view->display['default']->handler;
|
||||
|
||||
$display->override_option('fields', array(
|
||||
'id' => array(
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
'label' => 'ID',
|
||||
),
|
||||
'name' => array(
|
||||
'id' => 'name',
|
||||
'table' => 'views_test',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'age' => array(
|
||||
'id' => 'age',
|
||||
'table' => 'views_test',
|
||||
'field' => 'age',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'job' => array(
|
||||
'id' => 'job',
|
||||
'table' => 'views_test',
|
||||
'field' => 'job',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
|
||||
$style_options = array(
|
||||
'replace_newlines' => TRUE,
|
||||
'newline_replacement' => ';',
|
||||
'newline_token' => 0,
|
||||
);
|
||||
|
||||
$expected = <<<EOT
|
||||
"ID","Name","Age","Job"
|
||||
"1","John","25","Singer\r;Songwriter\r;Pianist"
|
||||
"2","George","27","Singer;Guitar player;Sitar player"
|
||||
"3","Ringo","28","Drummer"
|
||||
"4","Paul","26","Songwriter\rBass guitarist"
|
||||
"5","Meredith","30","Speaker"
|
||||
EOT;
|
||||
|
||||
$message = 'Linefeed characters are replaced.';
|
||||
|
||||
$this->executeAndCompareGivenView($view, $expected, $message, $style_options);
|
||||
|
||||
// And now replace all kind of newlines.
|
||||
$style_options = array(
|
||||
'replace_newlines' => TRUE,
|
||||
'newline_replacement' => ';',
|
||||
'newline_token' => 1,
|
||||
);
|
||||
|
||||
$expected = '"ID","Name","Age","Job"
|
||||
"1","John","25","Singer;Songwriter;Pianist"
|
||||
"2","George","27","Singer;Guitar player;Sitar player"
|
||||
"3","Ringo","28","Drummer"
|
||||
"4","Paul","26","Songwriter;Bass guitarist"
|
||||
"5","Meredith","30","Speaker"';
|
||||
|
||||
$message = 'All newline characters are replaced.';
|
||||
|
||||
$this->executeAndCompareGivenView($view, $expected, $message, $style_options);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
class DOCExportViewsDataExportTests extends ViewsDataExportSimpleExportTest {
|
||||
|
||||
protected $profile = 'testing';
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'DOC Export',
|
||||
'description' => 'Various tests for exporting to DOC.',
|
||||
'group' => 'Views Data Export',
|
||||
);
|
||||
}
|
||||
|
||||
protected $vde_export_type = 'DOC';
|
||||
|
||||
protected function getStylePluginName() {
|
||||
return 'views_data_export_doc';
|
||||
}
|
||||
|
||||
protected function getExportView($path = 'vde_test') {
|
||||
// Create the basic view.
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$display = $view->new_display('views_data_export', 'Data export', 'vde_test');
|
||||
$display->override_option('style_plugin', $this->getStylePluginName());
|
||||
$display->override_option('path', $path);
|
||||
|
||||
$expected = '<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<thead><tr><th>ID</th><th>Name</th><th>Age</th></tr></thead> <tbody> <tr class="odd"><td>1</td><td>John</td><td>25</td> </tr>
|
||||
<tr class="even"><td>2</td><td>George</td><td>27</td> </tr>
|
||||
<tr class="odd"><td>3</td><td>Ringo</td><td>28</td> </tr>
|
||||
<tr class="even"><td>4</td><td>Paul</td><td>26</td> </tr>
|
||||
<tr class="odd"><td>5</td><td>Meredith</td><td>30</td> </tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>';
|
||||
|
||||
return array(&$view, $expected);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Test class for garbage collection of VDE export data.
|
||||
*/
|
||||
class ViewsDataExportGarbageCollectionTest extends ViewsDataExportBaseTest {
|
||||
|
||||
protected $profile = 'testing';
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Garbage collection',
|
||||
'description' => 'Checks garbage collection of batched exports',
|
||||
'group' => 'Views Data Export',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that VDE export can only be downloaded by the user that created them.
|
||||
*/
|
||||
public function testExportedGarbageCollection() {
|
||||
// Run a batched export.
|
||||
$path = 'vde_test/' . $this->randomName();
|
||||
list($view, $expected) = $this->getExportView($path);
|
||||
$display = &$view->display['vde_test']->handler;
|
||||
// Set this view to be batched.
|
||||
$display->override_option('use_batch', 'batch');
|
||||
// Save this view so we can hit the path.
|
||||
$view->save();
|
||||
// Ensure that the menu router system is rebuilt on the next page load.
|
||||
variable_set('menu_rebuild_needed', TRUE);
|
||||
$exports = $this->getNumberOfStoredExports();
|
||||
$files = $this->getNumberOfFiles();
|
||||
$this->assertBatchedExportEqual($path, $expected, 'Batched access export matched expected output.');
|
||||
// We should have created a new export and file.
|
||||
$this->assertEqual($this->getNumberOfStoredExports(), $exports + 1, 'A single new batched export was created');
|
||||
$this->assertEqual($this->getNumberOfFiles(), $files + 1, 'A single new temporary file was created');
|
||||
|
||||
$middle_timestamp = time();
|
||||
sleep(1);
|
||||
$this->assertBatchedExportEqual($path, $expected, 'Batched access export matched expected output.');
|
||||
// We should have created a new export and file.
|
||||
$this->assertEqual($this->getNumberOfStoredExports(), $exports + 2, 'A single new batched export was created');
|
||||
$this->assertEqual($this->getNumberOfFiles(), $files + 2, 'A single new temporary file was created');
|
||||
|
||||
// Garbage collect the first export only.
|
||||
views_data_export_garbage_collect(REQUEST_TIME - $middle_timestamp);
|
||||
$this->assertEqual($this->getNumberOfStoredExports(), $exports + 1, 'Garbage collection removed 1 old export');
|
||||
$this->assertEqual($this->getNumberOfFiles(), $files + 1, 'Garbage collection removed 1 old temporary file');
|
||||
}
|
||||
|
||||
protected function getNumberOfStoredExports() {
|
||||
return (int) db_select('views_data_export')->countQuery()->execute()->fetchField();
|
||||
}
|
||||
|
||||
protected function getNumberOfFiles() {
|
||||
return (int) db_select('file_managed')->countQuery()->execute()->fetchField();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build and return a basic view of the views_test table.
|
||||
*
|
||||
* @return view
|
||||
*/
|
||||
protected function getBasicExportView() {
|
||||
views_include('view');
|
||||
|
||||
// Create the basic view.
|
||||
$view = new view();
|
||||
$view->vid = 'new';
|
||||
$view->base_table = 'views_test';
|
||||
|
||||
// Set up the fields we need.
|
||||
$display = $view->new_display('default', 'Master', 'default');
|
||||
|
||||
$display->override_option('fields', array(
|
||||
'id' => array(
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'name' => array(
|
||||
'id' => 'name',
|
||||
'table' => 'views_test',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'age' => array(
|
||||
'id' => 'age',
|
||||
'table' => 'views_test',
|
||||
'field' => 'age',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
|
||||
// Set up the sort order.
|
||||
$display->override_option('sorts', array(
|
||||
'id' => array(
|
||||
'order' => 'ASC',
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
|
||||
// Set up the pager.
|
||||
$display->override_option('pager', array(
|
||||
'type' => 'none',
|
||||
'options' => array('offset' => 0),
|
||||
));
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
protected function getStylePluginName() {
|
||||
return 'views_data_export_txt';
|
||||
}
|
||||
|
||||
protected function getExportView($path = 'vde_test') {
|
||||
// Create the basic view.
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$display = $view->new_display('views_data_export', 'Data export', 'vde_test');
|
||||
$display->override_option('style_plugin', $this->getStylePluginName());
|
||||
$display->override_option('path', $path);
|
||||
|
||||
$expected = '[ID]
|
||||
|
||||
1
|
||||
[Name]
|
||||
|
||||
John
|
||||
[Age]
|
||||
|
||||
25
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
2
|
||||
[Name]
|
||||
|
||||
George
|
||||
[Age]
|
||||
|
||||
27
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
3
|
||||
[Name]
|
||||
|
||||
Ringo
|
||||
[Age]
|
||||
|
||||
28
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
4
|
||||
[Name]
|
||||
|
||||
Paul
|
||||
[Age]
|
||||
|
||||
26
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
5
|
||||
[Name]
|
||||
|
||||
Meredith
|
||||
[Age]
|
||||
|
||||
30
|
||||
----------------------------------------';
|
||||
|
||||
return array(&$view, $expected);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
class TXTExportViewsDataExportTests extends ViewsDataExportSimpleExportTest {
|
||||
|
||||
protected $profile = 'testing';
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'TXT Export',
|
||||
'description' => 'Various tests for exporting to TXT.',
|
||||
'group' => 'Views Data Export',
|
||||
);
|
||||
}
|
||||
|
||||
protected $vde_export_type = 'TXT';
|
||||
|
||||
protected function getStylePluginName() {
|
||||
return 'views_data_export_txt';
|
||||
}
|
||||
|
||||
protected function getExportView($path = 'vde_test') {
|
||||
// Create the basic view.
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$display = $view->new_display('views_data_export', 'Data export', 'vde_test');
|
||||
$display->override_option('style_plugin', $this->getStylePluginName());
|
||||
$display->override_option('path', $path);
|
||||
|
||||
$expected = '[ID]
|
||||
|
||||
1
|
||||
[Name]
|
||||
|
||||
John
|
||||
[Age]
|
||||
|
||||
25
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
2
|
||||
[Name]
|
||||
|
||||
George
|
||||
[Age]
|
||||
|
||||
27
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
3
|
||||
[Name]
|
||||
|
||||
Ringo
|
||||
[Age]
|
||||
|
||||
28
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
4
|
||||
[Name]
|
||||
|
||||
Paul
|
||||
[Age]
|
||||
|
||||
26
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
5
|
||||
[Name]
|
||||
|
||||
Meredith
|
||||
[Age]
|
||||
|
||||
30
|
||||
----------------------------------------';
|
||||
|
||||
return array(&$view, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to check if empty fields are correctly hidden.
|
||||
*/
|
||||
protected function testHideEmptySupport() {
|
||||
$view = $this->getHideIfEmptyExportView();
|
||||
|
||||
// We need to ensure that the test fields are actually empty/0.
|
||||
db_update('views_test')
|
||||
->fields(array('age' => 0,))
|
||||
->condition('name', 'Paul')
|
||||
->execute();
|
||||
|
||||
db_update('views_test')
|
||||
->fields(array('name' => '',))
|
||||
->condition('name', 'George')
|
||||
->execute();
|
||||
|
||||
db_update('views_test')
|
||||
->fields(array('name' => 0,))
|
||||
->condition('name', 'John')
|
||||
->execute();
|
||||
|
||||
$expected = '[ID]
|
||||
|
||||
1
|
||||
[Name]
|
||||
|
||||
0
|
||||
[Age]
|
||||
|
||||
25
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
2
|
||||
[Age]
|
||||
|
||||
27
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
3
|
||||
[Name]
|
||||
|
||||
Ringo
|
||||
[Age]
|
||||
|
||||
28
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
4
|
||||
[Name]
|
||||
|
||||
Paul
|
||||
----------------------------------------
|
||||
|
||||
[ID]
|
||||
|
||||
5
|
||||
[Name]
|
||||
|
||||
Meredith
|
||||
[Age]
|
||||
|
||||
30
|
||||
----------------------------------------';
|
||||
|
||||
$message = 'Hide if empty support for ' . $this->vde_export_type . ' export matched expected output.';
|
||||
|
||||
$this->executeAndCompareGivenView($view, $expected, $message);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
class XLSExportViewsDataExportTests extends ViewsDataExportSimpleExportTest {
|
||||
|
||||
protected $profile = 'testing';
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XLS Export',
|
||||
'description' => 'Various tests for exporting to XLS.',
|
||||
'group' => 'Views Data Export',
|
||||
);
|
||||
}
|
||||
|
||||
protected $vde_export_type = 'XLS';
|
||||
|
||||
protected function getStylePluginName() {
|
||||
return 'views_data_export_xls';
|
||||
}
|
||||
|
||||
protected function getExportView($path = 'vde_test') {
|
||||
// Create the basic view.
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$display = $view->new_display('views_data_export', 'Data export', 'vde_test');
|
||||
$display->override_option('style_plugin', $this->getStylePluginName());
|
||||
$display->override_option('path', $path);
|
||||
|
||||
$expected = '<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<thead><tr><th>ID</th><th>Name</th><th>Age</th></tr></thead> <tbody> <tr class="odd"><td>1</td><td>John</td><td>25</td> </tr>
|
||||
<tr class="even"><td>2</td><td>George</td><td>27</td> </tr>
|
||||
<tr class="odd"><td>3</td><td>Ringo</td><td>28</td> </tr>
|
||||
<tr class="even"><td>4</td><td>Paul</td><td>26</td> </tr>
|
||||
<tr class="odd"><td>5</td><td>Meredith</td><td>30</td> </tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>';
|
||||
|
||||
return array(&$view, $expected);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
|
||||
class XMLExportViewsDataExportTests extends ViewsDataExportSimpleExportTest {
|
||||
|
||||
protected $profile = 'testing';
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'XML Export',
|
||||
'description' => 'Various tests for exporting to XML.',
|
||||
'group' => 'Views Data Export',
|
||||
);
|
||||
}
|
||||
|
||||
protected $vde_export_type = 'XML';
|
||||
|
||||
protected function getStylePluginName() {
|
||||
return 'views_data_export_xml';
|
||||
}
|
||||
|
||||
protected function getExportView($path = 'vde_test') {
|
||||
// Create the basic view.
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$display = $view->new_display('views_data_export', 'Data export', 'vde_test');
|
||||
$display->override_option('style_plugin', 'views_data_export_xml');
|
||||
$display->override_option('path', $path);
|
||||
|
||||
$expected = '<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<views_tests>
|
||||
<views_test>
|
||||
<ID>1</ID>
|
||||
<Name>John</Name>
|
||||
<Age>25</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID>2</ID>
|
||||
<Name>George</Name>
|
||||
<Age>27</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID>3</ID>
|
||||
<Name>Ringo</Name>
|
||||
<Age>28</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID>4</ID>
|
||||
<Name>Paul</Name>
|
||||
<Age>26</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID>5</ID>
|
||||
<Name>Meredith</Name>
|
||||
<Age>30</Age>
|
||||
</views_test>
|
||||
</views_tests>';
|
||||
|
||||
return array(&$view, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to check if empty fields are correctly hidden.
|
||||
*/
|
||||
protected function testHideEmptySupport() {
|
||||
$view = $this->getHideIfEmptyExportView();
|
||||
|
||||
// We need to ensure that the test fields are actually empty/0.
|
||||
db_update('views_test')
|
||||
->fields(array('age' => 0,))
|
||||
->condition('name', 'Paul')
|
||||
->execute();
|
||||
|
||||
db_update('views_test')
|
||||
->fields(array('name' => '',))
|
||||
->condition('name', 'George')
|
||||
->execute();
|
||||
|
||||
db_update('views_test')
|
||||
->fields(array('name' => 0,))
|
||||
->condition('name', 'John')
|
||||
->execute();
|
||||
|
||||
$expected = '<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<views_tests>
|
||||
<views_test>
|
||||
<ID>1</ID>
|
||||
<Name>0</Name>
|
||||
<Age>25</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID>2</ID>
|
||||
<Age>27</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID>3</ID>
|
||||
<Name>Ringo</Name>
|
||||
<Age>28</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID>4</ID>
|
||||
<Name>Paul</Name>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID>5</ID>
|
||||
<Name>Meredith</Name>
|
||||
<Age>30</Age>
|
||||
</views_test>
|
||||
</views_tests>';
|
||||
|
||||
$message = 'Hide if empty support for ' . $this->vde_export_type . ' export matched expected output.';
|
||||
|
||||
$this->executeAndCompareGivenView($view, $expected, $message);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to ensure that valid XML is produced when someone doesn't specify a label.
|
||||
*/
|
||||
protected function testEmptyLabels() {
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$display = $view->display['default']->handler;
|
||||
|
||||
$display->override_option('fields', array(
|
||||
'id' => array(
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'name' => array(
|
||||
'id' => 'name',
|
||||
'table' => 'views_test',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
// Remove the label from the name field.
|
||||
'label' => '',
|
||||
),
|
||||
'age' => array(
|
||||
'id' => 'age',
|
||||
'table' => 'views_test',
|
||||
'field' => 'age',
|
||||
'relationship' => 'none',
|
||||
// Make this label intentially invalid XML.
|
||||
'label' => '.',
|
||||
),
|
||||
));
|
||||
|
||||
$expected = '<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<views_tests>
|
||||
<views_test>
|
||||
<ID>1</ID>
|
||||
<no-name>John</no-name>
|
||||
<invalid-tag-name>25</invalid-tag-name>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID>2</ID>
|
||||
<no-name>George</no-name>
|
||||
<invalid-tag-name>27</invalid-tag-name>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID>3</ID>
|
||||
<no-name>Ringo</no-name>
|
||||
<invalid-tag-name>28</invalid-tag-name>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID>4</ID>
|
||||
<no-name>Paul</no-name>
|
||||
<invalid-tag-name>26</invalid-tag-name>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID>5</ID>
|
||||
<no-name>Meredith</no-name>
|
||||
<invalid-tag-name>30</invalid-tag-name>
|
||||
</views_test>
|
||||
</views_tests>';
|
||||
|
||||
$message = 'Empty label test in ' . $this->vde_export_type . ' export matched expected output.';
|
||||
|
||||
$this->executeAndCompareGivenView($view, $expected, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to ensure that XML nodes names can be manually specified.
|
||||
*/
|
||||
protected function testCustomiseXMLNodes() {
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
// Load the include that contains the _views_data_export_xml_tag_clean function.
|
||||
module_load_include('inc', 'views_data_export', 'theme/views_data_export.theme');
|
||||
|
||||
|
||||
$root_node = _views_data_export_xml_tag_clean($this->randomName());
|
||||
$item_node = _views_data_export_xml_tag_clean($this->randomName());
|
||||
|
||||
$style_options = array(
|
||||
'root_node' => $root_node,
|
||||
'item_node' => $item_node,
|
||||
);
|
||||
|
||||
$expected = '<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<' . $root_node . '>
|
||||
<' . $item_node . '>
|
||||
<ID>1</ID>
|
||||
<Name>John</Name>
|
||||
<Age>25</Age>
|
||||
</' . $item_node . '>
|
||||
<' . $item_node . '>
|
||||
<ID>2</ID>
|
||||
<Name>George</Name>
|
||||
<Age>27</Age>
|
||||
</' . $item_node . '>
|
||||
<' . $item_node . '>
|
||||
<ID>3</ID>
|
||||
<Name>Ringo</Name>
|
||||
<Age>28</Age>
|
||||
</' . $item_node . '>
|
||||
<' . $item_node . '>
|
||||
<ID>4</ID>
|
||||
<Name>Paul</Name>
|
||||
<Age>26</Age>
|
||||
</' . $item_node . '>
|
||||
<' . $item_node . '>
|
||||
<ID>5</ID>
|
||||
<Name>Meredith</Name>
|
||||
<Age>30</Age>
|
||||
</' . $item_node . '>
|
||||
</' . $root_node . '>';
|
||||
|
||||
$message = 'Custom XML nodes test in ' . $this->vde_export_type . ' export matched expected output.';
|
||||
|
||||
$this->executeAndCompareGivenView($view, $expected, $message, $style_options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to ensure certain fields can be optionally non-escaped.
|
||||
*/
|
||||
protected function testXMLNoEntityEncode() {
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$display = $view->display['default']->handler;
|
||||
|
||||
$style_options = array(
|
||||
'no_entity_encode' => array(
|
||||
'id' => 'id',
|
||||
),
|
||||
);
|
||||
|
||||
$display->override_option('fields', array(
|
||||
'id' => array(
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
'alter' => array(
|
||||
'alter_text' => TRUE,
|
||||
'text' => '<strong>[id]</strong>',
|
||||
),
|
||||
),
|
||||
'name' => array(
|
||||
'id' => 'name',
|
||||
'table' => 'views_test',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
'alter' => array(
|
||||
'alter_text' => TRUE,
|
||||
'text' => '<em>[name]</em>',
|
||||
),
|
||||
),
|
||||
'age' => array(
|
||||
'id' => 'age',
|
||||
'table' => 'views_test',
|
||||
'field' => 'age',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
|
||||
$expected = '<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<views_tests>
|
||||
<views_test>
|
||||
<ID><strong>1</strong></ID>
|
||||
<Name><em>John</em></Name>
|
||||
<Age>25</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID><strong>2</strong></ID>
|
||||
<Name><em>George</em></Name>
|
||||
<Age>27</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID><strong>3</strong></ID>
|
||||
<Name><em>Ringo</em></Name>
|
||||
<Age>28</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID><strong>4</strong></ID>
|
||||
<Name><em>Paul</em></Name>
|
||||
<Age>26</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID><strong>5</strong></ID>
|
||||
<Name><em>Meredith</em></Name>
|
||||
<Age>30</Age>
|
||||
</views_test>
|
||||
</views_tests>';
|
||||
|
||||
$message = 'XML in values test in ' . $this->vde_export_type . ' export matched expected output.';
|
||||
|
||||
$this->executeAndCompareGivenView($view, $expected, $message, $style_options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to ensure certain fields can be optionally non-escaped.
|
||||
*/
|
||||
protected function testXMLCDATAWrapper() {
|
||||
$view = $this->getBasicExportView();
|
||||
|
||||
$style_options = array(
|
||||
'cdata_wrapper' => array(
|
||||
'id' => 'id',
|
||||
'name' => 'name',
|
||||
),
|
||||
);
|
||||
|
||||
$display = $view->display['default']->handler;
|
||||
|
||||
$display->override_option('fields', array(
|
||||
'id' => array(
|
||||
'id' => 'id',
|
||||
'table' => 'views_test',
|
||||
'field' => 'id',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
'name' => array(
|
||||
'id' => 'name',
|
||||
'table' => 'views_test',
|
||||
'field' => 'name',
|
||||
'relationship' => 'none',
|
||||
'alter' => array(
|
||||
'alter_text' => TRUE,
|
||||
'text' => '<em>[name]</em>',
|
||||
),
|
||||
),
|
||||
'age' => array(
|
||||
'id' => 'age',
|
||||
'table' => 'views_test',
|
||||
'field' => 'age',
|
||||
'relationship' => 'none',
|
||||
),
|
||||
));
|
||||
|
||||
$expected = '<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<views_tests>
|
||||
<views_test>
|
||||
<ID><![CDATA[1]]></ID>
|
||||
<Name><![CDATA[<em>John</em>]]></Name>
|
||||
<Age>25</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID><![CDATA[2]]></ID>
|
||||
<Name><![CDATA[<em>George</em>]]></Name>
|
||||
<Age>27</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID><![CDATA[3]]></ID>
|
||||
<Name><![CDATA[<em>Ringo</em>]]></Name>
|
||||
<Age>28</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID><![CDATA[4]]></ID>
|
||||
<Name><![CDATA[<em>Paul</em>]]></Name>
|
||||
<Age>26</Age>
|
||||
</views_test>
|
||||
<views_test>
|
||||
<ID><![CDATA[5]]></ID>
|
||||
<Name><![CDATA[<em>Meredith</em>]]></Name>
|
||||
<Age>30</Age>
|
||||
</views_test>
|
||||
</views_tests>';
|
||||
|
||||
$message = 'XML in values test in ' . $this->vde_export_type . ' export matched expected output.';
|
||||
|
||||
$this->executeAndCompareGivenView($view, $expected, $message, $style_options);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,7 +41,7 @@ function theme_views_data_export_message($var) {
|
||||
*/
|
||||
function theme_views_data_export_feed_icon($variables) {
|
||||
extract($variables, EXTR_SKIP);
|
||||
$url_options = array('html' => true);
|
||||
$url_options = array('html' => TRUE);
|
||||
if ($query) {
|
||||
$url_options['query'] = $query;
|
||||
}
|
||||
@@ -58,7 +58,7 @@ function theme_views_data_export_feed_icon($variables) {
|
||||
function theme_views_data_export_complete_page($variables) {
|
||||
extract($variables, EXTR_SKIP);
|
||||
drupal_set_title(t('Data export successful'));
|
||||
drupal_add_html_head(array('#tag' => 'meta', '#attributes' => array('http-equiv' =>"refresh", 'content' => '3;url='. $file)), 'views_data_export_download');
|
||||
drupal_add_html_head(array('#tag' => 'meta', '#attributes' => array('http-equiv' =>"Refresh", 'content' => '3;url='. $file)), 'views_data_export_download');
|
||||
$output = '';
|
||||
$output .= '<p>';
|
||||
$output .= t('Your export has been created. View/download the file <a href="@link">here</a> (will automatically download in 3 seconds.)', array('@link' => $file));
|
||||
@@ -105,21 +105,24 @@ function template_preprocess_views_data_export_csv_header(&$vars) {
|
||||
|
||||
// Format header values.
|
||||
foreach ($vars['header'] as $key => $value) {
|
||||
$output = decode_entities(strip_tags($value));
|
||||
$output = decode_entities($value);
|
||||
$output = (empty($vars['options']['keep_html'])) ? strip_tags($output) : $output;
|
||||
if (!empty($vars['options']['trim'])) {
|
||||
$output = trim($output);
|
||||
}
|
||||
if (!empty($vars['options']['encoding']) && function_exists('iconv')) {
|
||||
switch($vars['options']['encoding']) {
|
||||
case 'ASCII':
|
||||
$converted = iconv("UTF-8", "ASCII//TRANSLIT", $output);
|
||||
if ($converted !== FALSE) {
|
||||
$output = $converted;
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch($vars['options']['encoding']) {
|
||||
case 'utf8_decode':
|
||||
$converted = utf8_decode($output);
|
||||
break;
|
||||
default:
|
||||
$converted = iconv("UTF-8", $vars['options']['encoding'], $output);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($converted !== FALSE) {
|
||||
$output = $converted;
|
||||
}
|
||||
}
|
||||
$vars['header'][$key] = $wrap . str_replace('"', $replace_value, $output) . $wrap;
|
||||
}
|
||||
}
|
||||
@@ -148,23 +151,32 @@ function template_preprocess_views_data_export_csv_body(&$vars) {
|
||||
// Format row values.
|
||||
foreach ($vars['themed_rows'] as $i => $values) {
|
||||
foreach ($values as $j => $value) {
|
||||
$output = decode_entities(strip_tags($value));
|
||||
$output = decode_entities($value);
|
||||
$output = (empty($vars['options']['keep_html'])) ? strip_tags($output) : $output;
|
||||
if (!empty($vars['options']['trim'])) {
|
||||
$output = trim($output);
|
||||
}
|
||||
|
||||
if (!empty($vars['options']['encoding']) && function_exists('iconv')) {
|
||||
switch($vars['options']['encoding']) {
|
||||
case 'ASCII':
|
||||
$converted = iconv("UTF-8", "ASCII//TRANSLIT", $output);
|
||||
if ($converted !== FALSE) {
|
||||
$output = $converted;
|
||||
}
|
||||
case 'utf8_decode':
|
||||
$converted = utf8_decode($output);
|
||||
break;
|
||||
default:
|
||||
$converted = iconv("UTF-8", $vars['options']['encoding'], $output);
|
||||
break;
|
||||
}
|
||||
if ($converted !== FALSE) {
|
||||
$output = $converted;
|
||||
}
|
||||
}
|
||||
if (!empty($vars['options']['replace_newlines'])) {
|
||||
$output = str_replace("\n", $vars['options']['newline_replacement'], $output);
|
||||
if (!empty($vars['options']['newline_token'])) {
|
||||
$output = str_replace( array("\r\n", "\r", "\n"), $vars['options']['newline_replacement'], $output);
|
||||
}
|
||||
else {
|
||||
$output = str_replace("\n", $vars['options']['newline_replacement'], $output);
|
||||
}
|
||||
}
|
||||
$vars['themed_rows'][$i][$j] = $wrap . str_replace('"', $replace_value, $output) . $wrap;
|
||||
}
|
||||
@@ -242,14 +254,22 @@ function template_preprocess_views_data_export_csv(&$vars) {
|
||||
*/
|
||||
function template_preprocess_views_data_export_txt_body(&$vars) {
|
||||
_views_data_export_header_shared_preprocess($vars);
|
||||
// We support not outputting fields when they are empty, so indicate so.
|
||||
$vars['hide_empty_support'] = TRUE;
|
||||
_views_data_export_body_shared_preprocess($vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_views_data_export_doc_body().
|
||||
*/
|
||||
function template_preprocess_views_data_export_doc_body(&$vars) {
|
||||
// Pass through the generic MS Office preprocess.
|
||||
template_preprocess_views_data_export_msoffice_body($vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_views_data_export_xls_body().
|
||||
*/
|
||||
function template_preprocess_views_data_export_xls_body(&$vars) {
|
||||
// Pass through the generic MS Office preprocess.
|
||||
template_preprocess_views_data_export_msoffice_body($vars);
|
||||
@@ -258,6 +278,25 @@ function template_preprocess_views_data_export_xls_body(&$vars) {
|
||||
function template_preprocess_views_data_export_msoffice_body(&$vars) {
|
||||
_views_data_export_header_shared_preprocess($vars);
|
||||
_views_data_export_body_shared_preprocess($vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_process_views_data_export_doc_body().
|
||||
*/
|
||||
function template_process_views_data_export_doc_body(&$vars) {
|
||||
// Pass through the generic MS Office process.
|
||||
template_process_views_data_export_msoffice_body($vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_process_views_data_export_xls_body().
|
||||
*/
|
||||
function template_process_views_data_export_xls_body(&$vars) {
|
||||
// Pass through the generic MS Office process.
|
||||
template_process_views_data_export_msoffice_body($vars);
|
||||
}
|
||||
|
||||
function template_process_views_data_export_msoffice_body(&$vars) {
|
||||
|
||||
$output = '';
|
||||
|
||||
@@ -346,24 +385,14 @@ function template_preprocess_views_data_export_msoffice_header(&$vars) {
|
||||
* Preprocess xml output template.
|
||||
*/
|
||||
function template_preprocess_views_data_export_xml_header(&$vars) {
|
||||
// Compute the root XML node, using the base table, and appending an 's' if needed.
|
||||
$root_node = $vars['view']->base_table;
|
||||
if (rtrim($root_node, 's') == $root_node) {
|
||||
$root_node .= 's';
|
||||
}
|
||||
$vars['root_node'] = _views_data_export_xml_tag_clean($root_node);
|
||||
$vars['root_node'] = _views_data_export_xml_tag_clean($vars['options']['root_node']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocess xml output template.
|
||||
*/
|
||||
function template_preprocess_views_data_export_xml_footer(&$vars) {
|
||||
// Compute the root XML node, using the base table, and appending an 's' if needed.
|
||||
$root_node = $vars['view']->base_table;
|
||||
if (rtrim($root_node, 's') == $root_node) {
|
||||
$root_node .= 's';
|
||||
}
|
||||
$vars['root_node'] = _views_data_export_xml_tag_clean($root_node);
|
||||
$vars['root_node'] = _views_data_export_xml_tag_clean($vars['options']['root_node']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,27 +400,50 @@ function template_preprocess_views_data_export_xml_footer(&$vars) {
|
||||
*/
|
||||
function template_preprocess_views_data_export_xml_body(&$vars) {
|
||||
_views_data_export_header_shared_preprocess($vars);
|
||||
// We support not outputting fields when they are empty, so indicate so.
|
||||
$vars['hide_empty_support'] = TRUE;
|
||||
_views_data_export_body_shared_preprocess($vars);
|
||||
|
||||
// Compute the tag name based on the views base table, minus any trailing 's'.
|
||||
$vars['item_node'] = _views_data_export_xml_tag_clean(rtrim($vars['view']->base_table, 's'));
|
||||
$view = $vars['view'];
|
||||
$style_options = $view->display_handler->get_option('style_options');
|
||||
|
||||
$no_encode = isset($style_options['no_entity_encode']) ? $style_options['no_entity_encode'] : array();
|
||||
|
||||
$cdata_wrapper = isset($style_options['cdata_wrapper']) ? $style_options['cdata_wrapper'] : array();
|
||||
|
||||
$vars['item_node'] = _views_data_export_xml_tag_clean($vars['options']['item_node']);
|
||||
|
||||
foreach ($vars['themed_rows'] as $num => $row) {
|
||||
foreach ($row as $field => $content) {
|
||||
// Prevent double encoding of the ampersand. Look for the entities produced by check_plain().
|
||||
$content = preg_replace('/&(?!(amp|quot|#039|lt|gt);)/', '&', $content);
|
||||
// Convert < and > to HTML entities.
|
||||
$content = str_replace(
|
||||
array('<', '>'),
|
||||
array('<', '>'),
|
||||
$content);
|
||||
|
||||
// Perform xml entity encoding unless excluded by style options.
|
||||
if (empty($no_encode[$field]) && empty($cdata_wrapper[$field])) {
|
||||
|
||||
// Prevent double encoding of the ampersand. Look for the entities produced by check_plain().
|
||||
$content = preg_replace('/&(?!(amp|quot|#039|lt|gt);)/', '&', $content);
|
||||
// Convert < and > to HTML entities.
|
||||
$content = str_replace(
|
||||
array('<', '>'),
|
||||
array('<', '>'),
|
||||
$content);
|
||||
}
|
||||
|
||||
// Perform wrapping the field data using the CDATA tag
|
||||
// unless excluded by style options.
|
||||
if (!empty($cdata_wrapper[$field])) {
|
||||
// Escape CDATA end sequence only.
|
||||
$content = '<![CDATA[' . str_replace(']]>', ']]]]><![CDATA[>', $content) . ']]>';
|
||||
}
|
||||
|
||||
$vars['themed_rows'][$num][$field] = $content;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($vars['header'] as $field => $header) {
|
||||
// If there is no field label, use 'no name'.
|
||||
$vars['xml_tag'][$field] = !empty($header) ? $header : 'no name';
|
||||
if (empty($header)) {
|
||||
$header = 'no name';
|
||||
}
|
||||
if ($vars['options']['transform']) {
|
||||
switch ($vars['options']['transform_type']) {
|
||||
case 'dash':
|
||||
@@ -438,6 +490,12 @@ function _views_data_export_xml_tag_clean($tag) {
|
||||
// Need to trim invalid characters from the start of the string:
|
||||
$tag = ltrim($tag, $invalid_start_chars);
|
||||
|
||||
// As a last line of defense, if we've stripped out everything, set it to
|
||||
// something.
|
||||
if (empty($tag)) {
|
||||
$tag = 'invalid-tag-name';
|
||||
}
|
||||
|
||||
return $tag;
|
||||
}
|
||||
|
||||
@@ -447,14 +505,18 @@ function _views_data_export_xml_tag_clean($tag) {
|
||||
function _views_data_export_header_shared_preprocess(&$vars) {
|
||||
$view = $vars['view'];
|
||||
$fields = &$view->field;
|
||||
|
||||
$fields_info = $view->display_handler->get_option('fields');
|
||||
$vars['header'] = array();
|
||||
foreach ($fields as $key => $field) {
|
||||
if (empty($field->options['exclude'])) {
|
||||
$vars['header'][$key] = check_plain($field->label());
|
||||
if (isset($fields_info) && isset($fields_info[$key]['label'])) {
|
||||
$vars['header'][$key] = check_plain($fields_info[$key]['label']);
|
||||
}
|
||||
else {
|
||||
$vars['header'][$key] = check_plain($field->label());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -463,6 +525,7 @@ function _views_data_export_header_shared_preprocess(&$vars) {
|
||||
function _views_data_export_body_shared_preprocess(&$vars) {
|
||||
$view = $vars['view'];
|
||||
$fields = &$view->field;
|
||||
$hide_empty_support = !empty($vars['hide_empty_support']);
|
||||
|
||||
$rows = $vars['rows'];
|
||||
|
||||
@@ -473,7 +536,15 @@ function _views_data_export_body_shared_preprocess(&$vars) {
|
||||
|
||||
foreach ($keys as $id) {
|
||||
if (empty($fields[$id]->options['exclude'])) {
|
||||
$vars['themed_rows'][$num][$id] = $view->style_plugin->rendered_fields[$num][$id];
|
||||
$content = $view->style_plugin->rendered_fields[$num][$id];
|
||||
|
||||
if ($hide_empty_support && !empty($fields[$id]->options['hide_empty'])) {
|
||||
if ($fields[$id]->is_value_empty($content, $fields[$id]->options['empty_zero'])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$vars['themed_rows'][$num][$id] = $content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,12 @@ function views_data_export_drush_command() {
|
||||
'output_file' => 'The file to write the results to - will be overwritten if it already exists',
|
||||
),
|
||||
'options' => array (
|
||||
'--format' => 'csv,doc,txt,xls or xml. These options are ignored if the display_id passed is a "views_data_export" display.',
|
||||
'--separator' => 'csv only: What character separates the fields (default:,)',
|
||||
'--trim-whitespace' => 'csv only: Trim whitespace from either side of fields (default:1)',
|
||||
'--header-row' => 'csv only: Make the first row a row of headers (default:1)',
|
||||
'--quote-values' => 'csv only: Surround each field in quotes (default:1)',
|
||||
'arguments' => 'Comma separated list of arguments to be passed to the view.',
|
||||
'format' => 'csv,doc,txt,xls or xml. These options are ignored if the display_id passed is a "views_data_export" display.',
|
||||
'separator' => 'csv only: What character separates the fields (default:,)',
|
||||
'trim-whitespace' => 'csv only: Trim whitespace from either side of fields (default:1)',
|
||||
'header-row' => 'csv only: Make the first row a row of headers (default:1)',
|
||||
'quote-values' => 'csv only: Surround each field in quotes (default:1)',
|
||||
),
|
||||
'examples' => array (
|
||||
'drush views-data-export myviewname views_data_export_1 output.csv' => 'Export myviewname:views_data_export_1 and write the output to output.csv in the current directory',
|
||||
@@ -111,7 +112,7 @@ function drush_views_data_export($view_name, $display_id, $output_file) {
|
||||
return;
|
||||
}
|
||||
$already_run = TRUE;
|
||||
|
||||
|
||||
// Set the display to the one that we computed earlier.
|
||||
$display_id = drush_get_option('views_data_export_display_id', 'default');
|
||||
|
||||
@@ -158,11 +159,44 @@ function drush_views_data_export($view_name, $display_id, $output_file) {
|
||||
// We execute the view normally, and take advantage
|
||||
// of an alter function to interject later and batch it ourselves
|
||||
|
||||
$options = array (
|
||||
'output_file' => realpath(drush_get_context('DRUSH_OLDCWD', getcwd())) . '/' . $output_file,
|
||||
);
|
||||
_drush_views_data_export_override_batch($view_name, $display_id, $options);
|
||||
$view->execute_display($display_id);
|
||||
$options = array();
|
||||
// Let's see if the given $output_file is a absolute path.
|
||||
if (strpos($output_file, '/') === 0) {
|
||||
$options['output_file'] = $output_file;
|
||||
}
|
||||
else {
|
||||
$options['output_file'] = realpath(drush_get_context('DRUSH_OLDCWD', getcwd())) . '/' . $output_file;
|
||||
}
|
||||
|
||||
$arguments = drush_get_option('arguments', '');
|
||||
if(!empty($arguments) && is_array($arguments)) {
|
||||
$arguments = explode(',', $arguments);
|
||||
}
|
||||
|
||||
if ($view->display_handler->is_batched()) {
|
||||
// This is a batched export, and needs to be handled as such.
|
||||
_drush_views_data_export_override_batch($view_name, $display_id, $options);
|
||||
|
||||
$view->execute_display($display_id, $arguments);
|
||||
}
|
||||
else {
|
||||
// This export isn't batched.
|
||||
ob_start();
|
||||
$view->execute_display($display_id, $arguments);
|
||||
// Get the results, and clean the output buffer.
|
||||
$result = ob_get_contents();
|
||||
// Clean the buffer.
|
||||
ob_end_clean();
|
||||
// Save the results to file.
|
||||
// Copy file over
|
||||
if (file_put_contents($options['output_file'], $result)) {
|
||||
drush_log("Data export saved to " . $options['output_file'], 'success');
|
||||
}
|
||||
else {
|
||||
drush_set_error('VIEWS_DATA_EXPORT_COPY_ERROR', dt("The file could not be copied to the selected destination"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,40 +4,27 @@ description = "Plugin to export views data into various file formats"
|
||||
package = Views
|
||||
core = 7.x
|
||||
|
||||
files[] = views_data_export.module
|
||||
dependencies[] = views
|
||||
|
||||
; Plugins
|
||||
files[] = plugins/views_data_export_plugin_display_export.inc
|
||||
files[] = plugins/views_data_export_plugin_style_export.inc
|
||||
files[] = plugins/views_data_export_plugin_style_export_csv.inc
|
||||
files[] = plugins/views_data_export_plugin_style_export_doc.inc
|
||||
files[] = plugins/views_data_export_plugin_style_export_txt.inc
|
||||
files[] = plugins/views_data_export_plugin_style_export_xls.inc
|
||||
files[] = plugins/views_data_export_plugin_style_export_xml.inc
|
||||
|
||||
; Theme
|
||||
files[] = theme/views_data_export.theme.inc
|
||||
files[] = theme/views-data-export-csv-body.tpl.php
|
||||
files[] = theme/views-data-export-csv-footer.tpl.php
|
||||
files[] = theme/views-data-export-csv-header.tpl.php
|
||||
files[] = theme/views-data-export-doc-body.tpl.php
|
||||
files[] = theme/views-data-export-doc-footer.tpl.php
|
||||
files[] = theme/views-data-export-doc-header.tpl.php
|
||||
files[] = theme/views-data-export-txt-body.tpl.php
|
||||
files[] = theme/views-data-export-txt-footer.tpl.php
|
||||
files[] = theme/views-data-export-txt-header.tpl.php
|
||||
files[] = theme/views-data-export-xls-body.tpl.php
|
||||
files[] = theme/views-data-export-xls-footer.tpl.php
|
||||
files[] = theme/views-data-export-xls-header.tpl.php
|
||||
files[] = theme/views-data-export-xml-body.tpl.php
|
||||
files[] = theme/views-data-export-xml-footer.tpl.php
|
||||
files[] = theme/views-data-export-xml-header.tpl.php
|
||||
files[] = theme/views-data-export.tpl.php
|
||||
; Tests
|
||||
files[] = "tests/base.test"
|
||||
files[] = "tests/access.test"
|
||||
files[] = "tests/garbagecollection.test"
|
||||
files[] = "tests/csv_export.test"
|
||||
files[] = "tests/doc_export.test"
|
||||
files[] = "tests/txt_export.test"
|
||||
files[] = "tests/xls_export.test"
|
||||
files[] = "tests/xml_export.test"
|
||||
|
||||
; Information added by drupal.org packaging script on 2012-05-10
|
||||
version = "7.x-3.0-beta6"
|
||||
; Information added by Drupal.org packaging script on 2017-04-05
|
||||
version = "7.x-3.2"
|
||||
core = "7.x"
|
||||
project = "views_data_export"
|
||||
datestamp = "1336632688"
|
||||
datestamp = "1491379387"
|
||||
|
||||
|
||||
@@ -17,14 +17,14 @@ function views_data_export_schema() {
|
||||
),
|
||||
'view_name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => '32',
|
||||
'length' => '128',
|
||||
'default' => '',
|
||||
'not null' => TRUE,
|
||||
'description' => 'The unique name of the view. This is the primary field views are loaded from, and is used so that views may be internal and not necessarily in the database. May only be alphanumeric characters plus underscores.',
|
||||
),
|
||||
'view_display_id' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => '32',
|
||||
'length' => '64',
|
||||
'default' => '',
|
||||
'not null' => TRUE,
|
||||
'description' => 'The unique name of the view. This is the primary field views are loaded from, and is used so that views may be internal and not necessarily in the database. May only be alphanumeric characters plus underscores.',
|
||||
@@ -75,7 +75,7 @@ function views_data_export_schema() {
|
||||
'description' => 'The time this cache was created or updated.',
|
||||
),
|
||||
'data' => array(
|
||||
'type' => 'text',
|
||||
'type' => 'blob',
|
||||
'size' => 'big',
|
||||
'description' => 'Serialized data being stored.',
|
||||
'serialize' => TRUE,
|
||||
@@ -115,6 +115,47 @@ function views_data_export_update_7100() {
|
||||
db_add_field('views_data_export_object_cache', 'data', $new_field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the length of the view_name field to match views_schema_7301().
|
||||
*/
|
||||
function views_data_export_update_7300() {
|
||||
$fields = array();
|
||||
|
||||
$fields['view_name'] = array(
|
||||
'type' => 'varchar',
|
||||
'length' => '128',
|
||||
'default' => '',
|
||||
'not null' => TRUE,
|
||||
'description' => 'The unique name of the view. This is the primary field views are loaded from, and is used so that views may be internal and not necessarily in the database. May only be alphanumeric characters plus underscores.',
|
||||
);
|
||||
|
||||
$fields['view_display_id'] = array(
|
||||
'type' => 'varchar',
|
||||
'length' => '64',
|
||||
'default' => '',
|
||||
'not null' => TRUE,
|
||||
'description' => 'An identifier for this display; usually generated from the display_plugin, so should be something like page or page_1 or block_2, etc.',
|
||||
);
|
||||
|
||||
// Apply the updates to the DB.
|
||||
foreach ($fields as $field_name => $spec) {
|
||||
db_change_field('views_data_export', $field_name, $field_name, $spec);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Change the object cache table to support postgres installations
|
||||
*/
|
||||
function views_data_export_update_7301(){
|
||||
$spec = array(
|
||||
'type' => 'blob',
|
||||
'size' => 'big',
|
||||
'description' => 'Serialized data being stored.',
|
||||
'serialize' => TRUE,
|
||||
);
|
||||
db_change_field('views_data_export_object_cache', 'data', 'data', $spec);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_requirements().
|
||||
*/
|
||||
@@ -126,17 +167,41 @@ function views_data_export_requirements($phase) {
|
||||
|
||||
switch ($phase) {
|
||||
case 'runtime':
|
||||
// Check the max allowed packet size.
|
||||
$max_allowed_packet = db_query('SHOW VARIABLES WHERE variable_name = :name', array(':name' => 'max_allowed_packet'))->fetchField(1);
|
||||
if (is_numeric($max_allowed_packet)) {
|
||||
if ($max_allowed_packet < (16 * 1024 * 1024)) {
|
||||
$requirements['views_data_export'] = array(
|
||||
'title' => $t('MySQL - max allowed packet'),
|
||||
'value' => format_size($max_allowed_packet),
|
||||
'description' => $t("Your MySQL 'max_allowed_packet' setting may be too low for Views data export to function correctly, Drupal's requirements recommend setting it to at least 16M. See: !link", array('!link' => l('http://drupal.org/requirements', 'http://drupal.org/requirements'))),
|
||||
'severity' => REQUIREMENT_WARNING,
|
||||
);
|
||||
}
|
||||
|
||||
$requirements['views_data_export_temp'] = array(
|
||||
'title' => t('Views Data Export temporary directory'),
|
||||
'severity' => REQUIREMENT_OK,
|
||||
'value' => t('Exists'),
|
||||
);
|
||||
|
||||
$path = variable_get('views_data_export_directory', 'temporary://views_plugin_display');
|
||||
if (!file_prepare_directory($path, FILE_CREATE_DIRECTORY)) {
|
||||
$requirements['views_data_export_temp']['description'] = t('The Views Data Export temporary directory, %path could not be created due to a misconfigured directory. Please ensure that the temporary directory is correctly configured and that the webserver has permission to create directories.', array('%path' => file_uri_target($path)));
|
||||
$requirements['views_data_export_temp']['severity'] = REQUIREMENT_ERROR;
|
||||
$requirements['views_data_export_temp']['value'] = t('Unable to create');
|
||||
}
|
||||
|
||||
$db_type = Database::getConnection()->databaseType();
|
||||
switch ($db_type) {
|
||||
case 'mysql':
|
||||
// Check the max allowed packet size.
|
||||
try {
|
||||
$max_allowed_packet = db_query('SHOW VARIABLES WHERE variable_name = :name', array(':name' => 'max_allowed_packet'))->fetchField(1);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$max_allowed_packet = NULL;
|
||||
}
|
||||
if (is_numeric($max_allowed_packet)) {
|
||||
if ($max_allowed_packet < (16 * 1024 * 1024)) {
|
||||
$requirements['views_data_export'] = array(
|
||||
'title' => $t('MySQL - max allowed packet'),
|
||||
'value' => format_size($max_allowed_packet),
|
||||
'description' => $t("Your MySQL 'max_allowed_packet' setting may be too low for Views data export to function correctly, Drupal's requirements recommend setting it to at least 16M. See: !link", array('!link' => l('http://drupal.org/requirements', 'http://drupal.org/requirements'))),
|
||||
'severity' => REQUIREMENT_WARNING,
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,25 @@ function views_data_export_views_api() {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Checks whether the passed URI identifies an export file.
|
||||
*
|
||||
* @param string $uri
|
||||
* A file URI.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the URI identifies an export file, FALSE otherwise.
|
||||
*/
|
||||
function views_data_export_is_export_file($uri) {
|
||||
foreach (entity_load('file', FALSE, array('uri' => $uri)) as $file) {
|
||||
// See if this is an export file.
|
||||
$usages = file_usage_list($file);
|
||||
return !empty($usages['views_data_export']['eid']);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of hook_theme().
|
||||
*/
|
||||
@@ -93,56 +112,83 @@ function views_data_export_cron() {
|
||||
* garbage collect all exports.
|
||||
*/
|
||||
function views_data_export_garbage_collect($expires = NULL, $chunk = NULL) {
|
||||
|
||||
if (!isset($expires)) {
|
||||
$expires = variable_get('views_data_export_gc_expires', 604800); // one week
|
||||
}
|
||||
if (!isset($chunk)) {
|
||||
$chunk = variable_get('views_data_export_gc_chunk', 30);
|
||||
}
|
||||
|
||||
if ($chunk == -1) {
|
||||
$result = db_query("SELECT eid FROM {views_data_export} WHERE time_stamp <= :timestamp ORDER BY time_stamp ASC", array(':timestamp' => REQUEST_TIME - $expires));
|
||||
}
|
||||
else {
|
||||
$result = db_query_range("SELECT eid FROM {views_data_export} WHERE time_stamp <= :timestamp ORDER BY time_stamp ASC", 0, $chunk, array(':timestamp' => REQUEST_TIME - $expires));
|
||||
}
|
||||
|
||||
$eids_to_clear = array();
|
||||
foreach ($result as $row) {
|
||||
$eids_to_clear[] = $row->eid;
|
||||
}
|
||||
|
||||
// We do two things to exports we want to garbage collect
|
||||
// 1. Delete the index table for it, if it is still around
|
||||
// 2. Delete the row from the exports table
|
||||
// 3. Delete the view from the object_cache
|
||||
if (count($eids_to_clear)) {
|
||||
foreach ($eids_to_clear as $eid) {
|
||||
// 1. Delete index table, if it is still around for some reason
|
||||
$table = VIEWS_DATA_EXPORT_INDEX_TABLE_PREFIX . $eid;
|
||||
if (db_table_exists($table)) {
|
||||
db_drop_table($table);
|
||||
}
|
||||
if (lock_acquire('views_data_export_gc')) {
|
||||
if (!isset($expires)) {
|
||||
$expires = variable_get('views_data_export_gc_expires', DRUPAL_MAXIMUM_TEMP_FILE_AGE);
|
||||
}
|
||||
if (!isset($chunk)) {
|
||||
$chunk = variable_get('views_data_export_gc_chunk', 30);
|
||||
}
|
||||
|
||||
// 2. Delete the entries in the exports table.
|
||||
db_delete('views_data_export')
|
||||
->condition('eid', $eids_to_clear, 'IN')
|
||||
->execute();
|
||||
if ($chunk == -1) {
|
||||
$result = db_query("SELECT eid FROM {views_data_export} WHERE time_stamp <= :timestamp ORDER BY time_stamp ASC", array(':timestamp' => REQUEST_TIME - $expires));
|
||||
}
|
||||
else {
|
||||
$result = db_query_range("SELECT eid FROM {views_data_export} WHERE time_stamp <= :timestamp ORDER BY time_stamp ASC", 0, $chunk, array(':timestamp' => REQUEST_TIME - $expires));
|
||||
}
|
||||
|
||||
// 3. Clear the cached views
|
||||
views_data_export_view_clear($eids_to_clear);
|
||||
$eids_to_clear = array();
|
||||
foreach ($result as $row) {
|
||||
$eids_to_clear[] = $row->eid;
|
||||
}
|
||||
|
||||
// We do two things to exports we want to garbage collect
|
||||
// 1. Delete the index table for it, if it is still around.
|
||||
// 2. Delete the files used during the export.
|
||||
// 3. Delete the row from the exports table.
|
||||
// 4. Delete the view from the object_cache.
|
||||
if (count($eids_to_clear)) {
|
||||
foreach ($eids_to_clear as $eid) {
|
||||
// 1. Delete index table, if it is still around for some reason
|
||||
$table = VIEWS_DATA_EXPORT_INDEX_TABLE_PREFIX . $eid;
|
||||
if (db_table_exists($table)) {
|
||||
db_drop_table($table);
|
||||
}
|
||||
|
||||
// 2. Delete the files used during the export.
|
||||
foreach (views_data_export_export_list_files($eid) as $file) {
|
||||
file_delete($file, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Delete the entries in the exports table.
|
||||
db_delete('views_data_export')
|
||||
->condition('eid', $eids_to_clear, 'IN')
|
||||
->execute();
|
||||
|
||||
// 4. Clear the cached views
|
||||
views_data_export_view_clear($eids_to_clear);
|
||||
}
|
||||
|
||||
lock_release('views_data_export_gc');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines where a file is used.
|
||||
*
|
||||
* @param $eid
|
||||
* The ID of a Views Data Export.
|
||||
*
|
||||
* @return array
|
||||
* An array of loaded files objects used by the specified export.
|
||||
*/
|
||||
function views_data_export_export_list_files($eid) {
|
||||
$result = db_select('file_usage', 'f')
|
||||
->fields('f', array('fid'))
|
||||
->condition('id', $eid)
|
||||
->condition('type', 'eid')
|
||||
->condition('module', 'views_data_export')
|
||||
->execute();
|
||||
return file_load_multiple($result->fetchCol());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Batch API callback.
|
||||
* Handles all batching operations by executing the appropriate view.
|
||||
*/
|
||||
function _views_data_export_batch_process($export_id, $display_id, &$context) {
|
||||
function _views_data_export_batch_process($export_id, $display_id, $exposed_input, &$context) {
|
||||
// Don't show the admin menu on batch page, some people don't like it.
|
||||
if (module_exists('admin_menu')) {
|
||||
module_invoke('admin_menu', 'suppress');
|
||||
@@ -151,7 +197,9 @@ function _views_data_export_batch_process($export_id, $display_id, &$context) {
|
||||
// Fetch the view in question from our cache
|
||||
$view = views_data_export_view_retrieve($export_id);
|
||||
$view->set_display($display_id);
|
||||
|
||||
if (!empty($exposed_input)) {
|
||||
$view->set_exposed_input($exposed_input);
|
||||
}
|
||||
// Inform the data_export display which export it corresponds to and execute
|
||||
if (!isset($view->display_handler->batched_execution_state)) {
|
||||
$view->display_handler->batched_execution_state = new stdClass();
|
||||
@@ -260,14 +308,7 @@ function views_data_export_view_retrieve($export_id) {
|
||||
* An export ID or an array of export IDs to clear from the object cache.
|
||||
*/
|
||||
function views_data_export_view_clear($export_id) {
|
||||
if (is_array($export_id)) {
|
||||
db_delete('views_data_export_object_cache')
|
||||
->condition('eid', $export_id, 'IN')
|
||||
->execute();
|
||||
}
|
||||
else {
|
||||
db_delete('views_data_export_object_cache')
|
||||
db_delete('views_data_export_object_cache')
|
||||
->condition('eid', $export_id)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||