webform.inc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <?php
  2. /**
  3. * Destination class for the webform_submissions table.
  4. *
  5. * Working component types:
  6. * - email
  7. * - date ('Y-m-d')
  8. * - file (use the file id)
  9. * - markup
  10. * - pagebreak (content is ignored)
  11. * - select (looks up the key by default, pass 'source_type' => 'value' as an
  12. * argument to try to match the value)
  13. * - textfield
  14. * - textarea
  15. * - time ('H:i:s')
  16. * Untested/needs work:
  17. * - grid
  18. * - hidden
  19. */
  20. class MigrateDestinationWebformSubmission extends MigrateDestination {
  21. static public function getKeySchema() {
  22. return array(
  23. 'sid' => array(
  24. 'type' => 'int',
  25. 'not null' => TRUE,
  26. 'unsigned' => TRUE,
  27. ),
  28. );
  29. }
  30. /**
  31. * The webform of the destination.
  32. *
  33. * @var string
  34. */
  35. protected $node;
  36. public function getWebform() {
  37. return $this->node;
  38. }
  39. /**
  40. * An array mapping our custom names to component ids.
  41. *
  42. * @var array
  43. */
  44. protected $component_cids;
  45. /**
  46. * Constructs a destination for a given webform node.
  47. *
  48. * @param object $node
  49. * A node object that's type has been enabled for webform use.
  50. */
  51. public function __construct($node) {
  52. parent::__construct();
  53. if (empty($node)) {
  54. throw new Exception(t("You must provide a webform node"));
  55. }
  56. // Make sure it's a webform node.
  57. $types = webform_variable_get('webform_node_types');
  58. if (!in_array($node->type, $types)) {
  59. throw new Exception(t("The node must be configured to accept webform submissions but %type was not", array('%type' => $node->type)));
  60. }
  61. $this->node = $node;
  62. // Webform expects the component values to be keyed by cid, so we need a
  63. // hash to map prefixed field names to cid.
  64. $this->component_cids = array();
  65. foreach ($this->node->webform['components'] as $component) {
  66. $this->component_cids['data_' . $component['form_key']] = $component['cid'];
  67. }
  68. // We use the functions in this file in import() but load it here so we
  69. // only do it once.
  70. module_load_include('inc', 'webform', 'includes/webform.submissions');
  71. }
  72. public function __toString() {
  73. return t('Submission for the <a href="!link">%title</a> Webform', array(
  74. '!link' => url('node/' . $this->node->nid),
  75. '%title' => $this->node->title,
  76. ));
  77. }
  78. /**
  79. * Returns a list of fields available to be mapped.
  80. *
  81. * @return array
  82. * Keys: machine names of the fields (to be passed to addFieldMapping)
  83. * Values: Human-friendly descriptions of the fields.
  84. */
  85. public function fields() {
  86. // Fields defined by the schema. nid is omitted since it should come from
  87. // $this->node.
  88. $fields = array(
  89. 'sid' => t('The unique identifier for this submission.'),
  90. 'uid' => t('The id of the user that completed this submission.'),
  91. 'is_draft' => t('Is this a draft of the submission?'),
  92. 'submitted' => t('Timestamp of when the form was submitted.'),
  93. 'remote_addr' => t('The IP address of the user that submitted the form.'),
  94. );
  95. // Create a field for each component on the webform.
  96. foreach ($this->node->webform['components'] as $component) {
  97. // TODO: Seems like we should skip over page break components.
  98. $fields['data_' . $component['form_key']] = t('@type: @name', array('@type' => $component['type'], '@name' => $component['name']));
  99. }
  100. // Then add in anything provided by handlers.
  101. $fields += migrate_handler_invoke_all('WebformSubmission', 'fields', $this->node);
  102. return $fields;
  103. }
  104. /**
  105. * Give handlers a shot at modifying the object before saving it.
  106. *
  107. * @param $entity
  108. * Webform submission object to build. Prefilled with any fields mapped in
  109. * the Migration.
  110. * @param $source_row
  111. * Raw source data object - passed through to prepare handlers.
  112. */
  113. public function prepare($entity, stdClass $source_row) {
  114. $migration = Migration::currentMigration();
  115. $entity->migrate = array(
  116. 'machineName' => $migration->getMachineName(),
  117. );
  118. // Call any general object handlers.
  119. migrate_handler_invoke_all('WebformSubmission', 'prepare', $entity, $source_row, $this->node);
  120. // Then call any prepare handler for this specific Migration.
  121. if (method_exists($migration, 'prepare')) {
  122. $migration->prepare($entity, $source_row, $this->node);
  123. }
  124. }
  125. /**
  126. * Give handlers a shot at modifying the object (or taking additional action)
  127. * after saving it.
  128. *
  129. * @param $entity
  130. * Webform submission object to build. This is the complete object after
  131. * saving.
  132. * @param $source_row
  133. * Raw source data object - passed through to complete handlers.
  134. */
  135. public function complete($entity, stdClass $source_row) {
  136. $migration = Migration::currentMigration();
  137. // Call any general object handlers.
  138. migrate_handler_invoke_all('WebformSubmission', 'complete', $entity, $source_row, $this->node);
  139. // Then call any complete handler for this specific Migration.
  140. if (method_exists($migration, 'complete')) {
  141. $migration->complete($entity, $source_row, $this->node);
  142. }
  143. }
  144. /**
  145. * Import a record.
  146. *
  147. * @param $entity
  148. * Webform submission object to build. This is the complete object after
  149. * saving.
  150. * @param $source_row
  151. * Raw source data object - passed through to complete handlers.
  152. */
  153. public function import(stdClass $entity, stdClass $row) {
  154. // Updating previously-migrated content?
  155. $migration = Migration::currentMigration();
  156. if (isset($row->migrate_map_destid1)) {
  157. if (isset($entity->sid) && $entity->sid != $row->migrate_map_destid1) {
  158. throw new MigrateException(t("Incoming sid !sid and map destination sid !destid1 don't match",
  159. array('!sid' => $entity->sid, '!destid1' => $row->migrate_map_destid1)));
  160. }
  161. else {
  162. $entity->sid = $row->migrate_map_destid1;
  163. }
  164. }
  165. $entity->nid = $this->node->nid;
  166. // Move the data from our custom keys back to webform's component ids.
  167. $data = array();
  168. foreach ($this->component_cids as $field_name => $cid) {
  169. if (isset($entity->$field_name)) {
  170. // Move the arguments out and kill any extraneous wrapper arrays.
  171. $value = $entity->$field_name;
  172. $arguments = array();
  173. if (is_array($value) && isset($value['arguments'])) {
  174. $arguments = (array) $value['arguments'];
  175. unset($value['arguments']);
  176. $value = count($value) ? reset($value) : $value;
  177. }
  178. // Avoid a warning if they passed in an empty array.
  179. $arguments += array('source_type' => 'key');
  180. // By default passed to select components are assumed to be the
  181. // key. If the key should be looked up use the add a
  182. // array('source_type' => 'value') argument to the field mapping.
  183. $component = $this->node->webform['components'][$cid];
  184. if ($component['type'] == 'select' && $arguments['source_type'] == 'value') {
  185. $options = _webform_select_options($component);
  186. $id = array_search($value, $options);
  187. $data[$cid] = ($id === FALSE) ? NULL : $id;
  188. }
  189. else {
  190. $data[$cid] = $value;
  191. }
  192. unset($entity->$field_name);
  193. }
  194. }
  195. $entity->data = webform_submission_data($this->node, $data);
  196. // Invoke migration prepare handlers
  197. $this->prepare($entity, $row);
  198. migrate_instrument_start('webform_submission_update/insert');
  199. // Determine if it's an insert or update.
  200. if (empty($entity->sid)) {
  201. $updating = FALSE;
  202. $sid = webform_submission_insert($this->node, $entity);
  203. }
  204. else {
  205. // If the sid was specified but doesn't exist we'll need to stick an
  206. // empty record in so webform's update has something to stick to.
  207. $status = db_merge('webform_submissions')
  208. ->key(array(
  209. 'sid' => $entity->sid,
  210. ))
  211. ->insertFields(array(
  212. 'sid' => $entity->sid,
  213. 'nid' => $entity->nid,
  214. 'submitted' => $entity->submitted,
  215. 'remote_addr' => $entity->remote_addr,
  216. 'is_draft' => $entity->is_draft,
  217. 'bundle' => $entity->bundle,
  218. ))
  219. ->execute();
  220. // If db_merge() makes no changes $status is NULL so make a less
  221. // elegant comparison.
  222. $updating = MergeQuery::STATUS_INSERT !== $status;
  223. $sid = webform_submission_update($this->node, $entity);
  224. }
  225. migrate_instrument_stop('webform_submission_update/insert');
  226. if (isset($sid)) {
  227. $entity->sid = $sid;
  228. if ($updating) {
  229. $this->numUpdated++;
  230. }
  231. else {
  232. $this->numCreated++;
  233. }
  234. $return = array($sid);
  235. }
  236. else {
  237. $return = FALSE;
  238. }
  239. // Invoke migration complete handlers
  240. $this->complete($entity, $row);
  241. return $return;
  242. }
  243. /**
  244. * Delete a batch of submissions at once.
  245. *
  246. * @param $sids
  247. * Array of submission IDs to be deleted.
  248. */
  249. public function bulkRollback(array $sids) {
  250. migrate_instrument_start(__METHOD__);
  251. foreach (webform_get_submissions(array('sid' => $sids)) as $submission) {
  252. webform_submission_delete($this->node, $submission);
  253. }
  254. migrate_instrument_stop(__METHOD__);
  255. }
  256. }