From 279eea493589dbed177a1fd5146aa77af81c30a0 Mon Sep 17 00:00:00 2001 From: Mike Decker Date: Mon, 12 Dec 2016 14:38:46 -0800 Subject: [PATCH] HSDO-641 #Initial structure & functionality of capx tampers HSDO-641 #Added features support HSDO-641 #added special support for "Copy To" tampers HSDO-641 #Added draggable table to list page for sorting tampers HSDO-641 #set weight on install, fixed bug with wildcard items added entity_load hook & entity_insert for future export/import Added validation to capx_tamper import form changed tampers to allow tamper per source per field target Added README.md --- includes/CAPx/Drupal/Mapper/EntityMapper.php | 6 +- .../PropertyProcessorAbstract.php | 2 - modules/capx_tamper/CapxTamper.inc | 96 ++++ modules/capx_tamper/README.md | 20 + modules/capx_tamper/capx_tamper.forms.inc | 516 ++++++++++++++++++ modules/capx_tamper/capx_tamper.info | 12 + modules/capx_tamper/capx_tamper.install | 76 +++ modules/capx_tamper/capx_tamper.module | 486 +++++++++++++++++ modules/capx_tamper/capx_tamper.theme.inc | 82 +++ modules/capx_tamper/css/capx_tamper.css | 58 ++ .../views/capx_tamper.views_default_alter.inc | 36 ++ .../views/views_handler_tamper_link.inc | 59 ++ stanford_capx.api.php | 2 +- 13 files changed, 1447 insertions(+), 4 deletions(-) create mode 100644 modules/capx_tamper/CapxTamper.inc create mode 100644 modules/capx_tamper/README.md create mode 100644 modules/capx_tamper/capx_tamper.forms.inc create mode 100644 modules/capx_tamper/capx_tamper.info create mode 100644 modules/capx_tamper/capx_tamper.install create mode 100644 modules/capx_tamper/capx_tamper.module create mode 100644 modules/capx_tamper/capx_tamper.theme.inc create mode 100644 modules/capx_tamper/css/capx_tamper.css create mode 100644 modules/capx_tamper/views/capx_tamper.views_default_alter.inc create mode 100644 modules/capx_tamper/views/views_handler_tamper_link.inc diff --git a/includes/CAPx/Drupal/Mapper/EntityMapper.php b/includes/CAPx/Drupal/Mapper/EntityMapper.php index 6020b0cd..1e4172ff 100644 --- a/includes/CAPx/Drupal/Mapper/EntityMapper.php +++ b/includes/CAPx/Drupal/Mapper/EntityMapper.php @@ -35,6 +35,7 @@ public function execute($entity, $data) { // Always attach the profileId to the entity $raw = $entity->value(); $raw->capx['profileId'] = $data['profileId']; + $raw->capxMapper = $this; $entity->set($raw); // Store this for later. @@ -100,6 +101,7 @@ public function mapFields($data) { /** @var \EntityDrupalWrapper $entity */ $entity = $this->getEntity(); $error = FALSE; + $d = $data; // Loop through each field and run a field processor on it. foreach ($config['fields'] as $fieldName => $remoteDataPaths) { @@ -111,7 +113,7 @@ public function mapFields($data) { if ($fieldInfoInstance) { $info = array(); - drupal_alter('capx_pre_map_field', $entity, $fieldName, $remoteDataPaths); + drupal_alter('capx_pre_map_field', $entity, $fieldName, $remoteDataPaths, $data); // Allow just one path as a string. // @todo For data structures like files we shouldn't convert data path to array. @@ -175,6 +177,7 @@ public function mapFields($data) { drupal_alter('capx_post_map_field', $entity, $fieldName); } } + $data = $d; } // Set the entity again for changes. @@ -205,6 +208,7 @@ public function mapProperties($data) { // Loop through each property and run a property processor on it. foreach ($config['properties'] as $propertyName => $remoteDataPath) { + drupal_alter('capx_pre_property_set', $entity, $data, $propertyName); try { $info = $this->getRemoteDataByJsonPath($data, $remoteDataPath); } diff --git a/includes/CAPx/Drupal/Processors/PropertyProcessors/PropertyProcessorAbstract.php b/includes/CAPx/Drupal/Processors/PropertyProcessors/PropertyProcessorAbstract.php index 4531deab..04ed2b17 100644 --- a/includes/CAPx/Drupal/Processors/PropertyProcessors/PropertyProcessorAbstract.php +++ b/includes/CAPx/Drupal/Processors/PropertyProcessors/PropertyProcessorAbstract.php @@ -34,8 +34,6 @@ public function put($data) { $data = is_array($data) ? array_shift($data) : $data; - drupal_alter('capx_pre_property_set', $entity, $data, $propertyName); - if (empty($data)) { // @todo Do we really need to log this? $this->logIssue(new \Exception(t('Got empty property value.'))); diff --git a/modules/capx_tamper/CapxTamper.inc b/modules/capx_tamper/CapxTamper.inc new file mode 100644 index 00000000..8e98af33 --- /dev/null +++ b/modules/capx_tamper/CapxTamper.inc @@ -0,0 +1,96 @@ +mapper = $mapper; + $this->parser = $parser; + $this->processor = $processor; + } + +} + +/** + * Class CapxTamperParser. + */ +class CapxTamperParser { + + /** + * Simulates FeedsCSVParser::getMappingSources(). + * + * @return bool + * Similar to FeedsCSVParser + */ + public function getMappingSources() { + return FALSE; + } + +} + +/** + * Class CapxTamperProcessor. + */ +class CapxTamperProcessor { + + /** + * Mapper to use for processing. + * + * @var CFEntity + */ + public $mapper; + + /** + * CapxTamperProcessor constructor. + * + * @param CFEntity $mapper + * CAPx Mapper to use. + */ + public function __construct(CFEntity $mapper) { + $this->mapper = $mapper; + } + + /** + * Passes the available sources & targets. + * + * @return array + * Available sources. + */ + public function getMappings() { + $mapper_sources = capx_tamper_get_mapper_sources($this->mapper); + $sources = array(); + foreach ($mapper_sources as $target => $path) { + $sources[] = array( + 'source' => $path, + 'target' => $target, + 'unique' => FALSE, + 'language' => LANGUAGE_NONE, + ); + } + return $sources; + } + +} diff --git a/modules/capx_tamper/README.md b/modules/capx_tamper/README.md new file mode 100644 index 00000000..32d840dc --- /dev/null +++ b/modules/capx_tamper/README.md @@ -0,0 +1,20 @@ +# Stanford CAPx Tampers +#### Version 2.0-dev + +CAPx Tampers provide the option to alter the data from the API before saving it to a profile. Much of this functionality is inspired by the feeds_tamper module and additional help can be found at that resource. + +## Installation + +Install this module like [any other Drupal module](https://www.drupal.org/documentation/install/modules-themes/modules-7). + +## Configuration + +When enabled please first create a mapper. After the mapper has been created there will be an additional link titled 'Tampers' under the actions in the mapper list page. (/admin/config/capx/mappers). Click on the 'Tampers' link and choose from the options available to create your tampers. + +## Troubleshooting + +If you are experiencing issues with this module try reverting the feature first. If you are still experiencing issues try posting an issue on the GitHub issues page. + +## Contribution / Collaboration + +You are welcome to contribute functionality, bug fixes, or documentation to this module. If you would like to suggest a fix or new functionality you may add a new issue to the GitHub issue queue or you may fork this repository and submit a pull request. For more help please see [GitHub's article on fork, branch, and pull requests](https://help.github.com/articles/using-pull-requests) diff --git a/modules/capx_tamper/capx_tamper.forms.inc b/modules/capx_tamper/capx_tamper.forms.inc new file mode 100644 index 00000000..2500fc44 --- /dev/null +++ b/modules/capx_tamper/capx_tamper.forms.inc @@ -0,0 +1,516 @@ + $mapper->title))); + capx_tamper_breadcrumb($mapper); + + $form['#tree'] = TRUE; + $form['mapper'] = array( + '#type' => 'hidden', + '#value' => $mapper->getMachineName(), + ); + + $sources = capx_tamper_get_mapper_sources($mapper); + + foreach ($sources as $target => $path) { + $path_tampers = capx_tamper_load_tampers(array( + 'mapper' => $mapper->getMachineName(), + 'target' => $target, + )); + + if ($instance = field_info_instance($mapper->entity_type, $target, $mapper->bundle_type)) { + $target_label = $instance['label']; + } + else { + $target_label = ucwords($target); + } + + $form['tampers'][$target] = array( + 'caption' => array( + '#markup' => t('!to <- !from', array( + '!from' => $path, + '!to' => $target_label, + )), + ), + ); + + foreach ($path_tampers as $tamper) { + $edit_url = CAPX_TAMPER . "/{$mapper->getMachineName()}/tamper/{$tamper->id}/edit"; + $delete_url = CAPX_TAMPER . "/{$mapper->getMachineName()}/tamper/{$tamper->id}/delete"; + $operations = l(t('Edit'), $edit_url) . " | " . l(t('Delete'), $delete_url); + $plugin = feeds_tamper_get_plugin($tamper->plugin_id); + + $form['tampers'][$target][$tamper->id]['description'] = array( + '#markup' => t($tamper->description), + ); + $form['tampers'][$target][$tamper->id]['plugin_id'] = array( + '#markup' => t($plugin['name']), + ); + $form['tampers'][$target][$tamper->id]['operations'] = array( + '#markup' => $operations, + ); + $form['tampers'][$target][$tamper->id]['weight'] = array( + '#type' => 'weight', + '#title' => t('Weight'), + '#default_value' => $tamper->weight, + '#attributes' => array( + 'class' => array(capx_tamper_table_id($target) . '-weight'), + ), + ); + $form['tampers'][$target][$tamper->id]['enabled'] = array( + '#type' => 'checkbox', + '#title' => t('Enabled'), + '#default_value' => !$tamper->disabled, + ); + } + $form['tampers'][$target]['add_link'] = array( + '#markup' => l(t('Add Plugin'), CAPX_TAMPER . "/{$mapper->getMachineName()}/tamper/add/" . bin2hex("$target:$path")), + '#prefix' => '', + ); + } + + $form['save'] = array( + '#type' => 'submit', + '#value' => t('Save'), + ); + $form['#attached']['css'] = array(drupal_get_path('module', 'capx_tamper') . '/css/capx_tamper.css'); + return $form; +} + +/** + * List form submit. Sets weights & enabled/disabled. + */ +function capx_tamper_list_form_submit($form, &$form_state) { + $disabled = variable_get('capx_tamper_disabled', array()); + foreach ($form_state['values']['tampers'] as $tampers) { + foreach ($tampers as $id => $settings) { + $tamper = capx_tamper_load_tampers(array('id' => $id)); + $tamper = reset($tamper); + $tamper->weight = $settings['weight']; + capx_tamper_save_instance($tamper); + + if (!$settings['enabled']) { + $disabled[$tamper->id] = TRUE; + } + else { + unset($disabled[$tamper->id]); + } + } + } + variable_set('capx_tamper_disabled', $disabled); + drupal_set_message(t('Settings saved')); +} + +/** + * Tamper add form. + * + * @param CFEntity $mapper + * CAPx Mapper used for the tamper. + * @param string $source + * JSON string path. + * + * @return array + * Form render array. + */ +function capx_tamper_add_form($form, &$form_state, CFEntity $mapper, $target_source) { + list($target, $source) = explode(':', hex2bin($target_source), 2); + + drupal_set_title(t('Add Tamper Plugin for !to <- !from', array( + '!from' => $source, + '!to' => $target, + ))); + capx_tamper_breadcrumb($mapper, TRUE); + + $form_state['mapper'] = $mapper; + $form_state['source'] = $source; + $form_state['target'] = $target; + + // Build plugin select list. + $capx_tamper_plugins = feeds_tamper_get_plugins(); + $plugins = array(); + foreach ($capx_tamper_plugins as $plugin_id => $plugin) { + $plugins[t($plugin['category'])][$plugin_id] = t($plugin['name']); + } + ksort($plugins); + foreach ($plugins as &$p) { + asort($p); + } + + // Unsupported tampers. + unset($plugins[t('Filter')]); + unset($plugins[t('Field Collection')]); + + $machine_name = key(reset($plugins)); + if (!empty($form_state['values']['plugin_id'])) { + $machine_name = $form_state['values']['plugin_id']; + } + $plugin = feeds_tamper_get_plugin($machine_name); + + $form['plugin_id'] = array( + '#title' => t('The plugin to add'), + '#type' => 'select', + '#options' => $plugins, + '#default_value' => '', + '#tree' => TRUE, + '#ajax' => array( + 'callback' => 'capx_tamper_ajax_callback', + 'wrapper' => 'capx-tamper-plugin', + 'progress' => 'none', + ), + ); + $form['update'] = array( + '#type' => 'submit', + '#limit_validation_errors' => array(array('plugin_id')), + '#submit' => array('capx_tamper_ui_add_plugin_form_submit'), + '#value' => t('Choose'), + '#attributes' => array('class' => array('no-js')), + ); + $form['plugin']['#prefix'] = '
'; + $form['plugin']['#suffix'] = '
'; + + $form['plugin']['description'] = array( + '#title' => t('Description'), + '#type' => 'textfield', + '#default_value' => $plugin['default description'] ? t($plugin['default description']) : t($plugin['name']), + '#required' => TRUE, + '#description' => t('A useful description of what this plugin is doing.'), + ); + $form['plugin']['id'] = array( + '#title' => t('Machine name'), + '#type' => 'machine_name', + '#maxlength' => 32, + '#machine_name' => array( + 'exists' => 'capx_tamper_machine_name_callback', + 'source' => array('plugin', 'description'), + ), + '#default_value' => $machine_name, + ); + $form['plugin']['settings'] = array( + '#title' => t('Configure @name', array('@name' => $plugin['name'])), + '#type' => 'fieldset', + '#tree' => TRUE, + ); + + $parser = new CapxTamperParser(); + $processor = new CapxTamperProcessor($mapper); + $importer = new CapxTamper($mapper, $parser, $processor); + + $form['plugin']['settings'] += $plugin['form']($importer, $source, array(), $form_state); + $form['add'] = array( + '#type' => 'submit', + '#value' => t('Add'), + ); + + $form['#attached']['css'][] = drupal_get_path('module', 'capx_tamper') . '/css/capx_tamper.css'; + + return $form; +} + +/** + * Tamper add form validation. + * + * @param array $form + * Form Array. + * @param array $form_state + * Form State Array. + */ +function capx_tamper_add_form_validate($form, &$form_state) { + if ($form_state['triggering_element']['#value'] == t('Add')) { + if (capx_tamper_machine_name_callback($form_state['values']['id'], $form, $form_state)) { + form_set_error('id', t('The machine-readable name is already in use. It must be unique.')); + return; + } + $plugin_id = $form_state['values']['plugin_id']; + $plugin = feeds_tamper_get_plugin($plugin_id); + if ($plugin['validate'] && isset($form_state['values']['settings'])) { + $plugin['validate']($form_state['values']['settings']); + } + return; + } + unset($form_state['input']['id']); + unset($form_state['input']['description']); + unset($form_state['input']['settings']); +} + +/** + * Tamper add form submit to save to table. + * + * @param array $form + * Form Array. + * @param array $form_state + * Form State Array. + */ +function capx_tamper_add_form_submit($form, &$form_state) { + if ($form_state['triggering_element']['#value'] == t('Add')) { + /** @var CFEntity $mapper */ + $mapper = $form_state['mapper']; + $id = implode('-', array( + $mapper->identifier(), + $form_state['source'], + $form_state['target'], + $form_state['values']['id'], + )); + + $tamper = new stdClass(); + $tamper->id = $id; + $tamper->mapper = $mapper->identifier(); + $tamper->source = $form_state['source']; + $tamper->target = $form_state['target']; + $tamper->plugin_id = $form_state['values']['plugin_id']; + $tamper->settings = $form_state['values']['settings']; + $tamper->weight = '0'; + $tamper->description = $form_state['values']['description']; + + if (capx_tamper_save_instance($tamper)) { + $form_state['redirect'] = CAPX_TAMPER . "/{$mapper->identifier()}/tamper"; + drupal_set_message(t('Plugin %description was successfully added to %source.', array( + '%description' => $form_state['values']['description'], + '%source' => $form_state['source'], + ))); + return; + } + } + $form_state['rebuild'] = TRUE; +} + +/** + * Tamper plugin edit form. + * + * @param array $form + * Form Array. + * @param array $form_state + * Form State Array. + * @param CFEntity $mapper + * CAPx Mapper used for the tamper. + * @param string $tamper_id + * CAPx Tamper id. + * + * @return array + * Form render array. + */ +function capx_tamper_edit_form($form, &$form_state, CFEntity $mapper, $tamper_id) { + $tamper = capx_tamper_load_tampers(array('id' => $tamper_id)); + $tamper = reset($tamper); + if (!$tamper) { + drupal_not_found(); + drupal_exit(); + } + capx_tamper_breadcrumb($mapper, TRUE); + + $form_state['mapper'] = $mapper; + $form_state['tamper'] = $tamper; + + drupal_set_title(t('Edit !title', array('!title' => $tamper->description))); + + $plugin = feeds_tamper_get_plugin($tamper->plugin_id); + $form['#tree'] = TRUE; + + $form['description'] = array( + '#title' => t('Description'), + '#type' => 'textfield', + '#description' => t('A useful description of what this plugin is doing.'), + '#default_value' => $tamper->description, + ); + $form['settings'] = array( + '#title' => t('Configure @plugin', array('@plugin' => $plugin['name'])), + '#type' => 'fieldset', + '#tree' => TRUE, + ); + + $parser = new CapxTamperParser(); + $processor = new CapxTamperProcessor($mapper); + $importer = new CapxTamper($mapper, $parser, $processor); + + $form['settings'] += $plugin['form']($importer, $tamper->source, $tamper->settings, $form_state); + + $form['save'] = array( + '#type' => 'submit', + '#value' => t('Save'), + ); + + $form['#attached']['css'][] = drupal_get_path('module', 'capx_tamper') . '/css/capx_tamper.css'; + return $form; +} + +/** + * Tamper edit validation. + * + * @param array $form + * Form Array. + * @param array $form_state + * Form State Array. + */ +function capx_tamper_edit_form_validate($form, &$form_state) { + $plugin_id = $form_state['tamper']->plugin_id; + $plugin = feeds_tamper_get_plugin($plugin_id); + if ($plugin['validate']) { + $plugin['validate']($form_state['values']['settings']); + } +} + +/** + * Tamper plugin edit submit. + * + * @param array $form + * Form Array. + * @param array $form_state + * Form State Array. + */ +function capx_tamper_edit_form_submit($form, &$form_state) { + $mapper = $form_state['mapper']; + $tamper = $form_state['tamper']; + if (isset($form_state['values']['settings'])) { + $tamper->settings = $form_state['values']['settings']; + } + $tamper->description = $form_state['values']['description']; + + if (capx_tamper_save_instance($tamper)) { + drupal_set_message(t('The plugin %plugin has been updated.', array('%plugin' => $tamper->description))); + $form_state['redirect'] = CAPX_TAMPER . "/{$mapper->identifier()}/tamper"; + } +} + +/** + * Confirmation of delete tamper. + * + * @param array $form + * Form Array. + * @param array $form_state + * Form State Array. + * @param CFEntity $mapper + * CAPx Mapper used for the tamper. + * @param string $tamper_id + * CAPx Tamper id. + * + * @return array + * Form render array. + */ +function capx_tamper_delete_form($form, &$form_state, CFEntity $mapper, $tamper_id) { + capx_tamper_breadcrumb($mapper, TRUE); + $tamper = capx_tamper_load_tampers(array('id' => $tamper_id)); + $tamper = reset($tamper); + $form_state['tamper'] = $tamper; + $form_state['mapper'] = $mapper; + $question = t('Would you really like to delete the plugin @tamper?', array('@tamper' => $tamper->description)); + $button_label = t('Delete'); + + return confirm_form( + $form, + $question, + CAPX_TAMPER . "/{$mapper->identifier()}/tamper", + NULL, + $button_label + ); +} + +/** + * Tamper delete form submit. + */ +function capx_tamper_delete_form_submit($form, &$form_state) { + $tamper = $form_state['tamper']; + /** @var CFEntity $mapper */ + $mapper = $form_state['mapper']; + capx_tamper_delete_instance($tamper); + drupal_set_message(t('The plugin %plugin has been deleted from %source.', array( + '%plugin' => $tamper->description, + '%source' => $tamper->source, + ))); + $form_state['redirect'] = CAPX_TAMPER . "/{$mapper->identifier()}/tamper"; +} + +/** + * Checks for existing machine name. + * + * @param string $id + * Machine name of the new plugin. + * @param array $form + * Form Array. + * @param array $form_state + * Form State Array. + * + * @return array + * Array of the existing tamper or an empty array. + */ +function capx_tamper_machine_name_callback($id, $form, &$form_state) { + $check_id = implode('-', array( + $form_state['mapper']->identifier(), + $form_state['source'], + $form_state['target'], + $form_state['values']['id'], + )); + return capx_tamper_load_tampers(array('id' => $check_id)); +} + +/** + * Ajax callback for add plugin form. + */ +function capx_tamper_ajax_callback($form, $form_state) { + return $form['plugin']; +} + +/** + * Sets the breadcrumb links for the page. + * + * @param CFEntity $mapper + * CAPx Mapper in use. + * @param bool $show_tamper + * Show the tamper page in the breadcrumb. + */ +function capx_tamper_breadcrumb(CFEntity $mapper, $show_tamper = FALSE) { + $breadcrumb = array( + l(t('Home'), '/'), + l(t('Administration'), '/admin'), + l(t('Configuration'), '/admin/config'), + l(t('CAPx'), '/admin/config/capx'), + l(t('Map'), '/admin/config/capx/mapper'), + l(t($mapper->title), CAPX_TAMPER . "/edit/{$mapper->getMachineName()}"), + ); + + if ($show_tamper) { + $breadcrumb[] = l(t('Tamper'), CAPX_TAMPER . "/{$mapper->getMachineName()}/tamper"); + } + drupal_set_breadcrumb($breadcrumb); +} diff --git a/modules/capx_tamper/capx_tamper.info b/modules/capx_tamper/capx_tamper.info new file mode 100644 index 00000000..4bdd982a --- /dev/null +++ b/modules/capx_tamper/capx_tamper.info @@ -0,0 +1,12 @@ +name = CAPx Tamper +core = 7.x +version = 7.x-2.0-dev +package = Stanford CAPx +project = capx_tamper + +files[] = CapxTamper.inc +files[] = views/views_handler_tamper_link.inc + +dependencies[] = ctools +dependencies[] = stanford_capx +dependencies[] = feeds_tamper diff --git a/modules/capx_tamper/capx_tamper.install b/modules/capx_tamper/capx_tamper.install new file mode 100644 index 00000000..b39c9c05 --- /dev/null +++ b/modules/capx_tamper/capx_tamper.install @@ -0,0 +1,76 @@ + 'Table storing CAPx tamper instances.', + 'fields' => array( + 'id' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Id of the CAPx tamper instance.', + ), + 'mapper' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Id of the CAPx importer.', + ), + 'source' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The source field of the importer.', + ), + 'target' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'The target field of the importer.', + ), + 'plugin_id' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Id of the tamper plugin.', + ), + 'settings' => array( + 'type' => 'text', + 'size' => 'big', + 'description' => 'A serialized array of options for a CAPx Tamper plugin.', + 'serialize' => TRUE, + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'unsigned' => FALSE, + 'description' => 'The weight of a plugin instance. Plugins are executed in order.', + ), + 'description' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Description of this plugin.', + ), + ), + 'primary key' => array('id'), + 'indexes' => array( + 'mapper' => array('mapper'), + ), + ); + return $schema; +} diff --git a/modules/capx_tamper/capx_tamper.module b/modules/capx_tamper/capx_tamper.module new file mode 100644 index 00000000..84aedca5 --- /dev/null +++ b/modules/capx_tamper/capx_tamper.module @@ -0,0 +1,486 @@ + 1); + } +} + +/** + * Implements hook_theme(). + */ +function capx_tamper_theme($existing, $type, $theme, $path) { + return array( + 'capx_tamper_list_form' => array( + 'render element' => 'form', + 'file' => 'capx_tamper.theme.inc', + ), + ); +} + +/** + * Implements hook_views_data_alter(). + */ +function capx_tamper_views_data_alter(&$data) { + // Tampers link. + $data['capx_cfe']['tamper_link'] = array( + 'title' => t('Manage Tampers link'), + 'help' => t('Manage Tampers link for mappings'), + 'field' => array( + 'handler' => 'views_handler_tamper_link', + ), + ); +} + +/** + * Implements hook_menu(). + */ +function capx_tamper_menu() { + $items = array(); + + $items[CAPX_TAMPER . '/%capx_tamper_mapper/tamper'] = array( + 'title' => 'Tampers', + 'page callback' => 'capx_tamper_build_form', + 'page arguments' => array('list', 4), + 'file' => 'capx_tamper.forms.inc', + 'access arguments' => array('administer capx'), + 'type' => MENU_NORMAL_ITEM, + ); + + $items[CAPX_TAMPER . '/%capx_tamper_mapper/tamper/add/%'] = array( + 'title' => 'Add Tamper', + 'page callback' => 'capx_tamper_build_form', + 'page arguments' => array(6, 4, 7), + 'file' => 'capx_tamper.forms.inc', + 'access arguments' => array('administer capx'), + 'type' => MENU_CALLBACK, + ); + + $items[CAPX_TAMPER . '/%capx_tamper_mapper/tamper/%/edit'] = array( + 'title' => 'Tampers', + 'page callback' => 'capx_tamper_build_form', + 'page arguments' => array(7, 4, 6), + 'file' => 'capx_tamper.forms.inc', + 'access arguments' => array('administer capx'), + 'type' => MENU_CALLBACK, + ); + + $items[CAPX_TAMPER . '/%capx_tamper_mapper/tamper/%/delete'] = array( + 'title' => 'Tampers', + 'page callback' => 'capx_tamper_build_form', + 'page arguments' => array(7, 4, 6), + 'file' => 'capx_tamper.forms.inc', + 'access arguments' => array('administer capx'), + 'type' => MENU_CALLBACK, + ); + + return $items; +} + +/** + * Loader for menu & pages. + * + * @param string $mapper_id + * Machine name of the Capx Mapper. + * + * @return \CAPx\Drupal\Entities\CFEntity + * CAPx Mapper. + */ +function capx_tamper_mapper_load($mapper_id) { + $mapper = capx_cfe_load_by_machine_name($mapper_id, 'mapper'); + return $mapper; +} + +/** + * Takes the configurations of the CAPx Mapper and gives the available sources. + * + * @param CFEntity $mapper + * CAPx Mapper to collect available sources. + * + * @return array + * Associative array of keys as json paths. Values as an array of targets. + */ +function capx_tamper_get_mapper_sources(CFEntity $mapper) { + $sources = $mapper->properties; + foreach ($mapper->fields as $field_key => $field_settings) { + $field_settings = array_filter($field_settings); + if ($field_settings) { + $path = $field_settings[key($field_settings)]; + $sources[$field_key] = $path; + } + } + return $sources; +} + +/** + * Implements hook_entity_load(). + */ +function capx_tamper_entity_load($entities, $type) { + if ($type == 'capx_cfe') { + foreach ($entities as &$entity) { + if ($entity->type == 'mapper') { + $entity->tampers = capx_tamper_load_tampers(array('mapper' => $entity->machine_name)); + } + } + } +} + +/** + * Implements hook_entity_insert(). + */ +function capx_tamper_entity_insert($entity, $type) { + if ($type == 'capx_cfe' && $entity->type == 'mapper' && isset($entity->tampers)) { + /** @var CFEntity $entity */ + foreach ($entity->tampers as $tamper) { + $tamper = (array) $tamper; + $tamper['mapper'] = $entity->machine_name; + capx_tamper_save_instance($tamper); + } + } +} + +/** + * Implements hook_entity_update(). + * + * If the source does not exist after saving the mapper, delete any CAPx Tamper + * plugins that are attached. + */ +function capx_tamper_entity_update($entity, $type) { + if ($type == 'capx_cfe' && $entity->type == 'mapper' && isset($entity->tampers)) { + /** @var \CAPx\Drupal\Entities\CFEntity $entity */ + $sources = capx_tamper_get_mapper_sources($entity); + foreach ($entity->tampers as $id => $tamper) { + if (!isset($sources[$tamper->target]) || $sources[$tamper->target] != $tamper->source) { + capx_tamper_delete_instance($tamper); + } + } + } +} + +/** + * Implements hook_entity_delete(). + * + * Deletes any CAPx Tamper plugins when the mapper is deleted. + */ +function capx_tamper_entity_delete($entity, $type) { + if ($type == 'capx_cfe' && $entity->type == 'mapper' && isset($entity->tampers)) { + /** @var \CAPx\Drupal\Entities\CFEntity $entity */ + foreach ($entity->tampers as $tamper) { + capx_tamper_delete_instance($tamper); + } + } +} + +/** + * Load a single or all capx tamper plugins. + * + * @param array $conditions + * Keyed conditions of the tampers to load. + * 'mapper' => mapper machine name. + * 'id' => tamper id. + * 'source' => field name or entity property. + * + * @return array + * Single or all tampers configured. + */ +function capx_tamper_load_tampers($conditions = array()) { + $disabled = variable_get('capx_tamper_disabled', array()); + + $query = db_select('capx_tamper', 'c') + ->fields('c'); + foreach ($conditions as $key => $value) { + if (db_field_exists('capx_tamper', $key)) { + if (is_array($value)) { + $query->condition($key, $value, 'IN'); + } + else { + $query->condition($key, $value); + } + } + } + $query->orderBy('weight', 'ASC'); + $results = $query->execute(); + + $items = array(); + while ($item = $results->fetchAssoc()) { + $item['settings'] = unserialize($item['settings']); + if (isset($disabled[$item['id']])) { + $item['disabled'] = TRUE; + } + $items[] = (object) $item; + } + + return $items; +} + +/** + * Saves the tamper plugin and settings. + * + * @param object|array $tamper + * CAPx Tamper object. + * + * @return bool + * TRUE if successful. FALSE otherwise. + */ +function capx_tamper_save_instance($tamper) { + $tamper = (array) $tamper; + + // Validity check. + list($mapper_id) = explode('-', $tamper['id']); + if ($mapper_id != $tamper['mapper'] || !capx_tamper_mapper_load($mapper_id)) { + drupal_set_message(t('Invalid CAPx Mapper "%mapper" in tamper plugin "%description"', array( + '%mapper' => $mapper_id, + '%description' => $tamper['description'], + )), 'error'); + return FALSE; + } + + if (capx_tamper_load_tampers(array('id' => $tamper['id']))) { + $success = drupal_write_record('capx_tamper', $tamper, 'id'); + } + else { + $success = drupal_write_record('capx_tamper', $tamper); + } + capx_tamper_invalidate_etags($tamper['mapper']); + return $success; +} + +/** + * Deletes the given CAPx Tamper plugin. + * + * @param object $tamper + * CAPx Tamper plugin to delete. + */ +function capx_tamper_delete_instance($tamper) { + db_delete('capx_tamper') + ->condition('id', $tamper->id) + ->condition('mapper', $tamper->mapper) + ->execute(); + + $disabled = variable_get('capx_tamper_disabled', array()); + unset($disabled[$tamper->id]); + variable_set('capx_tamper_disabled', $disabled); + capx_tamper_invalidate_etags($tamper->mapper); +} + +/** + * Invalidates etags when a tamper is created, edited, or deleted. + * + * @param string $mapper_id + * Machine name of the mapper to invalidate. + */ +function capx_tamper_invalidate_etags($mapper_id) { + /** @var \CAPx\Drupal\Entities\CFEntity $mapper */ + $mapper = capx_tamper_mapper_load($mapper_id); + CAPx::invalidateEtags('mapper', $mapper); +} + +/** + * Special handler for "Copy To" tamper. + * + * Switches it to a "Copy From" and flips the sources. + * + * @param object $result + * Results object with items property. + * @param int $item_key + * Index of the item. Normally 0. + * @param string $path + * JSON path string. + * @param mixed $field + * String for most field results to manipulate. + * @param array $settings + * CAPx Tamper settings. + * @param string $source + * Original source. + */ +function capx_tamper_feeds_tamper_copy_callback(&$result, &$item_key, &$path, &$field, &$settings, &$source) { + if ($settings['to_from'] == 'to') { + $settings['to_from'] = 'from'; + $a = $path; + $path = $settings['source']; + $settings['source'] = $a; + } +} + +/** + * Recursive function, given the object's data array, change a single value. + * + * @param array $data + * Object array from json response from API. + * @param string $path + * JSON string path to desired value. + * @param mixed $value + * New value to set. + * @param int $index + * For wildcard items, change the correct indexed item. + */ +function capx_tamper_set_data_by_json_path(&$data, $path, $value, $index = 0) { + $path = trim($path, '$.'); + + $paths = explode('.', $path); + $piece = array_shift($paths); + + // End of path. + if (!$paths && isset($data[$piece])) { + $data[$piece] = $value; + return; + } + + // Wildcard, replace the piece with the index value. + if ($piece == '*') { + $piece = $index; + } + + if (isset($data[$piece])) { + capx_tamper_set_data_by_json_path($data[$piece], implode('.', $paths), $value, $index); + } +} + +/** + * Creates an HTML id/class attribute name. + * + * @param string $path + * JSON path. + * + * @return string + * HTML ready id/class converted from the path. + */ +function capx_tamper_table_id($path) { + $path = trim($path, '$.'); + return preg_replace("/[^A-Za-z0-9 ]/", '-', $path); +} + +/** + * Implements hook_capx_pre_property_set_alter(). + */ +function capx_tamper_capx_pre_property_set_alter($entity, &$data, &$property_name) { + capx_tamper_alter_data($entity, $data, $property_name); +} + +/** + * Implements hook_capx_pre_map_field_alter(). + */ +function capx_tamper_capx_pre_map_field_alter(&$entity, &$field_name, &$remote_data_paths, &$data) { + capx_tamper_alter_data($entity, $data, $field_name); +} + +/** + * Alters the data object as configured from the tampers. + * + * @param object $entity + * Entity to be operated on. + * @param array $data + * Json data array. + * @param string $field_name + * Field machine name or entity property. + */ +function capx_tamper_alter_data($entity, &$data, $field_name) { + $e = $entity->raw(); + /** @var EntityMapper $mapper */ + $mapper = $e->capxMapper; + /** @var CFEntity $cfe_mapper */ + $cfe_mapper = $mapper->getMapper(); + + + // Sets an object to be used for particular tamper plugins. + // @see feeds_tamper_copy_callback(). + $result = new stdClass(); + $items = array(); + foreach (capx_tamper_get_mapper_sources($cfe_mapper) as $path) { + $item = $mapper->getRemoteDataByJsonPath($data, $path); + if (strpos($path, '*') !== FALSE) { + $index = $mapper->getIndex(); + if (isset($item[$index])) { + $item = $item[$index]; + } + } + + if (is_array($item)) { + if (array_filter($item)) { + $item = reset($item); + } + else { + $item = NULL; + } + } + $items[0][$path] = $item; + } + $result->items = $items; + + // Loop through tamper plugins on the mapper. + foreach ($cfe_mapper->tampers as $tamper) { + if ($tamper->disabled || $tamper->target !== $field_name) { + continue; + } + $plugin = feeds_tamper_get_plugin($tamper->plugin_id); + if (function_exists($plugin['callback']) && isset($result->items[0][$tamper->source])) { + $path = $tamper->source; + $field = &$result->items[0][$path]; + + // Call any functions that have to alter the tamper before using + // the feeds tamper plugin directly. + $pre_callback = 'capx_tamper_' . $plugin['callback']; + $key = 0; + if (function_exists($pre_callback)) { + $pre_callback($result, $key, $path, $field, $tamper->settings, $tamper->source); + } + // Call the feeds tamper. Then set that value back into the data array. + $plugin['callback']($result, $key, $path, $field, $tamper->settings, $tamper->source); + capx_tamper_set_data_by_json_path($data, $path, $field, $mapper->getIndex()); + } + } +} + +/** + * Add a validation function to the import form. + * + * @param array $form + * Form Array. + * @param array $form_state + * Form State Array. + */ +function capx_tamper_form_stanford_capx_preset_import_form_alter(&$form, &$form_state) { + $form['#validate'][] = "capx_tamper_form_stanford_capx_preset_import_form_alter_validate"; +} + +/** + * Do some validation prior to saving. + * + * @param array $form + * Form Array. + * @param array $form_state + * Form State Array. + */ +function capx_tamper_form_stanford_capx_preset_import_form_alter_validate($form, &$form_state) { + $code = $form_state['values']['code']; + $cfe = entity_import('capx_cfe', $code); + + if (isset($cfe->tampers)) { + foreach ($cfe->tampers as $tamper) { + // Validity check. + list($mapper_id) = explode('-', $tamper['id']); + if ($mapper_id !== $tamper['mapper'] || $mapper_id !== $cfe->machine_name) { + form_set_error('code', t('Invalid CAPx Mapper "%mapper" in tamper plugin "%description"', array( + '%mapper' => $mapper_id, + '%description' => $tamper['description'], + ))); + } + } + } +} diff --git a/modules/capx_tamper/capx_tamper.theme.inc b/modules/capx_tamper/capx_tamper.theme.inc new file mode 100644 index 00000000..47fca8e2 --- /dev/null +++ b/modules/capx_tamper/capx_tamper.theme.inc @@ -0,0 +1,82 @@ + array( + drupal_render($form['tampers'][$target][$tamper_id]['description']), + drupal_render($form['tampers'][$target][$tamper_id]['plugin_id']), + drupal_render($form['tampers'][$target][$tamper_id]['operations']), + drupal_render($form['tampers'][$target][$tamper_id]['weight']), + drupal_render($form['tampers'][$target][$tamper_id]['enabled']), + ), + 'class' => array('draggable', 'group-' . capx_tamper_table_id($target)), + ); + } + + // Empty plugins. + if (!$rows) { + $rows[] = array( + array( + 'data' => t('No plugins defined.'), + 'colspan' => count($header), + 'class' => array('add-tamper'), + ), + ); + } + + $add_link = drupal_render($form['tampers'][$target]['add_link']); + $rows[] = array( + 'data' => array(array('data' => $add_link, 'colspan' => count($header))), + 'class' => array('capx-tamper-add'), + ); + + $output .= theme('table', array( + 'header' => $header, + 'rows' => $rows, + 'attributes' => array( + 'id' => capx_tamper_table_id($target), + 'class' => array('feeds-tamper-table'), + ), + 'caption' => $caption, + )); + + drupal_add_tabledrag(capx_tamper_table_id($target), 'order', 'sibling', capx_tamper_table_id($target) . '-weight'); + } + + $output .= drupal_render_children($form); + return $output; +} diff --git a/modules/capx_tamper/css/capx_tamper.css b/modules/capx_tamper/css/capx_tamper.css new file mode 100644 index 00000000..9c84d7c3 --- /dev/null +++ b/modules/capx_tamper/css/capx_tamper.css @@ -0,0 +1,58 @@ +/* Stolen from Rules */ +.feeds-tamper-table ul.action-links { + margin: 0; + padding: 0; +} + +.feeds-tamper-table li { + list-style-position: inside; +} + +.feeds-tamper-table ul.feeds-tamper-add a { + line-height: 1em; +} + +tr.feeds-tamper-add td { + padding-top: 2px; + padding-bottom: 2px; +} + +.feeds-tamper-table { + margin-top: 3em; + position: relative; +} + +/* Fix table drag weights to don't take extra space */ +.feeds-tamper-table .tabledrag-toggle-weight-wrapper { + float: right; + position: absolute; + right: 0px; +} + +.feeds-tamper-table caption { + font-size: 110%; + font-weight: bold; + padding-bottom: 0.5em; + text-align: left; +} + +.feeds-tamper-table tr.disabled td { + color: #aaaaaa; +} + +.feeds-tamper-table tr.disabled { + color: #aaaaaa; + background-color: #f5f5f5; +} + +/* Style add plugin form. */ + +/* hide the next button when not degrading to non-javascript browser */ +html.js .no-js { + display: none; +} + +/* Make Choose button come up next to select. */ +.form-item-plugin-id { + display: inline-block; +} diff --git a/modules/capx_tamper/views/capx_tamper.views_default_alter.inc b/modules/capx_tamper/views/capx_tamper.views_default_alter.inc new file mode 100644 index 00000000..67504840 --- /dev/null +++ b/modules/capx_tamper/views/capx_tamper.views_default_alter.inc @@ -0,0 +1,36 @@ +display['default']->handler; + + /* Field: Configuration Entity: Manage Tampers link */ + $handler->display->display_options['fields']['tamper_link']['id'] = 'tamper_link'; + $handler->display->display_options['fields']['tamper_link']['table'] = 'capx_cfe'; + $handler->display->display_options['fields']['tamper_link']['field'] = 'tamper_link'; + $handler->display->display_options['fields']['tamper_link']['label'] = ''; + $handler->display->display_options['fields']['tamper_link']['exclude'] = TRUE; + $handler->display->display_options['fields']['tamper_link']['element_label_colon'] = FALSE; + $handler->display->display_options['fields']['tamper_link']['text'] = 'Tampers'; + + /* Field: Global: Custom text */ + $action_links = $handler->display->display_options['fields']['nothing_2']; + unset($handler->display->display_options['fields']['nothing_2']); + $action_links['alter']['text'] = trim($action_links['alter']['text']); + $action_links['alter']['text'] .= ' | [tamper_link]'; + + $handler->display->display_options['fields']['nothing_2'] = $action_links; +} diff --git a/modules/capx_tamper/views/views_handler_tamper_link.inc b/modules/capx_tamper/views/views_handler_tamper_link.inc new file mode 100644 index 00000000..dc833254 --- /dev/null +++ b/modules/capx_tamper/views/views_handler_tamper_link.inc @@ -0,0 +1,59 @@ + '', 'translatable' => TRUE); + return $options; + } + + /** + * {@inheritdoc} + */ + public function options_form(&$form, &$form_state) { + $form['text'] = array( + '#type' => 'textfield', + '#title' => t('Text to display'), + '#default_value' => $this->options['text'], + ); + parent::options_form($form, $form_state); + + // The path is set by render_link function so don't allow to set it. + $form['alter']['path'] = array('#access' => FALSE); + $form['alter']['external'] = array('#access' => FALSE); + } + + /** + * {@inheritdoc} + */ + public function render($values) { + if ($entity = $this->get_value($values)) { + return $this->render_link($entity); + } + } + + /** + * {@inheritdoc} + */ + public function render_link($mapper) { + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['path'] = "admin/config/capx/mapper/" . $mapper->getMachineName() . '/tamper'; + $text = !empty($this->options['text']) ? $this->options['text'] : t('Tamper'); + return $text; + } + +} diff --git a/stanford_capx.api.php b/stanford_capx.api.php index 9d6a3407..aa7d4d49 100644 --- a/stanford_capx.api.php +++ b/stanford_capx.api.php @@ -12,7 +12,7 @@ * @param [type] $remote_data_paths [description] * @return [type] [description] */ -function hook_capx_pre_map_field_alter(&$entity, &$field_name, &$remote_data_paths) { +function hook_capx_pre_map_field_alter(&$entity, &$field_name, &$remote_data_paths, &$data) { }