From c70a4bed1157afeab03af3e3467b351e99d9fc05 Mon Sep 17 00:00:00 2001 From: Bachir Soussi Chiadmi Date: Sat, 5 Nov 2016 17:58:43 +0100 Subject: [PATCH] updated serial field --- .../modules/contrib/fields/serial/serial.inc | 165 ++++++++----- .../modules/contrib/fields/serial/serial.info | 14 +- .../contrib/fields/serial/serial.install | 33 ++- .../contrib/fields/serial/serial.module | 223 +++++++++--------- .../fields/serial/tests/serial.fields.inc | 191 +++++++++++++++ .../contrib/fields/serial/tests/serial.test | 128 ++++++++++ 6 files changed, 570 insertions(+), 184 deletions(-) create mode 100644 sites/all/modules/contrib/fields/serial/tests/serial.fields.inc create mode 100644 sites/all/modules/contrib/fields/serial/tests/serial.test diff --git a/sites/all/modules/contrib/fields/serial/serial.inc b/sites/all/modules/contrib/fields/serial/serial.inc index 28e12a3b..52ed5d11 100644 --- a/sites/all/modules/contrib/fields/serial/serial.inc +++ b/sites/all/modules/contrib/fields/serial/serial.inc @@ -41,29 +41,31 @@ function _serial_drop_table(array $field, array $instance) { } /** - * Renames serial table(s) when a content type us renamed. + * Renames serial table(s) when a entity bundle us renamed. * - * @param string $old_type - * An old node type machine name. - * @param string $new_type - * A new node type machine name. + * @param string $entity_type + * Type of entity. + * @param string $bundle_old + * An old entity bundle machine name. + * @param string $bundle_new + * A new entity bundle machine name. */ -function _serial_rename_tables($old_type, $new_type) { +function _serial_rename_tables($entity_type, $bundle_old, $bundle_new) { // Build the query to find all affected tables. $query = db_select('field_config', 'f') ->fields('f', array('field_name')); - $query->join( - 'field_config_instance', - 'i', - "f.field_name = i.field_name AND f.type = 'serial' AND i.bundle = '$new_type'" - ); + $query->join('field_config_instance', 'i', '(f.field_name = i.field_name)'); + + $query->condition('f.type', SERIAL_FIELD_TYPE); + $query->condition('i.entity_type', $entity_type); + $query->condition('i.bundle', $bundle_new); // Rename each affected table. foreach ($query->addTag('node_access')->execute() as $record) { db_rename_table( - _serial_get_table_name($old_type, $record->field_name), - _serial_get_table_name($new_type, $record->field_name) + _serial_get_table_name($entity_type, $bundle_old, $record->field_name), + _serial_get_table_name($entity_type, $bundle_new, $record->field_name) ); } } @@ -80,22 +82,26 @@ function _serial_rename_tables($old_type, $new_type) { * The name of the assistant table of the specified field instance. */ function _serial_get_field_table_name(array $field, array $instance) { - return _serial_get_table_name($instance['bundle'], $field['field_name']); + return _serial_get_table_name($instance['entity_type'], $instance['bundle'], $field['field_name']); } /** * Gets the name of the assistant table for a specific field. * + * @param string $entity_type + * Type of entity (e.g. node) * @param string $bundle - * The name of the entity type that contains the field. + * The name of the entity type that contains the field (e.g. content type) * @param string $field_name * The name of the field. * * @return string - * the name of the assistant table of the specified field. + * The name of the assistant table of the specified field. */ -function _serial_get_table_name($bundle, $field_name) { - return db_escape_table('serial_' . $bundle . '_' . $field_name); +function _serial_get_table_name($entity_type, $bundle, $field_name) { + // Remember about max length of MySQL tables - 64 symbols. + // @todo Think about improvement for this. + return db_escape_table('serial_' . md5("{$entity_type}_{$bundle}_{$field_name}")); } /** @@ -108,17 +114,18 @@ function _serial_get_table_schema() { return array( 'fields' => array( 'sid' => array( - 'type' => 'serial', - 'unsigned' => TRUE, + 'type' => SERIAL_FIELD_TYPE, 'not null' => TRUE, + 'unsigned' => TRUE, 'description' => 'The atomic serial field.', ), 'uniqid' => array( - 'description' => 'Unique temporary allocation Id.', 'type' => 'varchar', 'length' => 23, + 'default' => '', 'not null' => TRUE, - 'default' => ''), + 'description' => 'Unique temporary allocation Id.', + ), ), 'primary key' => array('sid'), 'unique keys' => array( @@ -128,8 +135,10 @@ function _serial_get_table_schema() { } /** - * Generates a unique serial value (unique per node type). + * Generates a unique serial value (unique per entity bundle). * + * @param string $entity_type + * Type of entity (e.g. node) * @param string $bundle * Containing bundle (e.g. content type). * @param string $field_name @@ -139,76 +148,102 @@ function _serial_get_table_schema() { * * @return int * the unique serial value number. + * + * @throws \Exception */ -function _serial_generate_value($bundle, $field_name, $delete = TRUE) { - // Get the name of the relevant table. - $table = _serial_get_table_name($bundle, $field_name); +function _serial_generate_value($entity_type, $bundle, $field_name, $delete = TRUE) { + $transaction = db_transaction(); - // Insert a temporary record to get a new unique serial value. - $uniqid = uniqid('', TRUE); - $sid = db_insert($table) - ->fields(array('uniqid' => $uniqid)) - ->execute(); - - // If there's a reason why it's come back undefined, reset it. - $sid = isset($sid) ? $sid : 0; - - // Delete the temporary record. - if ($delete && ($sid % 10) == 0) { - db_delete($table) - ->condition('uniqid', $uniqid) + try { + // Get the name of the relevant table. + $table = _serial_get_table_name($entity_type, $bundle, $field_name); + // Insert a temporary record to get a new unique serial value. + $uniqid = uniqid('', TRUE); + $sid = db_insert($table) + ->fields(array('uniqid' => $uniqid)) ->execute(); - } - // Return the new unique serial value. - return $sid; + // If there's a reason why it's come back undefined, reset it. + $sid = isset($sid) ? $sid : 0; + + // Delete the temporary record. + if ($delete && $sid && ($sid % 10) == 0) { + db_delete($table) + ->condition('sid', $sid, '<') + ->execute(); + } + + // Return the new unique serial value. + return $sid; + } + catch (Exception $e) { + $transaction->rollback(); + watchdog_exception('serial', $e); + throw $e; + } } /** - * Initializes the value of a new serial field in existing nodes. - * - * @todo Currently works only for nodes - should support comments and users. + * Initializes the value of a new serial field in existing entities. * + * @param string $entity_type + * Type of entity (e.g. node) * @param string $bundle * Containing bundle (e.g. content type). * @param string $field_name * The field name. * * @return int - * Number of existing nodes that have been initialized. + * Number of existing entities that have been initialized. */ -function _serial_init_old_nodes($bundle, $field_name) { - $nodes = node_load_multiple(array(), array('type' => $bundle)); +function _serial_init_old_entities($entity_type, $bundle, $field_name) { + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', $entity_type) + ->fieldCondition($field_name); - // Allocate a serial number for every old node. - foreach ($nodes as $node) { - $node->{$field_name} = array( - LANGUAGE_NONE => array( - array( - 'value' => _serial_generate_value($bundle, $field_name, FALSE), - ), - ), - ); - - node_save($node); + // The "comment" entity type does not support bundle conditions. + // @see https://api.drupal.org/api/drupal/includes!entity.inc/function/EntityFieldQuery%3A%3AentityCondition/7 + if ('comment' !== $entity_type) { + $query->entityCondition('bundle', $bundle); } - // Return the number of existing nodes that have been initialized. - return count($nodes); + $results = $query->execute(); + + if (!empty($results[$entity_type])) { + foreach ($results[$entity_type] as $entity) { + list($id, , $bundle) = entity_extract_ids($entity_type, $entity); + + $entity = entity_load_unchanged($entity_type, $id); + $entity->{$field_name} = array( + LANGUAGE_NONE => array( + array( + 'value' => _serial_generate_value($entity_type, $bundle, $field_name, FALSE), + ), + ), + ); + + field_attach_insert($entity_type, $entity); + } + + return count($results[$entity_type]); + } + + return 0; } /** * Retrieves all the managed serial fields. * - * @return array - * Pairs of node type name, field name. + * @return string[] + * Result set containing entity type, entity bundle, field name. */ function _serial_get_all_fields() { $query = db_select('field_config', 'f'); $query->join('field_config_instance', 'i', 'i.field_name = f.field_name'); - return $query->fields('i', array('bundle', 'field_name')) - ->condition('f.type', 'serial') + return $query + ->fields('i', array('entity_type', 'bundle', 'field_name')) + ->condition('f.type', SERIAL_FIELD_TYPE) ->condition('i.deleted', 0) ->execute() ->fetchAll(); diff --git a/sites/all/modules/contrib/fields/serial/serial.info b/sites/all/modules/contrib/fields/serial/serial.info index fce942fd..d503e861 100644 --- a/sites/all/modules/contrib/fields/serial/serial.info +++ b/sites/all/modules/contrib/fields/serial/serial.info @@ -1,14 +1,16 @@ name = Serial description = Defines atomic auto increment (serial) field type. -package = Fields +package = Field core = 7.x + +files[] = tests/serial.test +files[] = tests/serial.fields.inc + dependencies[] = field -files[] = serial.module - -; Information added by Drupal.org packaging script on 2015-05-19 -version = "7.x-1.4" +; Information added by Drupal.org packaging script on 2016-08-31 +version = "7.x-1.8" core = "7.x" project = "serial" -datestamp = "1432026177" +datestamp = "1472678341" diff --git a/sites/all/modules/contrib/fields/serial/serial.install b/sites/all/modules/contrib/fields/serial/serial.install index e3abdcf8..9bc09039 100644 --- a/sites/all/modules/contrib/fields/serial/serial.install +++ b/sites/all/modules/contrib/fields/serial/serial.install @@ -7,7 +7,7 @@ /** * Implements hook_field_schema(). */ -function serial_field_schema($field) { +function serial_field_schema(array $field) { $columns = array(); switch ($field['type']) { @@ -37,7 +37,7 @@ function serial_schema() { $schema = array(); foreach (_serial_get_all_fields() as $field) { - $schema[_serial_get_table_name($field->bundle, $field->field_name)] = $table_schema; + $schema[_serial_get_table_name($field->entity_type, $field->bundle, $field->field_name)] = $table_schema; } // Return the schema of all the assistant tables (one per field instance). @@ -57,7 +57,7 @@ function serial_update_7130() { // Update the schema of old assistant tables. foreach (_serial_get_all_fields() as $field) { // Empty the table. - $table = _serial_get_table_name($field->bundle, $field->field_name); + $table = _serial_get_table_name($field->entity_type, $field->bundle, $field->field_name); db_delete($table)->execute(); // Drop nid field and key. @@ -69,3 +69,30 @@ function serial_update_7130() { db_add_unique_key($table, 'uniqid', array('uniqid')); } } + +/** + * Add 'node_' to all existing serial tables. + * + * Change name: + * from: serial_{content_type}_{field_name} + * to: serial_node_{content_type}_{field_name} + */ +function serial_update_7131() { + // All old serial tables are of 'node' entity type. + foreach (db_find_tables('serial_%') as $table) { + db_rename_table($table, preg_replace('/^serial_/', 'serial_node_', $table)); + } +} + +/** + * Reorganize table names to prevent collisions with long names. + */ +function serial_update_7132() { + module_load_include('inc', 'serial'); + + foreach (db_find_tables('serial_%') as $table) { + // Explode by underscores and match old format. + list(, $entity_type, $bundle, $field_name) = explode('_', $table, 4); + db_rename_table($table, _serial_get_table_name($entity_type, $bundle, $field_name)); + } +} diff --git a/sites/all/modules/contrib/fields/serial/serial.module b/sites/all/modules/contrib/fields/serial/serial.module index 9567fab8..af382f5c 100644 --- a/sites/all/modules/contrib/fields/serial/serial.module +++ b/sites/all/modules/contrib/fields/serial/serial.module @@ -4,110 +4,35 @@ * The Serial module main file. */ +define('SERIAL_FIELD_TYPE', 'serial'); + /** * Implements hook_field_info(). */ function serial_field_info() { return array( - 'serial' => array( + SERIAL_FIELD_TYPE => array( 'label' => t('Serial'), 'description' => t('Auto increment serial field type.'), // The "property_type" should be defined for accessing the // field by entity metadata wrapper. - 'property_type' => 'serial', - 'default_widget' => 'serial', + 'property_type' => 'integer', + 'default_widget' => 'serial_widget_default', 'default_formatter' => 'serial_formatter_default', ), ); } /** - * Implements hook_field_create_instance(). + * Implements hook_field_widget_info(). */ -function serial_field_create_instance($instance) { - $field = field_read_field($instance['field_name']); - - if ('serial' == $field['type']) { - // Create the assistant table: - module_load_include('inc', 'serial'); - _serial_create_table($field, $instance); - - // Set serial values for old objects. - $old_count = _serial_init_old_nodes($instance['bundle'], $field['field_name']); - - if ($old_count > 0) { - drupal_set_message(t('Serial values have been automatically set for %count existing nodes.', array( - '%count' => $old_count, - ))); - } - } -} - -/** - * Implements hook_field_delete_instance(). - */ -function serial_field_delete_instance($instance) { - $field = field_read_field($instance['field_name']); - - if ('serial' == $field['type']) { - // Drop the assistant table. - module_load_include('inc', 'serial'); - _serial_drop_table($field, $instance); - } -} - -/** - * Implements hook_form_alter(). - */ -function serial_form_alter(&$form, $form_state, $form_id) { - if ('field_ui_field_settings_form' == $form_id && 'serial' == $form['field']['type']['#value']) { - drupal_set_message(t('Serial field %field has been created.', array( - '%field' => $form['field']['field_name']['#value'], - ))); - - drupal_goto("admin/structure/types/manage/{$form['#bundle']}/fields"); - } -} - -/** - * Implements hook_field_presave(). - */ -function serial_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) { - if (empty($items)) { - module_load_include('inc', 'serial'); - - $items = array(array('value' => _serial_generate_value($instance['bundle'], $field['field_name']))); - } -} - -/** - * Implements hook_field_is_empty(). - */ -function serial_field_is_empty($item, $field) { - // Never should be treated as empty. - return FALSE; -} - -/** - * Implements hook_node_presave(). - */ -function serial_node_presave($node) { - if (module_exists('auto_nodetitle')) { - if (auto_nodetitle_get_setting($node->type)) { - auto_nodetitle_set_title($node); - } - } -} - -/** - * Implements hook_node_type_update(). - */ -function serial_node_type_update($info) { - // Handle content type rename: - if (isset($info->old_type) && ($info->old_type != $info->type)) { - module_load_include('inc', 'serial'); - _serial_rename_tables($info->old_type, $info->type); - } +function serial_field_widget_info() { + return array( + 'serial_widget_default' => array( + 'label' => t('Hidden (Automatic)'), + 'field types' => array(SERIAL_FIELD_TYPE), + ), + ); } /** @@ -117,15 +42,95 @@ function serial_field_formatter_info() { return array( 'serial_formatter_default' => array( 'label' => t('Default'), - 'field types' => array('serial'), + 'field types' => array(SERIAL_FIELD_TYPE), ), ); } +/** + * Implements hook_field_create_instance(). + */ +function serial_field_create_instance(array $instance) { + $field = field_read_field($instance['field_name']); + + if (SERIAL_FIELD_TYPE === $field['type']) { + // Create the assistant table: + module_load_include('inc', 'serial'); + _serial_create_table($field, $instance); + + // Set serial values for old objects. + $old_count = _serial_init_old_entities($instance['entity_type'], $instance['bundle'], $field['field_name']); + + if ($old_count > 0) { + drupal_set_message(t('Serial values have been automatically set for %count existing entities.', array( + '%count' => $old_count, + ))); + } + } +} + +/** + * Implements hook_field_delete_instance(). + */ +function serial_field_delete_instance(array $instance) { + $field = field_read_field($instance['field_name']); + + if (SERIAL_FIELD_TYPE == $field['type']) { + // Drop the assistant table. + module_load_include('inc', 'serial'); + _serial_drop_table($field, $instance); + } +} + +/** + * Implements hook_field_presave(). + */ +function serial_field_presave($entity_type, $entity, array $field, array $instance, $langcode, array &$items) { + if (empty($items)) { + module_load_include('inc', 'serial'); + + $items[] = array( + 'value' => _serial_generate_value($entity_type, $instance['bundle'], $field['field_name']), + ); + } +} + +/** + * Implements hook_field_is_empty(). + */ +function serial_field_is_empty($item, $field) { + // Never should be treated as empty. + return empty($item['value']); +} + +/** + * Implements hook_clone_node_alter(). + */ +function serial_clone_node_alter(stdClass $node) { + // Reset values of all serial fields when cloning a node. + foreach (field_info_instances('node', $node->type) as $field_name => $instance) { + $info = field_info_field($field_name); + + if ('serial' === $info['type']) { + $node->{$field_name} = array(); + } + } +} + +/** + * Implements hook_field_attach_rename_bundle(). + */ +function serial_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) { + if ($bundle_old !== $bundle_new) { + module_load_include('inc', 'serial'); + _serial_rename_tables($entity_type, $bundle_old, $bundle_new); + } +} + /** * Implements hook_field_formatter_view(). */ -function serial_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { +function serial_field_formatter_view($entity_type, $entity, array $field, array $instance, $langcode, array $items, array $display) { $element = array(); // Define the field contents for the single default formatter. @@ -157,30 +162,28 @@ function serial_theme() { /** * Theme function for the default formatter. */ -function theme_serial_formatter_default($variables) { +function theme_serial_formatter_default(array $variables) { return $variables['serial_id']; } /** - * Implements hook_field_widget_info(). + * Implements hook_field_widget_form(). */ -function serial_field_widget_info() { - return array( - 'serial' => array( - 'label' => t('Hidden (Automatic)'), - 'field types' => array('serial'), - ), - ); -} +function serial_field_widget_form( + array &$form, + array &$form_state, + array $field, + array $instance, + $langcode, + array $items, + $delta, + array $element +) { + $element['#type'] = 'hidden'; -/** - * Implements hook_field_widget(). - */ -function serial_field_widget(&$form, &$form_state, $field, $instance, $items, $delta = 0) { - return array( - 'value' => array( - '#type' => 'hidden', - '#default_value' => $items[$delta]['value'], - ), - ); + if (isset($items[$delta]['value'])) { + $element['#default_value'] = $items[$delta]['value']; + } + + return array('value' => $element); } diff --git a/sites/all/modules/contrib/fields/serial/tests/serial.fields.inc b/sites/all/modules/contrib/fields/serial/tests/serial.fields.inc new file mode 100644 index 00000000..bf2dd983 --- /dev/null +++ b/sites/all/modules/contrib/fields/serial/tests/serial.fields.inc @@ -0,0 +1,191 @@ +fields = $fields; + } + + /** + * Create fields. + * + * @return self + * Object instance. + * + * @throws \FieldException + * When cannot create a field. + */ + public function create() { + foreach ($this->fields as $name => $data) { + if (!db_table_exists("field_data_$name")) { + field_create_field($data + array( + 'default' => '', + 'not null' => TRUE, + 'field_name' => $name, + )); + } + } + + return $this; + } + + /** + * Completely delete fields. + * + * This function deletes tables: "field_data_NAME" and "field_revision_NAME" + * and entries in "field_config" and "field_config_instances". + * + * @return self + * Object instance. + */ + public function delete() { + foreach (array_keys($this->fields) as $name) { + // Delete tables. + foreach (array('data', 'revision') as $table_type) { + $table = "field_{$table_type}_{$name}"; + + if (db_table_exists($table)) { + db_drop_table($table); + } + } + + // Delete entries. + foreach (array('config', 'config_instance') as $table_type) { + db_delete("field_$table_type") + ->condition('field_name', $name) + ->execute(); + } + } + + return $this; + } + + /** + * Attach existing fields into entity. + * + * @param string $entity_type + * Entity machine name. + * @param string $bundle_name + * Entity bundle name. + * + * @return self + * Object instance. + * + * @throws \FieldException + * When instance cannot be created. + */ + public function attach($entity_type, $bundle_name) { + $attached_fields = field_info_instances($entity_type, $bundle_name); + + foreach ($this->fields as $field_name => $data) { + if (empty($attached_fields[$field_name]) && field_info_field($field_name)) { + // Provide a possibility to specify field weight, depending on + // another one. + // + // @code + // $fields = array( + // 'field_title' => array( + // 'type' => 'text', + // 'label' => 'Title', + // 'widget' => array( + // 'weight' => 10, + // ), + // ), + // 'field_description' => array( + // 'type' => 'text', + // 'label' => 'Description', + // 'widget' => array( + // // Weight of this field will be "9". + // 'weight' => array('field_title', -1), + // ), + // ), + // ); + // @endcode + if (isset($data['widget']['weight']) && is_array($data['widget']['weight'])) { + list($dependent, $calc) = $data['widget']['weight']; + + $dependent = field_info_instance($entity_type, $dependent, $bundle_name); + + if (!empty($dependent)) { + $data['widget']['weight'] = $dependent['widget']['weight'] + $calc; + } + } + + field_create_instance($data + array( + 'bundle' => $bundle_name, + 'field_name' => $field_name, + 'entity_type' => $entity_type, + )); + } + } + + return $this; + } + + /** + * Get field instances. + * + * @return array[] + * Field instances. + */ + public function &getInstances() { + if (empty($this->instances)) { + $query = db_select('field_config_instance', 'fci') + ->fields('fci', array('field_name', 'data')) + ->condition('field_name', array_keys($this->fields)) + ->execute() + ->fetchAllKeyed(); + + $this->instances = array_map('unserialize', $query); + } + + return $this->instances; + } + + /** + * Field definitions getter. + * + * @return array[] + * Field definitions. + */ + public function getFields() { + return $this->fields; + } + + /** + * Save field instances. + * + * @return self + * Object instance. + * + * @throws \Exception + * @throws \InvalidMergeQueryException + */ + public function saveInstances() { + foreach ($this->instances as $field_name => $data) { + db_merge('field_config_instance') + ->fields(array('data' => serialize($data))) + ->condition('field_name', $field_name) + ->execute(); + } + + return $this; + } + +} diff --git a/sites/all/modules/contrib/fields/serial/tests/serial.test b/sites/all/modules/contrib/fields/serial/tests/serial.test new file mode 100644 index 00000000..fde9515c --- /dev/null +++ b/sites/all/modules/contrib/fields/serial/tests/serial.test @@ -0,0 +1,128 @@ + 'Serial Field', + 'group' => 'Field', + 'description' => 'Testing serial field functionality.', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp('serial', 'comment'); + + $this->drupalCreateContentType(array('type' => static::CONTENT_TYPE)); + + $this->fields = new SerialFields(array( + 'serial' => array( + 'type' => SERIAL_FIELD_TYPE, + 'label' => 'Serial', + 'settings' => array(), + ), + )); + + $this->fields + ->create() + // Attach serial field to content type and comments form. + ->attach('node', static::CONTENT_TYPE) + ->attach('comment', 'comment_node_' . static::CONTENT_TYPE); + + // Grant all known permissions for user. + $this->drupalLogin($this->drupalCreateUser(array_keys(module_invoke_all('permission')))); + } + + /** + * Create N nodes and attach N comments for the last. + * + * @param int $nodes + * Number of nodes for creation. + * @param int $comments + * Number of comments for creation. + */ + public function testSerial($nodes = 15, $comments = 6) { + for ($i = 0; $i < $nodes; $i++) { + // Open form for add new node. + $this->visit('node/add/' . str_replace('_', '-', static::CONTENT_TYPE)); + // Submit new node with filled title. + $this->drupalPost(NULL, array('title' => "Node $i"), t('Save')); + } + + // Go to editing of the last created node. + $this->visit("node/$nodes/edit"); + // Check that last created node number equal to serial ID. + $this->assertSerialField($nodes); + // Go to viewing of the last created node. + $this->visit("node/$nodes"); + + // Post comments for last created node. + for ($i = 0; $i < $comments; $i++) { + $this->drupalPost(NULL, array(self::fieldName('comment_body') => "Comment $i"), t('Save')); + } + + // Go to editing of the last created comment. + $this->visit("comment/$comments/edit"); + // Ensure the last-posted comment number equal to serial ID. + $this->assertSerialField($comments); + } + + /** + * Assert number with value of the serial field on the page. + * + * @param int $number + * The number for verification. + */ + private function assertSerialField($number) { + $this->assertFieldByXPath($this->constructFieldXpath('name', self::fieldName('serial')), $number); + } + + /** + * Visit path and assert response code. + * + * @param string $path + * Path to visit. + * @param int $code + * Expected response code. + */ + private function visit($path, $code = 200) { + $this->drupalGet($path); + $this->assertResponse($code); + } + + /** + * Convert Drupal field name into HTML. + * + * @param string $name + * Drupal field name. + * @param string $column + * Field column. + * + * @return string + * HTML input name. + */ + private static function fieldName($name, $column = 'value') { + return $name . '[' . LANGUAGE_NONE . '][0][' . $column . ']'; + } + +}