summaryrefslogtreecommitdiff
path: root/kolab.org/www/drupal-7.26/modules/field/modules
diff options
context:
space:
mode:
Diffstat (limited to 'kolab.org/www/drupal-7.26/modules/field/modules')
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.info14
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.install215
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.module758
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.test441
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/list/list.info14
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/list/list.install138
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/list/list.module486
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list.test457
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.module32
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/number/number.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/number/number.install45
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/number/number.module419
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/number/number.test155
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/options/options.api.php74
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/options/options.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/options/options.module417
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/options/options.test553
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/text/text.info14
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/text/text.install95
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/text/text.js49
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/text/text.module611
-rw-r--r--kolab.org/www/drupal-7.26/modules/field/modules/text/text.test517
23 files changed, 5542 insertions, 0 deletions
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.info b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.info
new file mode 100644
index 0000000..a5a8eaf
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.info
@@ -0,0 +1,14 @@
+name = Field SQL storage
+description = Stores field data in an SQL database.
+package = Core
+version = VERSION
+core = 7.x
+dependencies[] = field
+files[] = field_sql_storage.test
+required = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.install b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.install
new file mode 100644
index 0000000..24973ab
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.install
@@ -0,0 +1,215 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the field_sql_storage module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function field_sql_storage_schema() {
+ $schema = array();
+
+ // Dynamic (data) tables.
+ if (db_table_exists('field_config')) {
+ $fields = field_read_fields(array(), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
+ drupal_load('module', 'field_sql_storage');
+ foreach ($fields as $field) {
+ if ($field['storage']['type'] == 'field_sql_storage') {
+ $schema += _field_sql_storage_schema($field);
+ }
+ }
+ }
+ return $schema;
+}
+
+/**
+ * Utility function: write field data directly to SQL storage.
+ *
+ * This function can be used for databases whose schema is at field module
+ * version 7000 or higher.
+ *
+ * @ingroup update_api
+ */
+function _update_7000_field_sql_storage_write($entity_type, $bundle, $entity_id, $revision_id, $field_name, $data) {
+ $table_name = "field_data_{$field_name}";
+ $revision_name = "field_revision_{$field_name}";
+
+ db_delete($table_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $entity_id)
+ ->execute();
+ db_delete($revision_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $entity_id)
+ ->condition('revision_id', $revision_id)
+ ->execute();
+
+ $columns = array();
+ foreach ($data as $langcode => $items) {
+ foreach ($items as $delta => $item) {
+ $record = array(
+ 'entity_type' => $entity_type,
+ 'entity_id' => $entity_id,
+ 'revision_id' => $revision_id,
+ 'bundle' => $bundle,
+ 'delta' => $delta,
+ 'language' => $langcode,
+ );
+ foreach ($item as $column => $value) {
+ $record[_field_sql_storage_columnname($field_name, $column)] = $value;
+ }
+
+ $records[] = $record;
+ // Record the columns used.
+ $columns += $record;
+ }
+ }
+
+ if ($columns) {
+ $query = db_insert($table_name)->fields(array_keys($columns));
+ $revision_query = db_insert($revision_name)->fields(array_keys($columns));
+ foreach ($records as $record) {
+ $query->values($record);
+ if ($revision_id) {
+ $revision_query->values($record);
+ }
+ }
+ $query->execute();
+ $revision_query->execute();
+ }
+}
+
+/**
+ * @addtogroup updates-6.x-to-7.x
+ * @{
+ */
+
+/**
+ * Field SQL storage update version placeholder.
+ */
+function field_sql_storage_update_7000() {
+ // Some update helper functions (such as
+ // _update_7000_field_sql_storage_write()) modify the database directly. They
+ // can be used safely only if the database schema matches the field module
+ // schema established for Drupal 7.0 (i.e. version 7000). This function exists
+ // solely to set the schema version to 7000, so that update functions calling
+ // those helpers can do so safely by declaring a dependency on
+ // field_sql_storage_update_7000().
+}
+
+/**
+ * Remove the field_config_entity_type table and store 'entity_type' strings.
+ */
+function field_sql_storage_update_7001(&$sandbox) {
+ if (!isset($sandbox['progress'])) {
+ // Collect current etids.
+ $sandbox['etids'] = db_query('SELECT etid, type FROM {field_config_entity_type}')->fetchAllKeyed();
+
+ // Collect affected tables: field data, field revision data, 'deleted'
+ // tables.
+ $sandbox['tables'] = array();
+ $results = db_select('field_config', 'fc', array('fetch' => PDO::FETCH_ASSOC))
+ ->fields('fc')
+ ->condition('storage_module', 'field_sql_storage')
+ ->execute();
+ foreach ($results as $field) {
+ if ($field['deleted']) {
+ $sandbox['tables']["field_deleted_data_{$field['id']}"] = 'data';
+ $sandbox['tables']["field_deleted_revision_{$field['id']}"] = 'revision';
+ }
+ else {
+ $sandbox['tables']["field_data_{$field['field_name']}"] = 'data';
+ $sandbox['tables']["field_revision_{$field['field_name']}"] = 'revision';
+ }
+ }
+ reset($sandbox['tables']);
+
+ $sandbox['total'] = count($sandbox['tables']);
+ $sandbox['progress'] = 0;
+ }
+
+ if ($sandbox['tables']) {
+ // Grab the next table to process.
+ $table = key($sandbox['tables']);
+ $type = array_shift($sandbox['tables']);
+
+ if (db_table_exists($table)) {
+ // Add the 'entity_type' column.
+ if (!db_field_exists($table, 'entity_type')) {
+ $column = array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The entity type this data is attached to.',
+ );
+ db_add_field($table, 'entity_type', $column);
+
+ // Populate the 'entity_type' column based on the 'etid' column.
+ foreach ($sandbox['etids'] as $etid => $entity_type) {
+ db_update($table)
+ ->fields(array('entity_type' => $entity_type))
+ ->condition('etid', $etid)
+ ->execute();
+ }
+
+ // Index the new column.
+ db_add_index($table, 'entity_type', array('entity_type'));
+ }
+
+ // Use the 'entity_type' column in the primary key.
+ db_drop_primary_key($table);
+ $primary_keys = array(
+ 'data' => array('entity_type', 'entity_id', 'deleted', 'delta', 'language'),
+ 'revision' => array('entity_type', 'entity_id', 'revision_id', 'deleted', 'delta', 'language'),
+ );
+ db_add_primary_key($table, $primary_keys[$type]);
+
+ // Drop the 'etid' column.
+ if (db_field_exists($table, 'etid')) {
+ db_drop_field($table, 'etid');
+ }
+ }
+
+ // Report progress.
+ $sandbox['progress']++;
+ $sandbox['#finished'] = min(0.99, $sandbox['progress'] / $sandbox['total']);
+ }
+ else {
+ // No more tables left: drop the field_config_entity_type table.
+ db_drop_table('field_config_entity_type');
+
+ // Drop the previous 'field_sql_storage_ENTITYTYPE_etid' system variables.
+ foreach ($sandbox['etids'] as $etid => $entity_type) {
+ variable_del('field_sql_storage_' . $entity_type . '_etid');
+ }
+
+ // We're done.
+ $sandbox['#finished'] = 1;
+ }
+}
+
+/**
+ * Fix primary keys in field revision data tables.
+ */
+function field_sql_storage_update_7002() {
+ $results = db_select('field_config', 'fc', array('fetch' => PDO::FETCH_ASSOC))
+ ->fields('fc')
+ ->condition('storage_module', 'field_sql_storage')
+ ->execute();
+ foreach ($results as $field) {
+ // Revision tables of deleted fields do not need to be fixed, since no new
+ // data is written to them.
+ if (!$field['deleted']) {
+ $table = "field_revision_{$field['field_name']}";
+ db_drop_primary_key($table);
+ db_add_primary_key($table, array('entity_type', 'entity_id', 'revision_id', 'deleted', 'delta', 'language'));
+ }
+ }
+}
+
+/**
+ * @} End of "addtogroup updates-6.x-to-7.x".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.module b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.module
new file mode 100644
index 0000000..93f2077
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.module
@@ -0,0 +1,758 @@
+<?php
+
+/**
+ * @file
+ * Default implementation of the field storage API.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function field_sql_storage_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#field_sql_storage':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The Field SQL storage module stores field data in the database. It is the default field storage module; other field storage mechanisms may be available as contributed modules. See the <a href="@field-help">Field module help page</a> for more information about fields.', array('@field-help' => url('admin/help/field'))) . '</p>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_field_storage_info().
+ */
+function field_sql_storage_field_storage_info() {
+ return array(
+ 'field_sql_storage' => array(
+ 'label' => t('Default SQL storage'),
+ 'description' => t('Stores fields in the local SQL database, using per-field tables.'),
+ ),
+ );
+}
+
+/**
+ * Generate a table name for a field data table.
+ *
+ * @param $field
+ * The field structure.
+ * @return
+ * A string containing the generated name for the database table
+ */
+function _field_sql_storage_tablename($field) {
+ if ($field['deleted']) {
+ return "field_deleted_data_{$field['id']}";
+ }
+ else {
+ return "field_data_{$field['field_name']}";
+ }
+}
+
+/**
+ * Generate a table name for a field revision archive table.
+ *
+ * @param $name
+ * The field structure.
+ * @return
+ * A string containing the generated name for the database table
+ */
+function _field_sql_storage_revision_tablename($field) {
+ if ($field['deleted']) {
+ return "field_deleted_revision_{$field['id']}";
+ }
+ else {
+ return "field_revision_{$field['field_name']}";
+ }
+}
+
+/**
+ * Generate a column name for a field data table.
+ *
+ * @param $name
+ * The name of the field
+ * @param $column
+ * The name of the column
+ * @return
+ * A string containing a generated column name for a field data
+ * table that is unique among all other fields.
+ */
+function _field_sql_storage_columnname($name, $column) {
+ return $name . '_' . $column;
+}
+
+/**
+ * Generate an index name for a field data table.
+ *
+ * @param $name
+ * The name of the field
+ * @param $column
+ * The name of the index
+ * @return
+ * A string containing a generated index name for a field data
+ * table that is unique among all other fields.
+ */
+function _field_sql_storage_indexname($name, $index) {
+ return $name . '_' . $index;
+}
+
+/**
+ * Return the database schema for a field. This may contain one or
+ * more tables. Each table will contain the columns relevant for the
+ * specified field. Leave the $field's 'columns' and 'indexes' keys
+ * empty to get only the base schema.
+ *
+ * @param $field
+ * The field structure for which to generate a database schema.
+ * @return
+ * One or more tables representing the schema for the field.
+ */
+function _field_sql_storage_schema($field) {
+ $deleted = $field['deleted'] ? 'deleted ' : '';
+ $current = array(
+ 'description' => "Data storage for {$deleted}field {$field['id']} ({$field['field_name']})",
+ 'fields' => array(
+ 'entity_type' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The entity type this data is attached to',
+ ),
+ 'bundle' => array(
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance',
+ ),
+ 'deleted' => array(
+ 'type' => 'int',
+ 'size' => 'tiny',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'A boolean indicating whether this data item has been deleted'
+ ),
+ 'entity_id' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'description' => 'The entity id this data is attached to',
+ ),
+ 'revision_id' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => FALSE,
+ 'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned',
+ ),
+ // @todo Consider storing language as integer.
+ 'language' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'description' => 'The language for this data item.',
+ ),
+ 'delta' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'description' => 'The sequence number for this data item, used for multi-value fields',
+ ),
+ ),
+ 'primary key' => array('entity_type', 'entity_id', 'deleted', 'delta', 'language'),
+ 'indexes' => array(
+ 'entity_type' => array('entity_type'),
+ 'bundle' => array('bundle'),
+ 'deleted' => array('deleted'),
+ 'entity_id' => array('entity_id'),
+ 'revision_id' => array('revision_id'),
+ 'language' => array('language'),
+ ),
+ );
+
+ $field += array('columns' => array(), 'indexes' => array(), 'foreign keys' => array());
+ // Add field columns.
+ foreach ($field['columns'] as $column_name => $attributes) {
+ $real_name = _field_sql_storage_columnname($field['field_name'], $column_name);
+ $current['fields'][$real_name] = $attributes;
+ }
+
+ // Add indexes.
+ foreach ($field['indexes'] as $index_name => $columns) {
+ $real_name = _field_sql_storage_indexname($field['field_name'], $index_name);
+ foreach ($columns as $column_name) {
+ $current['indexes'][$real_name][] = _field_sql_storage_columnname($field['field_name'], $column_name);
+ }
+ }
+
+ // Add foreign keys.
+ foreach ($field['foreign keys'] as $specifier => $specification) {
+ $real_name = _field_sql_storage_indexname($field['field_name'], $specifier);
+ $current['foreign keys'][$real_name]['table'] = $specification['table'];
+ foreach ($specification['columns'] as $column_name => $referenced) {
+ $sql_storage_column = _field_sql_storage_columnname($field['field_name'], $column_name);
+ $current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced;
+ }
+ }
+
+ // Construct the revision table.
+ $revision = $current;
+ $revision['description'] = "Revision archive storage for {$deleted}field {$field['id']} ({$field['field_name']})";
+ $revision['primary key'] = array('entity_type', 'entity_id', 'revision_id', 'deleted', 'delta', 'language');
+ $revision['fields']['revision_id']['not null'] = TRUE;
+ $revision['fields']['revision_id']['description'] = 'The entity revision id this data is attached to';
+
+ return array(
+ _field_sql_storage_tablename($field) => $current,
+ _field_sql_storage_revision_tablename($field) => $revision,
+ );
+}
+
+/**
+ * Implements hook_field_storage_create_field().
+ */
+function field_sql_storage_field_storage_create_field($field) {
+ $schema = _field_sql_storage_schema($field);
+ foreach ($schema as $name => $table) {
+ db_create_table($name, $table);
+ }
+ drupal_get_schema(NULL, TRUE);
+}
+
+/**
+ * Implements hook_field_update_forbid().
+ *
+ * Forbid any field update that changes column definitions if there is
+ * any data.
+ */
+function field_sql_storage_field_update_forbid($field, $prior_field, $has_data) {
+ if ($has_data && $field['columns'] != $prior_field['columns']) {
+ throw new FieldUpdateForbiddenException("field_sql_storage cannot change the schema for an existing field with data.");
+ }
+}
+
+/**
+ * Implements hook_field_storage_update_field().
+ */
+function field_sql_storage_field_storage_update_field($field, $prior_field, $has_data) {
+ if (! $has_data) {
+ // There is no data. Re-create the tables completely.
+
+ if (Database::getConnection()->supportsTransactionalDDL()) {
+ // If the database supports transactional DDL, we can go ahead and rely
+ // on it. If not, we will have to rollback manually if something fails.
+ $transaction = db_transaction();
+ }
+
+ try {
+ $prior_schema = _field_sql_storage_schema($prior_field);
+ foreach ($prior_schema as $name => $table) {
+ db_drop_table($name, $table);
+ }
+ $schema = _field_sql_storage_schema($field);
+ foreach ($schema as $name => $table) {
+ db_create_table($name, $table);
+ }
+ }
+ catch (Exception $e) {
+ if (Database::getConnection()->supportsTransactionalDDL()) {
+ $transaction->rollback();
+ }
+ else {
+ // Recreate tables.
+ $prior_schema = _field_sql_storage_schema($prior_field);
+ foreach ($prior_schema as $name => $table) {
+ if (!db_table_exists($name)) {
+ db_create_table($name, $table);
+ }
+ }
+ }
+ throw $e;
+ }
+ }
+ else {
+ // There is data, so there are no column changes. Drop all the
+ // prior indexes and create all the new ones, except for all the
+ // priors that exist unchanged.
+ $table = _field_sql_storage_tablename($prior_field);
+ $revision_table = _field_sql_storage_revision_tablename($prior_field);
+ foreach ($prior_field['indexes'] as $name => $columns) {
+ if (!isset($field['indexes'][$name]) || $columns != $field['indexes'][$name]) {
+ $real_name = _field_sql_storage_indexname($field['field_name'], $name);
+ db_drop_index($table, $real_name);
+ db_drop_index($revision_table, $real_name);
+ }
+ }
+ $table = _field_sql_storage_tablename($field);
+ $revision_table = _field_sql_storage_revision_tablename($field);
+ foreach ($field['indexes'] as $name => $columns) {
+ if (!isset($prior_field['indexes'][$name]) || $columns != $prior_field['indexes'][$name]) {
+ $real_name = _field_sql_storage_indexname($field['field_name'], $name);
+ $real_columns = array();
+ foreach ($columns as $column_name) {
+ $real_columns[] = _field_sql_storage_columnname($field['field_name'], $column_name);
+ }
+ db_add_index($table, $real_name, $real_columns);
+ db_add_index($revision_table, $real_name, $real_columns);
+ }
+ }
+ }
+ drupal_get_schema(NULL, TRUE);
+}
+
+/**
+ * Implements hook_field_storage_delete_field().
+ */
+function field_sql_storage_field_storage_delete_field($field) {
+ // Mark all data associated with the field for deletion.
+ $field['deleted'] = 0;
+ $table = _field_sql_storage_tablename($field);
+ $revision_table = _field_sql_storage_revision_tablename($field);
+ db_update($table)
+ ->fields(array('deleted' => 1))
+ ->execute();
+
+ // Move the table to a unique name while the table contents are being deleted.
+ $field['deleted'] = 1;
+ $new_table = _field_sql_storage_tablename($field);
+ $revision_new_table = _field_sql_storage_revision_tablename($field);
+ db_rename_table($table, $new_table);
+ db_rename_table($revision_table, $revision_new_table);
+ drupal_get_schema(NULL, TRUE);
+}
+
+/**
+ * Implements hook_field_storage_load().
+ */
+function field_sql_storage_field_storage_load($entity_type, $entities, $age, $fields, $options) {
+ $load_current = $age == FIELD_LOAD_CURRENT;
+
+ foreach ($fields as $field_id => $ids) {
+ // By the time this hook runs, the relevant field definitions have been
+ // populated and cached in FieldInfo, so calling field_info_field_by_id()
+ // on each field individually is more efficient than loading all fields in
+ // memory upfront with field_info_field_by_ids().
+ $field = field_info_field_by_id($field_id);
+ $field_name = $field['field_name'];
+ $table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
+
+ $query = db_select($table, 't')
+ ->fields('t')
+ ->condition('entity_type', $entity_type)
+ ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
+ ->condition('language', field_available_languages($entity_type, $field), 'IN')
+ ->orderBy('delta');
+
+ if (empty($options['deleted'])) {
+ $query->condition('deleted', 0);
+ }
+
+ $results = $query->execute();
+
+ $delta_count = array();
+ foreach ($results as $row) {
+ if (!isset($delta_count[$row->entity_id][$row->language])) {
+ $delta_count[$row->entity_id][$row->language] = 0;
+ }
+
+ if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->language] < $field['cardinality']) {
+ $item = array();
+ // For each column declared by the field, populate the item
+ // from the prefixed database column.
+ foreach ($field['columns'] as $column => $attributes) {
+ $column_name = _field_sql_storage_columnname($field_name, $column);
+ $item[$column] = $row->$column_name;
+ }
+
+ // Add the item to the field values for the entity.
+ $entities[$row->entity_id]->{$field_name}[$row->language][] = $item;
+ $delta_count[$row->entity_id][$row->language]++;
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_field_storage_write().
+ */
+function field_sql_storage_field_storage_write($entity_type, $entity, $op, $fields) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+ if (!isset($vid)) {
+ $vid = $id;
+ }
+
+ foreach ($fields as $field_id) {
+ $field = field_info_field_by_id($field_id);
+ $field_name = $field['field_name'];
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+
+ $all_languages = field_available_languages($entity_type, $field);
+ $field_languages = array_intersect($all_languages, array_keys((array) $entity->$field_name));
+
+ // Delete and insert, rather than update, in case a value was added.
+ if ($op == FIELD_STORAGE_UPDATE) {
+ // Delete languages present in the incoming $entity->$field_name.
+ // Delete all languages if $entity->$field_name is empty.
+ $languages = !empty($entity->$field_name) ? $field_languages : $all_languages;
+ if ($languages) {
+ db_delete($table_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->condition('language', $languages, 'IN')
+ ->execute();
+ db_delete($revision_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->condition('revision_id', $vid)
+ ->condition('language', $languages, 'IN')
+ ->execute();
+ }
+ }
+
+ // Prepare the multi-insert query.
+ $do_insert = FALSE;
+ $columns = array('entity_type', 'entity_id', 'revision_id', 'bundle', 'delta', 'language');
+ foreach ($field['columns'] as $column => $attributes) {
+ $columns[] = _field_sql_storage_columnname($field_name, $column);
+ }
+ $query = db_insert($table_name)->fields($columns);
+ $revision_query = db_insert($revision_name)->fields($columns);
+
+ foreach ($field_languages as $langcode) {
+ $items = (array) $entity->{$field_name}[$langcode];
+ $delta_count = 0;
+ foreach ($items as $delta => $item) {
+ // We now know we have someting to insert.
+ $do_insert = TRUE;
+ $record = array(
+ 'entity_type' => $entity_type,
+ 'entity_id' => $id,
+ 'revision_id' => $vid,
+ 'bundle' => $bundle,
+ 'delta' => $delta,
+ 'language' => $langcode,
+ );
+ foreach ($field['columns'] as $column => $attributes) {
+ $record[_field_sql_storage_columnname($field_name, $column)] = isset($item[$column]) ? $item[$column] : NULL;
+ }
+ $query->values($record);
+ if (isset($vid)) {
+ $revision_query->values($record);
+ }
+
+ if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
+ break;
+ }
+ }
+ }
+
+ // Execute the query if we have values to insert.
+ if ($do_insert) {
+ $query->execute();
+ $revision_query->execute();
+ }
+ }
+}
+
+/**
+ * Implements hook_field_storage_delete().
+ *
+ * This function deletes data for all fields for an entity from the database.
+ */
+function field_sql_storage_field_storage_delete($entity_type, $entity, $fields) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ foreach (field_info_instances($entity_type, $bundle) as $instance) {
+ if (isset($fields[$instance['field_id']])) {
+ $field = field_info_field_by_id($instance['field_id']);
+ field_sql_storage_field_storage_purge($entity_type, $entity, $field, $instance);
+ }
+ }
+}
+
+/**
+ * Implements hook_field_storage_purge().
+ *
+ * This function deletes data from the database for a single field on
+ * an entity.
+ */
+function field_sql_storage_field_storage_purge($entity_type, $entity, $field, $instance) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_delete($table_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->execute();
+ db_delete($revision_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->execute();
+}
+
+/**
+ * Implements hook_field_storage_query().
+ */
+function field_sql_storage_field_storage_query(EntityFieldQuery $query) {
+ if ($query->age == FIELD_LOAD_CURRENT) {
+ $tablename_function = '_field_sql_storage_tablename';
+ $id_key = 'entity_id';
+ }
+ else {
+ $tablename_function = '_field_sql_storage_revision_tablename';
+ $id_key = 'revision_id';
+ }
+ $table_aliases = array();
+ // Add tables for the fields used.
+ foreach ($query->fields as $key => $field) {
+ $tablename = $tablename_function($field);
+ // Every field needs a new table.
+ $table_alias = $tablename . $key;
+ $table_aliases[$key] = $table_alias;
+ if ($key) {
+ $select_query->join($tablename, $table_alias, "$table_alias.entity_type = $field_base_table.entity_type AND $table_alias.$id_key = $field_base_table.$id_key");
+ }
+ else {
+ $select_query = db_select($tablename, $table_alias);
+ // Allow queries internal to the Field API to opt out of the access
+ // check, for situations where the query's results should not depend on
+ // the access grants for the current user.
+ if (!isset($query->tags['DANGEROUS_ACCESS_CHECK_OPT_OUT'])) {
+ $select_query->addTag('entity_field_access');
+ }
+ $select_query->addMetaData('base_table', $tablename);
+ $select_query->fields($table_alias, array('entity_type', 'entity_id', 'revision_id', 'bundle'));
+ $field_base_table = $table_alias;
+ }
+ if ($field['cardinality'] != 1 || $field['translatable']) {
+ $select_query->distinct();
+ }
+ }
+
+ // Add field conditions. We need a fresh grouping cache.
+ drupal_static_reset('_field_sql_storage_query_field_conditions');
+ _field_sql_storage_query_field_conditions($query, $select_query, $query->fieldConditions, $table_aliases, '_field_sql_storage_columnname');
+
+ // Add field meta conditions.
+ _field_sql_storage_query_field_conditions($query, $select_query, $query->fieldMetaConditions, $table_aliases, '_field_sql_storage_query_columnname');
+
+ if (isset($query->deleted)) {
+ $select_query->condition("$field_base_table.deleted", (int) $query->deleted);
+ }
+
+ // Is there a need to sort the query by property?
+ $has_property_order = FALSE;
+ foreach ($query->order as $order) {
+ if ($order['type'] == 'property') {
+ $has_property_order = TRUE;
+ }
+ }
+
+ if ($query->propertyConditions || $has_property_order) {
+ if (empty($query->entityConditions['entity_type']['value'])) {
+ throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.');
+ }
+ $entity_type = $query->entityConditions['entity_type']['value'];
+ $entity_base_table = _field_sql_storage_query_join_entity($select_query, $entity_type, $field_base_table);
+ $query->entityConditions['entity_type']['operator'] = '=';
+ foreach ($query->propertyConditions as $property_condition) {
+ $query->addCondition($select_query, "$entity_base_table." . $property_condition['column'], $property_condition);
+ }
+ }
+ foreach ($query->entityConditions as $key => $condition) {
+ $query->addCondition($select_query, "$field_base_table.$key", $condition);
+ }
+
+ // Order the query.
+ foreach ($query->order as $order) {
+ if ($order['type'] == 'entity') {
+ $key = $order['specifier'];
+ $select_query->orderBy("$field_base_table.$key", $order['direction']);
+ }
+ elseif ($order['type'] == 'field') {
+ $specifier = $order['specifier'];
+ $field = $specifier['field'];
+ $table_alias = $table_aliases[$specifier['index']];
+ $sql_field = "$table_alias." . _field_sql_storage_columnname($field['field_name'], $specifier['column']);
+ $select_query->orderBy($sql_field, $order['direction']);
+ }
+ elseif ($order['type'] == 'property') {
+ $select_query->orderBy("$entity_base_table." . $order['specifier'], $order['direction']);
+ }
+ }
+
+ return $query->finishQuery($select_query, $id_key);
+}
+
+/**
+ * Adds the base entity table to a field query object.
+ *
+ * @param SelectQuery $select_query
+ * A SelectQuery containing at least one table as specified by
+ * _field_sql_storage_tablename().
+ * @param $entity_type
+ * The entity type for which the base table should be joined.
+ * @param $field_base_table
+ * Name of a table in $select_query. As only INNER JOINs are used, it does
+ * not matter which.
+ *
+ * @return
+ * The name of the entity base table joined in.
+ */
+function _field_sql_storage_query_join_entity(SelectQuery $select_query, $entity_type, $field_base_table) {
+ $entity_info = entity_get_info($entity_type);
+ $entity_base_table = $entity_info['base table'];
+ $entity_field = $entity_info['entity keys']['id'];
+ $select_query->join($entity_base_table, $entity_base_table, "$entity_base_table.$entity_field = $field_base_table.entity_id");
+ return $entity_base_table;
+}
+
+/**
+ * Adds field (meta) conditions to the given query objects respecting groupings.
+ *
+ * @param EntityFieldQuery $query
+ * The field query object to be processed.
+ * @param SelectQuery $select_query
+ * The SelectQuery that should get grouping conditions.
+ * @param condtions
+ * The conditions to be added.
+ * @param $table_aliases
+ * An associative array of table aliases keyed by field index.
+ * @param $column_callback
+ * A callback that should return the column name to be used for the field
+ * conditions. Accepts a field name and a field column name as parameters.
+ */
+function _field_sql_storage_query_field_conditions(EntityFieldQuery $query, SelectQuery $select_query, $conditions, $table_aliases, $column_callback) {
+ $groups = &drupal_static(__FUNCTION__, array());
+ foreach ($conditions as $key => $condition) {
+ $table_alias = $table_aliases[$key];
+ $field = $condition['field'];
+ // Add the specified condition.
+ $sql_field = "$table_alias." . $column_callback($field['field_name'], $condition['column']);
+ $query->addCondition($select_query, $sql_field, $condition);
+ // Add delta / language group conditions.
+ foreach (array('delta', 'language') as $column) {
+ if (isset($condition[$column . '_group'])) {
+ $group_name = $condition[$column . '_group'];
+ if (!isset($groups[$column][$group_name])) {
+ $groups[$column][$group_name] = $table_alias;
+ }
+ else {
+ $select_query->where("$table_alias.$column = " . $groups[$column][$group_name] . ".$column");
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Field meta condition column callback.
+ */
+function _field_sql_storage_query_columnname($field_name, $column) {
+ return $column;
+}
+
+/**
+ * Implements hook_field_storage_delete_revision().
+ *
+ * This function actually deletes the data from the database.
+ */
+function field_sql_storage_field_storage_delete_revision($entity_type, $entity, $fields) {
+ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+ if (isset($vid)) {
+ foreach ($fields as $field_id) {
+ $field = field_info_field_by_id($field_id);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_delete($revision_name)
+ ->condition('entity_type', $entity_type)
+ ->condition('entity_id', $id)
+ ->condition('revision_id', $vid)
+ ->execute();
+ }
+ }
+}
+
+/**
+ * Implements hook_field_storage_delete_instance().
+ *
+ * This function simply marks for deletion all data associated with the field.
+ */
+function field_sql_storage_field_storage_delete_instance($instance) {
+ $field = field_info_field($instance['field_name']);
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_update($table_name)
+ ->fields(array('deleted' => 1))
+ ->condition('entity_type', $instance['entity_type'])
+ ->condition('bundle', $instance['bundle'])
+ ->execute();
+ db_update($revision_name)
+ ->fields(array('deleted' => 1))
+ ->condition('entity_type', $instance['entity_type'])
+ ->condition('bundle', $instance['bundle'])
+ ->execute();
+}
+
+/**
+ * Implements hook_field_attach_rename_bundle().
+ */
+function field_sql_storage_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) {
+ // We need to account for deleted or inactive fields and instances.
+ $instances = field_read_instances(array('entity_type' => $entity_type, 'bundle' => $bundle_new), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
+ foreach ($instances as $instance) {
+ $field = field_info_field_by_id($instance['field_id']);
+ if ($field['storage']['type'] == 'field_sql_storage') {
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_update($table_name)
+ ->fields(array('bundle' => $bundle_new))
+ ->condition('entity_type', $entity_type)
+ ->condition('bundle', $bundle_old)
+ ->execute();
+ db_update($revision_name)
+ ->fields(array('bundle' => $bundle_new))
+ ->condition('entity_type', $entity_type)
+ ->condition('bundle', $bundle_old)
+ ->execute();
+ }
+ }
+}
+
+/**
+ * Implements hook_field_storage_purge_field().
+ *
+ * All field data items and instances have already been purged, so all
+ * that is left is to delete the table.
+ */
+function field_sql_storage_field_storage_purge_field($field) {
+ $table_name = _field_sql_storage_tablename($field);
+ $revision_name = _field_sql_storage_revision_tablename($field);
+ db_drop_table($table_name);
+ db_drop_table($revision_name);
+}
+
+/**
+ * Implements hook_field_storage_details().
+ */
+function field_sql_storage_field_storage_details($field) {
+ $details = array();
+ if (!empty($field['columns'])) {
+ // Add field columns.
+ foreach ($field['columns'] as $column_name => $attributes) {
+ $real_name = _field_sql_storage_columnname($field['field_name'], $column_name);
+ $columns[$column_name] = $real_name;
+ }
+ return array(
+ 'sql' => array(
+ FIELD_LOAD_CURRENT => array(
+ _field_sql_storage_tablename($field) => $columns,
+ ),
+ FIELD_LOAD_REVISION => array(
+ _field_sql_storage_revision_tablename($field) => $columns,
+ ),
+ ),
+ );
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.test b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.test
new file mode 100644
index 0000000..12c54ba
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/field_sql_storage/field_sql_storage.test
@@ -0,0 +1,441 @@
+<?php
+
+/**
+ * @file
+ * Tests for field_sql_storage.module.
+ *
+ * Field_sql_storage.module implements the default back-end storage plugin
+ * for the Field Strage API.
+ */
+
+/**
+ * Tests field storage.
+ */
+class FieldSqlStorageTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Field SQL storage tests',
+ 'description' => "Test field SQL storage module.",
+ 'group' => 'Field API'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_sql_storage', 'field', 'field_test', 'text');
+ $this->field_name = strtolower($this->randomName());
+ $this->field = array('field_name' => $this->field_name, 'type' => 'test_field', 'cardinality' => 4);
+ $this->field = field_create_field($this->field);
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle'
+ );
+ $this->instance = field_create_instance($this->instance);
+ $this->table = _field_sql_storage_tablename($this->field);
+ $this->revision_table = _field_sql_storage_revision_tablename($this->field);
+
+ }
+
+ /**
+ * Uses the mysql tables and records to verify
+ * field_load_revision works correctly.
+ */
+ function testFieldAttachLoad() {
+ $entity_type = 'test_entity';
+ $eid = 0;
+ $langcode = LANGUAGE_NONE;
+
+ $columns = array('entity_type', 'entity_id', 'revision_id', 'delta', 'language', $this->field_name . '_value');
+
+ // Insert data for four revisions to the field revisions table
+ $query = db_insert($this->revision_table)->fields($columns);
+ for ($evid = 0; $evid < 4; ++$evid) {
+ $values[$evid] = array();
+ // Note: we insert one extra value ('<=' instead of '<').
+ for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) {
+ $value = mt_rand(1, 127);
+ $values[$evid][] = $value;
+ $query->values(array($entity_type, $eid, $evid, $delta, $langcode, $value));
+ }
+ }
+ $query->execute();
+
+ // Insert data for the "most current revision" into the field table
+ $query = db_insert($this->table)->fields($columns);
+ foreach ($values[0] as $delta => $value) {
+ $query->values(array($entity_type, $eid, 0, $delta, $langcode, $value));
+ }
+ $query->execute();
+
+ // Load the "most current revision"
+ $entity = field_test_create_stub_entity($eid, 0, $this->instance['bundle']);
+ field_attach_load($entity_type, array($eid => $entity));
+ foreach ($values[0] as $delta => $value) {
+ if ($delta < $this->field['cardinality']) {
+ $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['value'], $value, "Value $delta is loaded correctly for current revision");
+ }
+ else {
+ $this->assertFalse(array_key_exists($delta, $entity->{$this->field_name}[$langcode]), "No extraneous value gets loaded for current revision.");
+ }
+ }
+
+ // Load every revision
+ for ($evid = 0; $evid < 4; ++$evid) {
+ $entity = field_test_create_stub_entity($eid, $evid, $this->instance['bundle']);
+ field_attach_load_revision($entity_type, array($eid => $entity));
+ foreach ($values[$evid] as $delta => $value) {
+ if ($delta < $this->field['cardinality']) {
+ $this->assertEqual($entity->{$this->field_name}[$langcode][$delta]['value'], $value, "Value $delta for revision $evid is loaded correctly");
+ }
+ else {
+ $this->assertFalse(array_key_exists($delta, $entity->{$this->field_name}[$langcode]), "No extraneous value gets loaded for revision $evid.");
+ }
+ }
+ }
+
+ // Add a translation in an unavailable language and verify it is not loaded.
+ $eid = $evid = 1;
+ $unavailable_language = 'xx';
+ $entity = field_test_create_stub_entity($eid, $evid, $this->instance['bundle']);
+ $values = array($entity_type, $eid, $evid, 0, $unavailable_language, mt_rand(1, 127));
+ db_insert($this->table)->fields($columns)->values($values)->execute();
+ db_insert($this->revision_table)->fields($columns)->values($values)->execute();
+ field_attach_load($entity_type, array($eid => $entity));
+ $this->assertFalse(array_key_exists($unavailable_language, $entity->{$this->field_name}), 'Field translation in an unavailable language ignored');
+ }
+
+ /**
+ * Reads mysql to verify correct data is
+ * written when using insert and update.
+ */
+ function testFieldAttachInsertAndUpdate() {
+ $entity_type = 'test_entity';
+ $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+ $langcode = LANGUAGE_NONE;
+
+ // Test insert.
+ $values = array();
+ // Note: we try to insert one extra value ('<=' instead of '<').
+ // TODO : test empty values filtering and "compression" (store consecutive deltas).
+ for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) {
+ $values[$delta]['value'] = mt_rand(1, 127);
+ }
+ $entity->{$this->field_name}[$langcode] = $rev_values[0] = $values;
+ field_attach_insert($entity_type, $entity);
+
+ $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
+ foreach ($values as $delta => $value) {
+ if ($delta < $this->field['cardinality']) {
+ $this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], format_string("Value %delta is inserted correctly", array('%delta' => $delta)));
+ }
+ else {
+ $this->assertFalse(array_key_exists($delta, $rows), "No extraneous value gets inserted.");
+ }
+ }
+
+ // Test update.
+ $entity = field_test_create_stub_entity(0, 1, $this->instance['bundle']);
+ $values = array();
+ // Note: we try to update one extra value ('<=' instead of '<').
+ for ($delta = 0; $delta <= $this->field['cardinality']; $delta++) {
+ $values[$delta]['value'] = mt_rand(1, 127);
+ }
+ $entity->{$this->field_name}[$langcode] = $rev_values[1] = $values;
+ field_attach_update($entity_type, $entity);
+ $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
+ foreach ($values as $delta => $value) {
+ if ($delta < $this->field['cardinality']) {
+ $this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], format_string("Value %delta is updated correctly", array('%delta' => $delta)));
+ }
+ else {
+ $this->assertFalse(array_key_exists($delta, $rows), "No extraneous value gets updated.");
+ }
+ }
+
+ // Check that data for both revisions are in the revision table.
+ // We make sure each value is stored correctly, then unset it.
+ // When an entire revision's values are unset (remembering that we
+ // put one extra value in $values per revision), unset the entire
+ // revision. Then, if $rev_values is empty at the end, all
+ // revision data was found.
+ $results = db_select($this->revision_table, 't')->fields('t')->execute();
+ foreach ($results as $row) {
+ $this->assertEqual($row->{$this->field_name . '_value'}, $rev_values[$row->revision_id][$row->delta]['value'], "Value {$row->delta} for revision {$row->revision_id} stored correctly");
+ unset($rev_values[$row->revision_id][$row->delta]);
+ if (count($rev_values[$row->revision_id]) == 1) {
+ unset($rev_values[$row->revision_id]);
+ }
+ }
+ $this->assertTrue(empty($rev_values), "All values for all revisions are stored in revision table {$this->revision_table}");
+
+ // Check that update leaves the field data untouched if
+ // $entity->{$field_name} is absent.
+ unset($entity->{$this->field_name});
+ field_attach_update($entity_type, $entity);
+ $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
+ foreach ($values as $delta => $value) {
+ if ($delta < $this->field['cardinality']) {
+ $this->assertEqual($rows[$delta][$this->field_name . '_value'], $value['value'], format_string("Update with no field_name entry leaves value %delta untouched", array('%delta' => $delta)));
+ }
+ }
+
+ // Check that update with an empty $entity->$field_name empties the field.
+ $entity->{$this->field_name} = NULL;
+ field_attach_update($entity_type, $entity);
+ $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', PDO::FETCH_ASSOC);
+ $this->assertEqual(count($rows), 0, "Update with an empty field_name entry empties the field.");
+ }
+
+ /**
+ * Tests insert and update with missing or NULL fields.
+ */
+ function testFieldAttachSaveMissingData() {
+ $entity_type = 'test_entity';
+ $entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
+ $langcode = LANGUAGE_NONE;
+
+ // Insert: Field is missing
+ field_attach_insert($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 0, 'Missing field results in no inserts');
+
+ // Insert: Field is NULL
+ $entity->{$this->field_name} = NULL;
+ field_attach_insert($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 0, 'NULL field results in no inserts');
+
+ // Add some real data
+ $entity->{$this->field_name}[$langcode] = array(0 => array('value' => 1));
+ field_attach_insert($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 1, 'Field data saved');
+
+ // Update: Field is missing. Data should survive.
+ unset($entity->{$this->field_name});
+ field_attach_update($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 1, 'Missing field leaves data in table');
+
+ // Update: Field is NULL. Data should be wiped.
+ $entity->{$this->field_name} = NULL;
+ field_attach_update($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 0, 'NULL field leaves no data in table');
+
+ // Add a translation in an unavailable language.
+ $unavailable_language = 'xx';
+ db_insert($this->table)
+ ->fields(array('entity_type', 'bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'language'))
+ ->values(array($entity_type, $this->instance['bundle'], 0, 0, 0, 0, $unavailable_language))
+ ->execute();
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 1, 'Field translation in an unavailable language saved.');
+
+ // Again add some real data.
+ $entity->{$this->field_name}[$langcode] = array(0 => array('value' => 1));
+ field_attach_insert($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 2, 'Field data saved.');
+
+ // Update: Field translation is missing but field is not empty. Translation
+ // data should survive.
+ $entity->{$this->field_name}[$unavailable_language] = array(mt_rand(1, 127));
+ unset($entity->{$this->field_name}[$langcode]);
+ field_attach_update($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 2, 'Missing field translation leaves data in table.');
+
+ // Update: Field translation is NULL but field is not empty. Translation
+ // data should be wiped.
+ $entity->{$this->field_name}[$langcode] = NULL;
+ field_attach_update($entity_type, $entity);
+ $count = db_select($this->table)
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($count, 1, 'NULL field translation is wiped.');
+ }
+
+ /**
+ * Test trying to update a field with data.
+ */
+ function testUpdateFieldSchemaWithData() {
+ // Create a decimal 5.2 field and add some data.
+ $field = array('field_name' => 'decimal52', 'type' => 'number_decimal', 'settings' => array('precision' => 5, 'scale' => 2));
+ $field = field_create_field($field);
+ $instance = array('field_name' => 'decimal52', 'entity_type' => 'test_entity', 'bundle' => 'test_bundle');
+ $instance = field_create_instance($instance);
+ $entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
+ $entity->decimal52[LANGUAGE_NONE][0]['value'] = '1.235';
+ field_attach_insert('test_entity', $entity);
+
+ // Attempt to update the field in a way that would work without data.
+ $field['settings']['scale'] = 3;
+ try {
+ field_update_field($field);
+ $this->fail(t('Cannot update field schema with data.'));
+ }
+ catch (FieldException $e) {
+ $this->pass(t('Cannot update field schema with data.'));
+ }
+ }
+
+ /**
+ * Test that failure to create fields is handled gracefully.
+ */
+ function testFieldUpdateFailure() {
+ // Create a text field.
+ $field = array('field_name' => 'test_text', 'type' => 'text', 'settings' => array('max_length' => 255));
+ $field = field_create_field($field);
+
+ // Attempt to update the field in a way that would break the storage.
+ $prior_field = $field;
+ $field['settings']['max_length'] = -1;
+ try {
+ field_update_field($field);
+ $this->fail(t('Update succeeded.'));
+ }
+ catch (Exception $e) {
+ $this->pass(t('Update properly failed.'));
+ }
+
+ // Ensure that the field tables are still there.
+ foreach (_field_sql_storage_schema($prior_field) as $table_name => $table_info) {
+ $this->assertTrue(db_table_exists($table_name), format_string('Table %table exists.', array('%table' => $table_name)));
+ }
+ }
+
+ /**
+ * Test adding and removing indexes while data is present.
+ */
+ function testFieldUpdateIndexesWithData() {
+
+ // Create a decimal field.
+ $field_name = 'testfield';
+ $field = array('field_name' => $field_name, 'type' => 'text');
+ $field = field_create_field($field);
+ $instance = array('field_name' => $field_name, 'entity_type' => 'test_entity', 'bundle' => 'test_bundle');
+ $instance = field_create_instance($instance);
+ $tables = array(_field_sql_storage_tablename($field), _field_sql_storage_revision_tablename($field));
+
+ // Verify the indexes we will create do not exist yet.
+ foreach ($tables as $table) {
+ $this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value'), format_string("No index named value exists in %table", array('%table' => $table)));
+ $this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value_format'), format_string("No index named value_format exists in %table", array('%table' => $table)));
+ }
+
+ // Add data so the table cannot be dropped.
+ $entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
+ $entity->{$field_name}[LANGUAGE_NONE][0]['value'] = 'field data';
+ field_attach_insert('test_entity', $entity);
+
+ // Add an index
+ $field = array('field_name' => $field_name, 'indexes' => array('value' => array('value')));
+ field_update_field($field);
+ foreach ($tables as $table) {
+ $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), format_string("Index on value created in %table", array('%table' => $table)));
+ }
+
+ // Add a different index, removing the existing custom one.
+ $field = array('field_name' => $field_name, 'indexes' => array('value_format' => array('value', 'format')));
+ field_update_field($field);
+ foreach ($tables as $table) {
+ $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value_format"), format_string("Index on value_format created in %table", array('%table' => $table)));
+ $this->assertFalse(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), format_string("Index on value removed in %table", array('%table' => $table)));
+ }
+
+ // Verify that the tables were not dropped.
+ $entity = field_test_create_stub_entity(0, 0, $instance['bundle']);
+ field_attach_load('test_entity', array(0 => $entity));
+ $this->assertEqual($entity->{$field_name}[LANGUAGE_NONE][0]['value'], 'field data', "Index changes performed without dropping the tables");
+ }
+
+ /**
+ * Test the storage details.
+ */
+ function testFieldStorageDetails() {
+ $current = _field_sql_storage_tablename($this->field);
+ $revision = _field_sql_storage_revision_tablename($this->field);
+
+ // Retrieve the field and instance with field_info so the storage details are attached.
+ $field = field_info_field($this->field['field_name']);
+ $instance = field_info_instance($this->instance['entity_type'], $this->instance['field_name'], $this->instance['bundle']);
+
+ // The storage details are indexed by a storage engine type.
+ $this->assertTrue(array_key_exists('sql', $field['storage']['details']), 'The storage type is SQL.');
+
+ // The SQL details are indexed by table name.
+ $details = $field['storage']['details']['sql'];
+ $this->assertTrue(array_key_exists($current, $details[FIELD_LOAD_CURRENT]), 'Table name is available in the instance array.');
+ $this->assertTrue(array_key_exists($revision, $details[FIELD_LOAD_REVISION]), 'Revision table name is available in the instance array.');
+
+ // Test current and revision storage details together because the columns
+ // are the same.
+ foreach ((array) $this->field['columns'] as $column_name => $attributes) {
+ $storage_column_name = _field_sql_storage_columnname($this->field['field_name'], $column_name);
+ $this->assertEqual($details[FIELD_LOAD_CURRENT][$current][$column_name], $storage_column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $current)));
+ $this->assertEqual($details[FIELD_LOAD_REVISION][$revision][$column_name], $storage_column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => $revision)));
+ }
+ }
+
+ /**
+ * Test foreign key support.
+ */
+ function testFieldSqlStorageForeignKeys() {
+ // Create a 'shape' field, with a configurable foreign key (see
+ // field_test_field_schema()).
+ $field_name = 'testfield';
+ $foreign_key_name = 'shape';
+ $field = array('field_name' => $field_name, 'type' => 'shape', 'settings' => array('foreign_key_name' => $foreign_key_name));
+ field_create_field($field);
+
+ // Retrieve the field definition and check that the foreign key is in place.
+ $field = field_info_field($field_name);
+ $this->assertEqual($field['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name preserved through CRUD');
+ $this->assertEqual($field['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name preserved through CRUD');
+
+ // Update the field settings, it should update the foreign key definition
+ // too.
+ $foreign_key_name = 'color';
+ $field['settings']['foreign_key_name'] = $foreign_key_name;
+ field_update_field($field);
+
+ // Retrieve the field definition and check that the foreign key is in place.
+ $field = field_info_field($field_name);
+ $this->assertEqual($field['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name modified after update');
+ $this->assertEqual($field['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name modified after update');
+
+ // Now grab the SQL schema and verify that too.
+ $schema = drupal_get_schema(_field_sql_storage_tablename($field), TRUE);
+ $this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema');
+ $foreign_key = reset($schema['foreign keys']);
+ $foreign_key_column = _field_sql_storage_columnname($field['field_name'], $foreign_key_name);
+ $this->assertEqual($foreign_key['table'], $foreign_key_name, 'Foreign key table name preserved in the schema');
+ $this->assertEqual($foreign_key['columns'][$foreign_key_column], 'id', 'Foreign key column name preserved in the schema');
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/list/list.info b/kolab.org/www/drupal-7.26/modules/field/modules/list/list.info
new file mode 100644
index 0000000..2aef100
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/list/list.info
@@ -0,0 +1,14 @@
+name = List
+description = Defines list field types. Use with Options to create selection lists.
+package = Core
+version = VERSION
+core = 7.x
+dependencies[] = field
+dependencies[] = options
+files[] = tests/list.test
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/list/list.install b/kolab.org/www/drupal-7.26/modules/field/modules/list/list.install
new file mode 100644
index 0000000..2386f04
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/list/list.install
@@ -0,0 +1,138 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the list module.
+ */
+
+/**
+ * Implements hook_field_schema().
+ */
+function list_field_schema($field) {
+ switch ($field['type']) {
+ case 'list_text':
+ $columns = array(
+ 'value' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => FALSE,
+ ),
+ );
+ break;
+ case 'list_float':
+ $columns = array(
+ 'value' => array(
+ 'type' => 'float',
+ 'not null' => FALSE,
+ ),
+ );
+ break;
+ case 'list_integer':
+ case 'list_boolean':
+ $columns = array(
+ 'value' => array(
+ 'type' => 'int',
+ 'not null' => FALSE,
+ ),
+ );
+ break;
+ }
+ return array(
+ 'columns' => $columns,
+ 'indexes' => array(
+ 'value' => array('value'),
+ ),
+ );
+}
+
+/**
+ * Rename the list field types and change 'allowed_values' format.
+ */
+function list_update_7001() {
+ $fields = _update_7000_field_read_fields(array('module' => 'list'));
+ foreach ($fields as $field) {
+ $update = array();
+
+ // Translate the old string format into the new array format.
+ $allowed_values = $field['settings']['allowed_values'];
+ if (is_string($allowed_values)) {
+ $position_keys = ($field['type'] == 'list');
+ $allowed_values = _list_update_7001_extract_allowed_values($allowed_values, $position_keys);
+
+ // Additionally, float keys need to be disambiguated ('.5' is '0.5').
+ if ($field['type'] == 'list_number' && !empty($allowed_values)) {
+ $keys = array_map(create_function('$a', 'return (string) (float) $a;'), array_keys($allowed_values));
+ $allowed_values = array_combine($keys, array_values($allowed_values));
+ }
+
+ // Place the new setting in the existing serialized 'data' column.
+ $data = db_query("SELECT data FROM {field_config} WHERE id = :id", array(':id' => $field['id']))->fetchField();
+ $data = unserialize($data);
+ $data['settings']['allowed_values'] = $allowed_values;
+ $update['data'] = serialize($data);
+ }
+
+ // Rename field types.
+ $types = array('list' => 'list_integer', 'list_number' => 'list_float');
+ if (isset($types[$field['type']])) {
+ $update['type'] = $types[$field['type']];
+ }
+
+ // Save the new data.
+ if ($update) {
+ $query = db_update('field_config')
+ ->condition('id', $field['id'])
+ ->fields($update)
+ ->execute();
+ }
+ }
+}
+
+/**
+ * Helper function for list_update_7001: extract allowed values from a string.
+ *
+ * This reproduces the parsing logic in use before D7 RC2.
+ */
+function _list_update_7001_extract_allowed_values($string, $position_keys) {
+ $values = array();
+
+ $list = explode("\n", $string);
+ $list = array_map('trim', $list);
+ $list = array_filter($list, 'strlen');
+
+ foreach ($list as $key => $value) {
+ // Check for a manually specified key.
+ if (strpos($value, '|') !== FALSE) {
+ list($key, $value) = explode('|', $value);
+ }
+ // Otherwise see if we need to use the value as the key. The "list" type
+ // will automatically convert non-keyed lines to integers.
+ elseif (!$position_keys) {
+ $key = $value;
+ }
+ $values[$key] = (isset($value) && $value !== '') ? $value : $key;
+ }
+
+ return $values;
+}
+
+/**
+ * @addtogroup updates-7.x-extra
+ * @{
+ */
+
+/**
+ * Re-apply list_update_7001() for deleted fields.
+ */
+function list_update_7002() {
+ // See http://drupal.org/node/1022924: list_update_7001() intitally
+ // overlooked deleted fields, which then caused fatal errors when the fields
+ // were being purged.
+ // list_update_7001() has the required checks to ensure it is reentrant, so
+ // it can simply be executed once more..
+ list_update_7001();
+}
+
+/**
+ * @} End of "addtogroup updates-7.x-extra".
+ */
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/list/list.module b/kolab.org/www/drupal-7.26/modules/field/modules/list/list.module
new file mode 100644
index 0000000..634c134
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/list/list.module
@@ -0,0 +1,486 @@
+<?php
+
+/**
+ * @file
+ * Defines list field types that can be used with the Options module.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function list_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#list':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The List module defines various fields for storing a list of items, for use with the Field module. Usually these items are entered through a select list, checkboxes, or radio buttons. See the <a href="@field-help">Field module help page</a> for more information about fields.', array('@field-help' => url('admin/help/field'))) . '</p>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_field_info().
+ */
+function list_field_info() {
+ return array(
+ 'list_integer' => array(
+ 'label' => t('List (integer)'),
+ 'description' => t("This field stores integer values from a list of allowed 'value => label' pairs, i.e. 'Lifetime in days': 1 => 1 day, 7 => 1 week, 31 => 1 month."),
+ 'settings' => array('allowed_values' => array(), 'allowed_values_function' => ''),
+ 'default_widget' => 'options_select',
+ 'default_formatter' => 'list_default',
+ ),
+ 'list_float' => array(
+ 'label' => t('List (float)'),
+ 'description' => t("This field stores float values from a list of allowed 'value => label' pairs, i.e. 'Fraction': 0 => 0, .25 => 1/4, .75 => 3/4, 1 => 1."),
+ 'settings' => array('allowed_values' => array(), 'allowed_values_function' => ''),
+ 'default_widget' => 'options_select',
+ 'default_formatter' => 'list_default',
+ ),
+ 'list_text' => array(
+ 'label' => t('List (text)'),
+ 'description' => t("This field stores text values from a list of allowed 'value => label' pairs, i.e. 'US States': IL => Illinois, IA => Iowa, IN => Indiana."),
+ 'settings' => array('allowed_values' => array(), 'allowed_values_function' => ''),
+ 'default_widget' => 'options_select',
+ 'default_formatter' => 'list_default',
+ ),
+ 'list_boolean' => array(
+ 'label' => t('Boolean'),
+ 'description' => t('This field stores simple on/off or yes/no options.'),
+ 'settings' => array('allowed_values' => array(), 'allowed_values_function' => ''),
+ 'default_widget' => 'options_buttons',
+ 'default_formatter' => 'list_default',
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_settings_form().
+ */
+function list_field_settings_form($field, $instance, $has_data) {
+ $settings = $field['settings'];
+
+ switch ($field['type']) {
+ case 'list_integer':
+ case 'list_float':
+ case 'list_text':
+ $form['allowed_values'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Allowed values list'),
+ '#default_value' => list_allowed_values_string($settings['allowed_values']),
+ '#rows' => 10,
+ '#element_validate' => array('list_allowed_values_setting_validate'),
+ '#field_has_data' => $has_data,
+ '#field' => $field,
+ '#field_type' => $field['type'],
+ '#access' => empty($settings['allowed_values_function']),
+ );
+
+ $description = '<p>' . t('The possible values this field can contain. Enter one value per line, in the format key|label.');
+ if ($field['type'] == 'list_integer' || $field['type'] == 'list_float') {
+ $description .= '<br/>' . t('The key is the stored value, and must be numeric. The label will be used in displayed values and edit forms.');
+ $description .= '<br/>' . t('The label is optional: if a line contains a single number, it will be used as key and label.');
+ $description .= '<br/>' . t('Lists of labels are also accepted (one label per line), only if the field does not hold any values yet. Numeric keys will be automatically generated from the positions in the list.');
+ }
+ else {
+ $description .= '<br/>' . t('The key is the stored value. The label will be used in displayed values and edit forms.');
+ $description .= '<br/>' . t('The label is optional: if a line contains a single string, it will be used as key and label.');
+ }
+ $description .= '</p>';
+ $form['allowed_values']['#description'] = $description;
+
+ break;
+
+ case 'list_boolean':
+ $values = $settings['allowed_values'];
+ $off_value = array_shift($values);
+ $on_value = array_shift($values);
+
+ $form['allowed_values'] = array(
+ '#type' => 'value',
+ '#description' => '',
+ '#value_callback' => 'list_boolean_allowed_values_callback',
+ '#access' => empty($settings['allowed_values_function']),
+ );
+ $form['allowed_values']['on'] = array(
+ '#type' => 'textfield',
+ '#title' => t('On value'),
+ '#default_value' => $on_value,
+ '#required' => FALSE,
+ '#description' => t('If left empty, "1" will be used.'),
+ // Change #parents to make sure the element is not saved into field
+ // settings.
+ '#parents' => array('on'),
+ );
+ $form['allowed_values']['off'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Off value'),
+ '#default_value' => $off_value,
+ '#required' => FALSE,
+ '#description' => t('If left empty, "0" will be used.'),
+ // Change #parents to make sure the element is not saved into field
+ // settings.
+ '#parents' => array('off'),
+ );
+
+ // Link the allowed value to the on / off elements to prepare for the rare
+ // case of an alter changing #parents.
+ $form['allowed_values']['#on_parents'] = &$form['allowed_values']['on']['#parents'];
+ $form['allowed_values']['#off_parents'] = &$form['allowed_values']['off']['#parents'];
+
+ break;
+ }
+
+ // Alter the description for allowed values depending on the widget type.
+ if ($instance['widget']['type'] == 'options_onoff') {
+ $form['allowed_values']['#description'] .= '<p>' . t("For a 'single on/off checkbox' widget, define the 'off' value first, then the 'on' value in the <strong>Allowed values</strong> section. Note that the checkbox will be labeled with the label of the 'on' value.") . '</p>';
+ }
+ elseif ($instance['widget']['type'] == 'options_buttons') {
+ $form['allowed_values']['#description'] .= '<p>' . t("The 'checkboxes/radio buttons' widget will display checkboxes if the <em>Number of values</em> option is greater than 1 for this field, otherwise radios will be displayed.") . '</p>';
+ }
+ $form['allowed_values']['#description'] .= '<p>' . t('Allowed HTML tags in labels: @tags', array('@tags' => _field_filter_xss_display_allowed_tags())) . '</p>';
+
+ $form['allowed_values_function'] = array(
+ '#type' => 'value',
+ '#value' => $settings['allowed_values_function'],
+ );
+ $form['allowed_values_function_display'] = array(
+ '#type' => 'item',
+ '#title' => t('Allowed values list'),
+ '#markup' => t('The value of this field is being determined by the %function function and may not be changed.', array('%function' => $settings['allowed_values_function'])),
+ '#access' => !empty($settings['allowed_values_function']),
+ );
+
+ return $form;
+}
+
+/**
+ * Element validate callback; check that the entered values are valid.
+ */
+function list_allowed_values_setting_validate($element, &$form_state) {
+ $field = $element['#field'];
+ $has_data = $element['#field_has_data'];
+ $field_type = $field['type'];
+ $generate_keys = ($field_type == 'list_integer' || $field_type == 'list_float') && !$has_data;
+
+ $values = list_extract_allowed_values($element['#value'], $field['type'], $generate_keys);
+
+ if (!is_array($values)) {
+ form_error($element, t('Allowed values list: invalid input.'));
+ }
+ else {
+ // Check that keys are valid for the field type.
+ foreach ($values as $key => $value) {
+ if ($field_type == 'list_integer' && !preg_match('/^-?\d+$/', $key)) {
+ form_error($element, t('Allowed values list: keys must be integers.'));
+ break;
+ }
+ if ($field_type == 'list_float' && !is_numeric($key)) {
+ form_error($element, t('Allowed values list: each key must be a valid integer or decimal.'));
+ break;
+ }
+ elseif ($field_type == 'list_text' && drupal_strlen($key) > 255) {
+ form_error($element, t('Allowed values list: each key must be a string at most 255 characters long.'));
+ break;
+ }
+ }
+
+ // Prevent removing values currently in use.
+ if ($has_data) {
+ $lost_keys = array_diff(array_keys($field['settings']['allowed_values']), array_keys($values));
+ if (_list_values_in_use($field, $lost_keys)) {
+ form_error($element, t('Allowed values list: some values are being removed while currently in use.'));
+ }
+ }
+
+ form_set_value($element, $values, $form_state);
+ }
+}
+
+/**
+* Form element #value_callback: assembles the allowed values for 'boolean' fields.
+*/
+function list_boolean_allowed_values_callback($element, $input, $form_state) {
+ $on = drupal_array_get_nested_value($form_state['input'], $element['#on_parents']);
+ $off = drupal_array_get_nested_value($form_state['input'], $element['#off_parents']);
+ return array($off, $on);
+}
+
+/**
+ * Implements hook_field_update_field().
+ */
+function list_field_update_field($field, $prior_field, $has_data) {
+ drupal_static_reset('list_allowed_values');
+}
+
+/**
+ * Returns the array of allowed values for a list field.
+ *
+ * The strings are not safe for output. Keys and values of the array should be
+ * sanitized through field_filter_xss() before being displayed.
+ *
+ * @param $field
+ * The field definition.
+ * @param $instance
+ * (optional) A field instance array. Defaults to NULL.
+ * @param $entity_type
+ * (optional) The type of entity; e.g. 'node' or 'user'. Defaults to NULL.
+ * @param $entity
+ * (optional) The entity object. Defaults to NULL.
+ *
+ * @return
+ * The array of allowed values. Keys of the array are the raw stored values
+ * (number or text), values of the array are the display labels.
+ */
+function list_allowed_values($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
+ $allowed_values = &drupal_static(__FUNCTION__, array());
+
+ if (!isset($allowed_values[$field['id']])) {
+ $function = $field['settings']['allowed_values_function'];
+ // If $cacheable is FALSE, then the allowed values are not statically
+ // cached. See list_test_dynamic_values_callback() for an example of
+ // generating dynamic and uncached values.
+ $cacheable = TRUE;
+ if (!empty($function) && function_exists($function)) {
+ $values = $function($field, $instance, $entity_type, $entity, $cacheable);
+ }
+ else {
+ $values = $field['settings']['allowed_values'];
+ }
+
+ if ($cacheable) {
+ $allowed_values[$field['id']] = $values;
+ }
+ else {
+ return $values;
+ }
+ }
+
+ return $allowed_values[$field['id']];
+}
+
+/**
+ * Parses a string of 'allowed values' into an array.
+ *
+ * @param $string
+ * The list of allowed values in string format described in
+ * list_allowed_values_string().
+ * @param $field_type
+ * The field type. Either 'list_number' or 'list_text'.
+ * @param $generate_keys
+ * Boolean value indicating whether to generate keys based on the position of
+ * the value if a key is not manually specified, and if the value cannot be
+ * used as a key. This should only be TRUE for fields of type 'list_number'.
+ *
+ * @return
+ * The array of extracted key/value pairs, or NULL if the string is invalid.
+ *
+ * @see list_allowed_values_string()
+ */
+function list_extract_allowed_values($string, $field_type, $generate_keys) {
+ $values = array();
+
+ $list = explode("\n", $string);
+ $list = array_map('trim', $list);
+ $list = array_filter($list, 'strlen');
+
+ $generated_keys = $explicit_keys = FALSE;
+ foreach ($list as $position => $text) {
+ $value = $key = FALSE;
+
+ // Check for an explicit key.
+ $matches = array();
+ if (preg_match('/(.*)\|(.*)/', $text, $matches)) {
+ $key = $matches[1];
+ $value = $matches[2];
+ $explicit_keys = TRUE;
+ }
+ // Otherwise see if we can use the value as the key. Detecting true integer
+ // strings takes a little trick.
+ elseif ($field_type == 'list_text'
+ || ($field_type == 'list_float' && is_numeric($text))
+ || ($field_type == 'list_integer' && is_numeric($text) && (float) $text == intval($text))) {
+ $key = $value = $text;
+ $explicit_keys = TRUE;
+ }
+ // Otherwise see if we can generate a key from the position.
+ elseif ($generate_keys) {
+ $key = (string) $position;
+ $value = $text;
+ $generated_keys = TRUE;
+ }
+ else {
+ return;
+ }
+
+ // Float keys are represented as strings and need to be disambiguated
+ // ('.5' is '0.5').
+ if ($field_type == 'list_float' && is_numeric($key)) {
+ $key = (string) (float) $key;
+ }
+
+ $values[$key] = $value;
+ }
+
+ // We generate keys only if the list contains no explicit key at all.
+ if ($explicit_keys && $generated_keys) {
+ return;
+ }
+
+ return $values;
+}
+
+/**
+ * Generates a string representation of an array of 'allowed values'.
+ *
+ * This string format is suitable for edition in a textarea.
+ *
+ * @param $values
+ * An array of values, where array keys are values and array values are
+ * labels.
+ *
+ * @return
+ * The string representation of the $values array:
+ * - Values are separated by a carriage return.
+ * - Each value is in the format "value|label" or "value".
+ */
+function list_allowed_values_string($values) {
+ $lines = array();
+ foreach ($values as $key => $value) {
+ $lines[] = "$key|$value";
+ }
+ return implode("\n", $lines);
+}
+
+/**
+ * Implements hook_field_update_forbid().
+ */
+function list_field_update_forbid($field, $prior_field, $has_data) {
+ if ($field['module'] == 'list' && $has_data) {
+ // Forbid any update that removes allowed values with actual data.
+ $lost_keys = array_diff(array_keys($prior_field['settings']['allowed_values']), array_keys($field['settings']['allowed_values']));
+ if (_list_values_in_use($field, $lost_keys)) {
+ throw new FieldUpdateForbiddenException(t('A list field (@field_name) with existing data cannot have its keys changed.', array('@field_name' => $field['field_name'])));
+ }
+ }
+}
+
+/**
+ * Checks if a list of values are being used in actual field values.
+ */
+function _list_values_in_use($field, $values) {
+ if ($values) {
+ $query = new EntityFieldQuery();
+ $found = $query
+ ->fieldCondition($field['field_name'], 'value', $values)
+ ->range(0, 1)
+ ->execute();
+ return !empty($found);
+ }
+
+ return FALSE;
+}
+
+/**
+ * Implements hook_field_validate().
+ *
+ * Possible error codes:
+ * - 'list_illegal_value': The value is not part of the list of allowed values.
+ */
+function list_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
+ $allowed_values = list_allowed_values($field, $instance, $entity_type, $entity);
+ foreach ($items as $delta => $item) {
+ if (!empty($item['value'])) {
+ if (!empty($allowed_values) && !isset($allowed_values[$item['value']])) {
+ $errors[$field['field_name']][$langcode][$delta][] = array(
+ 'error' => 'list_illegal_value',
+ 'message' => t('%name: illegal value.', array('%name' => $instance['label'])),
+ );
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_field_is_empty().
+ */
+function list_field_is_empty($item, $field) {
+ if (empty($item['value']) && (string) $item['value'] !== '0') {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implements hook_field_widget_info_alter().
+ *
+ * The List module does not implement widgets of its own, but reuses the
+ * widgets defined in options.module.
+ *
+ * @see list_options_list()
+ */
+function list_field_widget_info_alter(&$info) {
+ $widgets = array(
+ 'options_select' => array('list_integer', 'list_float', 'list_text'),
+ 'options_buttons' => array('list_integer', 'list_float', 'list_text', 'list_boolean'),
+ 'options_onoff' => array('list_boolean'),
+ );
+
+ foreach ($widgets as $widget => $field_types) {
+ $info[$widget]['field types'] = array_merge($info[$widget]['field types'], $field_types);
+ }
+}
+
+/**
+ * Implements hook_options_list().
+ */
+function list_options_list($field, $instance, $entity_type, $entity) {
+ return list_allowed_values($field, $instance, $entity_type, $entity);
+}
+
+/**
+ * Implements hook_field_formatter_info().
+ */
+function list_field_formatter_info() {
+ return array(
+ 'list_default' => array(
+ 'label' => t('Default'),
+ 'field types' => array('list_integer', 'list_float', 'list_text', 'list_boolean'),
+ ),
+ 'list_key' => array(
+ 'label' => t('Key'),
+ 'field types' => array('list_integer', 'list_float', 'list_text', 'list_boolean'),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_formatter_view().
+ */
+function list_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+ $element = array();
+
+ switch ($display['type']) {
+ case 'list_default':
+ $allowed_values = list_allowed_values($field, $instance, $entity_type, $entity);
+ foreach ($items as $delta => $item) {
+ if (isset($allowed_values[$item['value']])) {
+ $output = field_filter_xss($allowed_values[$item['value']]);
+ }
+ else {
+ // If no match was found in allowed values, fall back to the key.
+ $output = field_filter_xss($item['value']);
+ }
+ $element[$delta] = array('#markup' => $output);
+ }
+ break;
+
+ case 'list_key':
+ foreach ($items as $delta => $item) {
+ $element[$delta] = array('#markup' => field_filter_xss($item['value']));
+ }
+ break;
+ }
+
+ return $element;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list.test b/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list.test
new file mode 100644
index 0000000..84de7e8
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list.test
@@ -0,0 +1,457 @@
+<?php
+
+/**
+ * @file
+ * Tests for list.module.
+ */
+
+/**
+ * Tests for the 'List' field types.
+ */
+class ListFieldTestCase extends FieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'List field',
+ 'description' => 'Test the List field type.',
+ 'group' => 'Field types',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_test');
+
+ $this->field_name = 'test_list';
+ $this->field = array(
+ 'field_name' => $this->field_name,
+ 'type' => 'list_integer',
+ 'cardinality' => 1,
+ 'settings' => array(
+ 'allowed_values' => array(1 => 'One', 2 => 'Two', 3 => 'Three'),
+ ),
+ );
+ $this->field = field_create_field($this->field);
+
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'options_buttons',
+ ),
+ );
+ $this->instance = field_create_instance($this->instance);
+ }
+
+ /**
+ * Test that allowed values can be updated.
+ */
+ function testUpdateAllowedValues() {
+ $langcode = LANGUAGE_NONE;
+
+ // All three options appear.
+ $entity = field_test_create_stub_entity();
+ $form = drupal_get_form('field_test_entity_form', $entity);
+ $this->assertTrue(!empty($form[$this->field_name][$langcode][1]), 'Option 1 exists');
+ $this->assertTrue(!empty($form[$this->field_name][$langcode][2]), 'Option 2 exists');
+ $this->assertTrue(!empty($form[$this->field_name][$langcode][3]), 'Option 3 exists');
+
+ // Use one of the values in an actual entity, and check that this value
+ // cannot be removed from the list.
+ $entity = field_test_create_stub_entity();
+ $entity->{$this->field_name}[$langcode][0] = array('value' => 1);
+ field_test_entity_save($entity);
+ $this->field['settings']['allowed_values'] = array(2 => 'Two');
+ try {
+ field_update_field($this->field);
+ $this->fail(t('Cannot update a list field to not include keys with existing data.'));
+ }
+ catch (FieldException $e) {
+ $this->pass(t('Cannot update a list field to not include keys with existing data.'));
+ }
+ // Empty the value, so that we can actually remove the option.
+ $entity->{$this->field_name}[$langcode] = array();
+ field_test_entity_save($entity);
+
+ // Removed options do not appear.
+ $this->field['settings']['allowed_values'] = array(2 => 'Two');
+ field_update_field($this->field);
+ $entity = field_test_create_stub_entity();
+ $form = drupal_get_form('field_test_entity_form', $entity);
+ $this->assertTrue(empty($form[$this->field_name][$langcode][1]), 'Option 1 does not exist');
+ $this->assertTrue(!empty($form[$this->field_name][$langcode][2]), 'Option 2 exists');
+ $this->assertTrue(empty($form[$this->field_name][$langcode][3]), 'Option 3 does not exist');
+
+ // Completely new options appear.
+ $this->field['settings']['allowed_values'] = array(10 => 'Update', 20 => 'Twenty');
+ field_update_field($this->field);
+ $form = drupal_get_form('field_test_entity_form', $entity);
+ $this->assertTrue(empty($form[$this->field_name][$langcode][1]), 'Option 1 does not exist');
+ $this->assertTrue(empty($form[$this->field_name][$langcode][2]), 'Option 2 does not exist');
+ $this->assertTrue(empty($form[$this->field_name][$langcode][3]), 'Option 3 does not exist');
+ $this->assertTrue(!empty($form[$this->field_name][$langcode][10]), 'Option 10 exists');
+ $this->assertTrue(!empty($form[$this->field_name][$langcode][20]), 'Option 20 exists');
+
+ // Options are reset when a new field with the same name is created.
+ field_delete_field($this->field_name);
+ unset($this->field['id']);
+ $this->field['settings']['allowed_values'] = array(1 => 'One', 2 => 'Two', 3 => 'Three');
+ $this->field = field_create_field($this->field);
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'options_buttons',
+ ),
+ );
+ $this->instance = field_create_instance($this->instance);
+ $entity = field_test_create_stub_entity();
+ $form = drupal_get_form('field_test_entity_form', $entity);
+ $this->assertTrue(!empty($form[$this->field_name][$langcode][1]), 'Option 1 exists');
+ $this->assertTrue(!empty($form[$this->field_name][$langcode][2]), 'Option 2 exists');
+ $this->assertTrue(!empty($form[$this->field_name][$langcode][3]), 'Option 3 exists');
+ }
+}
+
+/**
+ * Sets up a List field for testing allowed values functions.
+ */
+class ListDynamicValuesTestCase extends FieldTestCase {
+ function setUp() {
+ parent::setUp(array('list', 'field_test', 'list_test'));
+
+ $this->field_name = 'test_list';
+ $this->field = array(
+ 'field_name' => $this->field_name,
+ 'type' => 'list_text',
+ 'cardinality' => 1,
+ 'settings' => array(
+ 'allowed_values_function' => 'list_test_dynamic_values_callback',
+ ),
+ );
+ $this->field = field_create_field($this->field);
+
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'required' => TRUE,
+ 'widget' => array(
+ 'type' => 'options_select',
+ ),
+ );
+ $this->instance = field_create_instance($this->instance);
+ $this->test = array(
+ 'id' => mt_rand(1, 10),
+ // Make sure this does not equal the ID so that
+ // list_test_dynamic_values_callback() always returns 4 values.
+ 'vid' => mt_rand(20, 30),
+ 'bundle' => 'test_bundle',
+ 'label' => $this->randomName(),
+ );
+ $this->entity = call_user_func_array('field_test_create_stub_entity', $this->test);
+ }
+}
+
+/**
+ * Tests the List field allowed values function.
+ */
+class ListDynamicValuesValidationTestCase extends ListDynamicValuesTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'List field dynamic values',
+ 'description' => 'Test the List field allowed values function.',
+ 'group' => 'Field types',
+ );
+ }
+
+ /**
+ * Test that allowed values function gets the entity.
+ */
+ function testDynamicAllowedValues() {
+ // Verify that the test passes against every value we had.
+ foreach ($this->test as $key => $value) {
+ $this->entity->test_list[LANGUAGE_NONE][0]['value'] = $value;
+ try {
+ field_attach_validate('test_entity', $this->entity);
+ $this->pass("$key should pass");
+ }
+ catch (FieldValidationException $e) {
+ // This will display as an exception, no need for a separate error.
+ throw($e);
+ }
+ }
+ // Now verify that the test does not pass against anything else.
+ foreach ($this->test as $key => $value) {
+ $this->entity->test_list[LANGUAGE_NONE][0]['value'] = is_numeric($value) ? (100 - $value) : ('X' . $value);
+ $pass = FALSE;
+ try {
+ field_attach_validate('test_entity', $this->entity);
+ }
+ catch (FieldValidationException $e) {
+ $pass = TRUE;
+ }
+ $this->assertTrue($pass, $key . ' should not pass');
+ }
+ }
+}
+
+/**
+ * List module UI tests.
+ */
+class ListFieldUITestCase extends FieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'List field UI',
+ 'description' => 'Test the List field UI functionality.',
+ 'group' => 'Field types',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_test', 'field_ui');
+
+ // Create test user.
+ $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
+ $this->drupalLogin($admin_user);
+
+ // Create content type, with underscores.
+ $type_name = 'test_' . strtolower($this->randomName());
+ $type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
+ $this->type = $type->type;
+ // Store a valid URL name, with hyphens instead of underscores.
+ $this->hyphen_type = str_replace('_', '-', $this->type);
+ }
+
+ /**
+ * List (integer) : test 'allowed values' input.
+ */
+ function testListAllowedValuesInteger() {
+ $this->field_name = 'field_list_integer';
+ $this->createListField('list_integer');
+
+ // Flat list of textual values.
+ $string = "Zero\nOne";
+ $array = array('0' => 'Zero', '1' => 'One');
+ $this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
+ // Explicit integer keys.
+ $string = "0|Zero\n2|Two";
+ $array = array('0' => 'Zero', '2' => 'Two');
+ $this->assertAllowedValuesInput($string, $array, 'Integer keys are accepted.');
+ // Check that values can be added and removed.
+ $string = "0|Zero\n1|One";
+ $array = array('0' => 'Zero', '1' => 'One');
+ $this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
+ // Non-integer keys.
+ $this->assertAllowedValuesInput("1.1|One", 'keys must be integers', 'Non integer keys are rejected.');
+ $this->assertAllowedValuesInput("abc|abc", 'keys must be integers', 'Non integer keys are rejected.');
+ // Mixed list of keyed and unkeyed values.
+ $this->assertAllowedValuesInput("Zero\n1|One", 'invalid input', 'Mixed lists are rejected.');
+
+ // Create a node with actual data for the field.
+ $settings = array(
+ 'type' => $this->type,
+ $this->field_name => array(LANGUAGE_NONE => array(array('value' => 1))),
+ );
+ $node = $this->drupalCreateNode($settings);
+
+ // Check that a flat list of values is rejected once the field has data.
+ $this->assertAllowedValuesInput( "Zero\nOne", 'invalid input', 'Unkeyed lists are rejected once the field has data.');
+
+ // Check that values can be added but values in use cannot be removed.
+ $string = "0|Zero\n1|One\n2|Two";
+ $array = array('0' => 'Zero', '1' => 'One', '2' => 'Two');
+ $this->assertAllowedValuesInput($string, $array, 'Values can be added.');
+ $string = "0|Zero\n1|One";
+ $array = array('0' => 'Zero', '1' => 'One');
+ $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
+ $this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
+
+ // Delete the node, remove the value.
+ node_delete($node->nid);
+ $string = "0|Zero";
+ $array = array('0' => 'Zero');
+ $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
+ }
+
+ /**
+ * List (float) : test 'allowed values' input.
+ */
+ function testListAllowedValuesFloat() {
+ $this->field_name = 'field_list_float';
+ $this->createListField('list_float');
+
+ // Flat list of textual values.
+ $string = "Zero\nOne";
+ $array = array('0' => 'Zero', '1' => 'One');
+ $this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
+ // Explicit numeric keys.
+ $string = "0|Zero\n.5|Point five";
+ $array = array('0' => 'Zero', '0.5' => 'Point five');
+ $this->assertAllowedValuesInput($string, $array, 'Integer keys are accepted.');
+ // Check that values can be added and removed.
+ $string = "0|Zero\n.5|Point five\n1.0|One";
+ $array = array('0' => 'Zero', '0.5' => 'Point five', '1' => 'One');
+ $this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
+ // Non-numeric keys.
+ $this->assertAllowedValuesInput("abc|abc\n", 'each key must be a valid integer or decimal', 'Non numeric keys are rejected.');
+ // Mixed list of keyed and unkeyed values.
+ $this->assertAllowedValuesInput("Zero\n1|One\n", 'invalid input', 'Mixed lists are rejected.');
+
+ // Create a node with actual data for the field.
+ $settings = array(
+ 'type' => $this->type,
+ $this->field_name => array(LANGUAGE_NONE => array(array('value' => .5))),
+ );
+ $node = $this->drupalCreateNode($settings);
+
+ // Check that a flat list of values is rejected once the field has data.
+ $this->assertAllowedValuesInput("Zero\nOne", 'invalid input', 'Unkeyed lists are rejected once the field has data.');
+
+ // Check that values can be added but values in use cannot be removed.
+ $string = "0|Zero\n.5|Point five\n2|Two";
+ $array = array('0' => 'Zero', '0.5' => 'Point five', '2' => 'Two');
+ $this->assertAllowedValuesInput($string, $array, 'Values can be added.');
+ $string = "0|Zero\n.5|Point five";
+ $array = array('0' => 'Zero', '0.5' => 'Point five');
+ $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
+ $this->assertAllowedValuesInput("0|Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
+
+ // Delete the node, remove the value.
+ node_delete($node->nid);
+ $string = "0|Zero";
+ $array = array('0' => 'Zero');
+ $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
+ }
+
+ /**
+ * List (text) : test 'allowed values' input.
+ */
+ function testListAllowedValuesText() {
+ $this->field_name = 'field_list_text';
+ $this->createListField('list_text');
+
+ // Flat list of textual values.
+ $string = "Zero\nOne";
+ $array = array('Zero' => 'Zero', 'One' => 'One');
+ $this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are accepted.');
+ // Explicit keys.
+ $string = "zero|Zero\none|One";
+ $array = array('zero' => 'Zero', 'one' => 'One');
+ $this->assertAllowedValuesInput($string, $array, 'Explicit keys are accepted.');
+ // Check that values can be added and removed.
+ $string = "zero|Zero\ntwo|Two";
+ $array = array('zero' => 'Zero', 'two' => 'Two');
+ $this->assertAllowedValuesInput($string, $array, 'Values can be added and removed.');
+ // Mixed list of keyed and unkeyed values.
+ $string = "zero|Zero\nOne\n";
+ $array = array('zero' => 'Zero', 'One' => 'One');
+ $this->assertAllowedValuesInput($string, $array, 'Mixed lists are accepted.');
+ // Overly long keys.
+ $this->assertAllowedValuesInput("zero|Zero\n" . $this->randomName(256) . "|One", 'each key must be a string at most 255 characters long', 'Overly long keys are rejected.');
+
+ // Create a node with actual data for the field.
+ $settings = array(
+ 'type' => $this->type,
+ $this->field_name => array(LANGUAGE_NONE => array(array('value' => 'One'))),
+ );
+ $node = $this->drupalCreateNode($settings);
+
+ // Check that flat lists of values are still accepted once the field has
+ // data.
+ $string = "Zero\nOne";
+ $array = array('Zero' => 'Zero', 'One' => 'One');
+ $this->assertAllowedValuesInput($string, $array, 'Unkeyed lists are still accepted once the field has data.');
+
+ // Check that values can be added but values in use cannot be removed.
+ $string = "Zero\nOne\nTwo";
+ $array = array('Zero' => 'Zero', 'One' => 'One', 'Two' => 'Two');
+ $this->assertAllowedValuesInput($string, $array, 'Values can be added.');
+ $string = "Zero\nOne";
+ $array = array('Zero' => 'Zero', 'One' => 'One');
+ $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
+ $this->assertAllowedValuesInput("Zero", 'some values are being removed while currently in use', 'Values in use cannot be removed.');
+
+ // Delete the node, remove the value.
+ node_delete($node->nid);
+ $string = "Zero";
+ $array = array('Zero' => 'Zero');
+ $this->assertAllowedValuesInput($string, $array, 'Values not in use can be removed.');
+ }
+
+ /**
+ * List (boolen) : test 'On/Off' values input.
+ */
+ function testListAllowedValuesBoolean() {
+ $this->field_name = 'field_list_boolean';
+ $this->createListField('list_boolean');
+
+ // Check that the separate 'On' and 'Off' form fields work.
+ $on = $this->randomName();
+ $off = $this->randomName();
+ $allowed_values = array(1 => $on, 0 => $off);
+ $edit = array(
+ 'on' => $on,
+ 'off' => $off,
+ );
+ $this->drupalPost($this->admin_path, $edit, t('Save settings'));
+ $this->assertText("Saved field_list_boolean configuration.", "The 'On' and 'Off' form fields work for boolean fields.");
+ // Test the allowed_values on the field settings form.
+ $this->drupalGet($this->admin_path);
+ $this->assertFieldByName('on', $on, "The 'On' value is stored correctly.");
+ $this->assertFieldByName('off', $off, "The 'Off' value is stored correctly.");
+ $field = field_info_field($this->field_name);
+ $this->assertEqual($field['settings']['allowed_values'], $allowed_values, 'The allowed value is correct');
+ $this->assertFalse(isset($field['settings']['on']), 'The on value is not saved into settings');
+ $this->assertFalse(isset($field['settings']['off']), 'The off value is not saved into settings');
+ }
+
+ /**
+ * Helper function to create list field of a given type.
+ *
+ * @param string $type
+ * 'list_integer', 'list_float', 'list_text' or 'list_boolean'
+ */
+ protected function createListField($type) {
+ // Create a test field and instance.
+ $field = array(
+ 'field_name' => $this->field_name,
+ 'type' => $type,
+ );
+ field_create_field($field);
+ $instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'node',
+ 'bundle' => $this->type,
+ );
+ field_create_instance($instance);
+
+ $this->admin_path = 'admin/structure/types/manage/' . $this->hyphen_type . '/fields/' . $this->field_name;
+ }
+
+ /**
+ * Tests a string input for the 'allowed values' form element.
+ *
+ * @param $input_string
+ * The input string, in the pipe-linefeed format expected by the form
+ * element.
+ * @param $result
+ * Either an expected resulting array in
+ * $field['settings']['allowed_values'], or an expected error message.
+ * @param $message
+ * Message to display.
+ */
+ function assertAllowedValuesInput($input_string, $result, $message) {
+ $edit = array('field[settings][allowed_values]' => $input_string);
+ $this->drupalPost($this->admin_path, $edit, t('Save settings'));
+
+ if (is_string($result)) {
+ $this->assertText($result, $message);
+ }
+ else {
+ field_info_cache_clear();
+ $field = field_info_field($this->field_name);
+ $this->assertIdentical($field['settings']['allowed_values'], $result, $message);
+ }
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.info b/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.info
new file mode 100644
index 0000000..1861cdc
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.info
@@ -0,0 +1,12 @@
+name = "List test"
+description = "Support module for the List module tests."
+core = 7.x
+package = Testing
+version = VERSION
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.module b/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.module
new file mode 100644
index 0000000..aa53337
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/list/tests/list_test.module
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Helper module for the List module tests.
+ */
+
+/**
+ * Allowed values callback.
+ */
+function list_test_allowed_values_callback($field) {
+ $values = array(
+ 'Group 1' => array(
+ 0 => 'Zero',
+ ),
+ 1 => 'One',
+ 'Group 2' => array(
+ 2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>',
+ ),
+ );
+
+ return $values;
+}
+
+/**
+ * An entity-bound allowed values callback.
+ */
+function list_test_dynamic_values_callback($field, $instance, $entity_type, $entity, &$cacheable) {
+ $cacheable = FALSE;
+ // We need the values of the entity as keys.
+ return drupal_map_assoc(array_merge(array($entity->ftlabel), entity_extract_ids($entity_type, $entity)));
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/number/number.info b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.info
new file mode 100644
index 0000000..df198b8
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.info
@@ -0,0 +1,13 @@
+name = Number
+description = Defines numeric field types.
+package = Core
+version = VERSION
+core = 7.x
+dependencies[] = field
+files[] = number.test
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/number/number.install b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.install
new file mode 100644
index 0000000..02c7a30
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.install
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the number module.
+ */
+
+/**
+ * Implements hook_field_schema().
+ */
+function number_field_schema($field) {
+ switch ($field['type']) {
+ case 'number_integer' :
+ $columns = array(
+ 'value' => array(
+ 'type' => 'int',
+ 'not null' => FALSE
+ ),
+ );
+ break;
+
+ case 'number_float' :
+ $columns = array(
+ 'value' => array(
+ 'type' => 'float',
+ 'not null' => FALSE
+ ),
+ );
+ break;
+
+ case 'number_decimal' :
+ $columns = array(
+ 'value' => array(
+ 'type' => 'numeric',
+ 'precision' => $field['settings']['precision'],
+ 'scale' => $field['settings']['scale'],
+ 'not null' => FALSE
+ ),
+ );
+ break;
+ }
+ return array(
+ 'columns' => $columns,
+ );
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/number/number.module b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.module
new file mode 100644
index 0000000..6046544
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.module
@@ -0,0 +1,419 @@
+<?php
+
+/**
+ * @file
+ * Defines numeric field types.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function number_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#number':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The Number module defines various numeric field types for the Field module. Numbers can be in integer, decimal, or floating-point form, and they can be formatted when displayed. Number fields can be limited to a specific set of input values or to a range of values. See the <a href="@field-help">Field module help page</a> for more information about fields.', array('@field-help' => url('admin/help/field'))) . '</p>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_field_info().
+ */
+function number_field_info() {
+ return array(
+ 'number_integer' => array(
+ 'label' => t('Integer'),
+ 'description' => t('This field stores a number in the database as an integer.'),
+ 'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''),
+ 'default_widget' => 'number',
+ 'default_formatter' => 'number_integer',
+ ),
+ 'number_decimal' => array(
+ 'label' => t('Decimal'),
+ 'description' => t('This field stores a number in the database in a fixed decimal format.'),
+ 'settings' => array('precision' => 10, 'scale' => 2, 'decimal_separator' => '.'),
+ 'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''),
+ 'default_widget' => 'number',
+ 'default_formatter' => 'number_decimal',
+ ),
+ 'number_float' => array(
+ 'label' => t('Float'),
+ 'description' => t('This field stores a number in the database in a floating point format.'),
+ 'settings' => array('decimal_separator' => '.'),
+ 'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''),
+ 'default_widget' => 'number',
+ 'default_formatter' => 'number_decimal',
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_settings_form().
+ */
+function number_field_settings_form($field, $instance, $has_data) {
+ $settings = $field['settings'];
+ $form = array();
+
+ if ($field['type'] == 'number_decimal') {
+ $form['precision'] = array(
+ '#type' => 'select',
+ '#title' => t('Precision'),
+ '#options' => drupal_map_assoc(range(10, 32)),
+ '#default_value' => $settings['precision'],
+ '#description' => t('The total number of digits to store in the database, including those to the right of the decimal.'),
+ '#disabled' => $has_data,
+ );
+ $form['scale'] = array(
+ '#type' => 'select',
+ '#title' => t('Scale'),
+ '#options' => drupal_map_assoc(range(0, 10)),
+ '#default_value' => $settings['scale'],
+ '#description' => t('The number of digits to the right of the decimal.'),
+ '#disabled' => $has_data,
+ );
+ }
+ if ($field['type'] == 'number_decimal' || $field['type'] == 'number_float') {
+ $form['decimal_separator'] = array(
+ '#type' => 'select',
+ '#title' => t('Decimal marker'),
+ '#options' => array('.' => t('Decimal point'), ',' => t('Comma')),
+ '#default_value' => $settings['decimal_separator'],
+ '#description' => t('The character users will input to mark the decimal point in forms.'),
+ );
+ }
+
+ return $form;
+}
+
+/**
+ * Implements hook_field_instance_settings_form().
+ */
+function number_field_instance_settings_form($field, $instance) {
+ $settings = $instance['settings'];
+
+ $form['min'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Minimum'),
+ '#default_value' => $settings['min'],
+ '#description' => t('The minimum value that should be allowed in this field. Leave blank for no minimum.'),
+ '#element_validate' => array('element_validate_number'),
+ );
+ $form['max'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Maximum'),
+ '#default_value' => $settings['max'],
+ '#description' => t('The maximum value that should be allowed in this field. Leave blank for no maximum.'),
+ '#element_validate' => array('element_validate_number'),
+ );
+ $form['prefix'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Prefix'),
+ '#default_value' => $settings['prefix'],
+ '#size' => 60,
+ '#description' => t("Define a string that should be prefixed to the value, like '$ ' or '&euro; '. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."),
+ );
+ $form['suffix'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Suffix'),
+ '#default_value' => $settings['suffix'],
+ '#size' => 60,
+ '#description' => t("Define a string that should be suffixed to the value, like ' m', ' kb/s'. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."),
+ );
+
+ return $form;
+}
+
+/**
+ * Implements hook_field_validate().
+ *
+ * Possible error codes:
+ * - 'number_min': The value is less than the allowed minimum value.
+ * - 'number_max': The value is greater than the allowed maximum value.
+ */
+function number_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
+ foreach ($items as $delta => $item) {
+ if ($item['value'] != '') {
+ if (is_numeric($instance['settings']['min']) && $item['value'] < $instance['settings']['min']) {
+ $errors[$field['field_name']][$langcode][$delta][] = array(
+ 'error' => 'number_min',
+ 'message' => t('%name: the value may be no less than %min.', array('%name' => $instance['label'], '%min' => $instance['settings']['min'])),
+ );
+ }
+ if (is_numeric($instance['settings']['max']) && $item['value'] > $instance['settings']['max']) {
+ $errors[$field['field_name']][$langcode][$delta][] = array(
+ 'error' => 'number_max',
+ 'message' => t('%name: the value may be no greater than %max.', array('%name' => $instance['label'], '%max' => $instance['settings']['max'])),
+ );
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_field_presave().
+ */
+function number_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
+ if ($field['type'] == 'number_decimal') {
+ // Let PHP round the value to ensure consistent behavior across storage
+ // backends.
+ foreach ($items as $delta => $item) {
+ if (isset($item['value'])) {
+ $items[$delta]['value'] = round($item['value'], $field['settings']['scale']);
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_field_is_empty().
+ */
+function number_field_is_empty($item, $field) {
+ if (empty($item['value']) && (string) $item['value'] !== '0') {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implements hook_field_formatter_info().
+ */
+function number_field_formatter_info() {
+ return array(
+ // The 'Default' formatter is different for integer fields on the one hand,
+ // and for decimal and float fields on the other hand, in order to be able
+ // to use different default values for the settings.
+ 'number_integer' => array(
+ 'label' => t('Default'),
+ 'field types' => array('number_integer'),
+ 'settings' => array(
+ 'thousand_separator' => ' ',
+ // The 'decimal_separator' and 'scale' settings are not configurable
+ // through the UI, and will therefore keep their default values. They
+ // are only present so that the 'number_integer' and 'number_decimal'
+ // formatters can use the same code.
+ 'decimal_separator' => '.',
+ 'scale' => 0,
+ 'prefix_suffix' => TRUE,
+ ),
+ ),
+ 'number_decimal' => array(
+ 'label' => t('Default'),
+ 'field types' => array('number_decimal', 'number_float'),
+ 'settings' => array(
+ 'thousand_separator' => ' ',
+ 'decimal_separator' => '.',
+ 'scale' => 2,
+ 'prefix_suffix' => TRUE,
+ ),
+ ),
+ 'number_unformatted' => array(
+ 'label' => t('Unformatted'),
+ 'field types' => array('number_integer', 'number_decimal', 'number_float'),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_formatter_settings_form().
+ */
+function number_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
+ $display = $instance['display'][$view_mode];
+ $settings = $display['settings'];
+
+ if ($display['type'] == 'number_decimal' || $display['type'] == 'number_integer') {
+ $options = array(
+ '' => t('<none>'),
+ '.' => t('Decimal point'),
+ ',' => t('Comma'),
+ ' ' => t('Space'),
+ );
+ $element['thousand_separator'] = array(
+ '#type' => 'select',
+ '#title' => t('Thousand marker'),
+ '#options' => $options,
+ '#default_value' => $settings['thousand_separator'],
+ );
+
+ if ($display['type'] == 'number_decimal') {
+ $element['decimal_separator'] = array(
+ '#type' => 'select',
+ '#title' => t('Decimal marker'),
+ '#options' => array('.' => t('Decimal point'), ',' => t('Comma')),
+ '#default_value' => $settings['decimal_separator'],
+ );
+ $element['scale'] = array(
+ '#type' => 'select',
+ '#title' => t('Scale'),
+ '#options' => drupal_map_assoc(range(0, 10)),
+ '#default_value' => $settings['scale'],
+ '#description' => t('The number of digits to the right of the decimal.'),
+ );
+ }
+
+ $element['prefix_suffix'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Display prefix and suffix.'),
+ '#default_value' => $settings['prefix_suffix'],
+ );
+ }
+
+ return $element;
+}
+
+/**
+ * Implements hook_field_formatter_settings_summary().
+ */
+function number_field_formatter_settings_summary($field, $instance, $view_mode) {
+ $display = $instance['display'][$view_mode];
+ $settings = $display['settings'];
+
+ $summary = array();
+ if ($display['type'] == 'number_decimal' || $display['type'] == 'number_integer') {
+ $summary[] = number_format(1234.1234567890, $settings['scale'], $settings['decimal_separator'], $settings['thousand_separator']);
+ if ($settings['prefix_suffix']) {
+ $summary[] = t('Display with prefix and suffix.');
+ }
+ }
+
+ return implode('<br />', $summary);
+}
+
+/**
+ * Implements hook_field_formatter_view().
+ */
+function number_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+ $element = array();
+ $settings = $display['settings'];
+
+ switch ($display['type']) {
+ case 'number_integer':
+ case 'number_decimal':
+ foreach ($items as $delta => $item) {
+ $output = number_format($item['value'], $settings['scale'], $settings['decimal_separator'], $settings['thousand_separator']);
+ if ($settings['prefix_suffix']) {
+ $prefixes = isset($instance['settings']['prefix']) ? array_map('field_filter_xss', explode('|', $instance['settings']['prefix'])) : array('');
+ $suffixes = isset($instance['settings']['suffix']) ? array_map('field_filter_xss', explode('|', $instance['settings']['suffix'])) : array('');
+ $prefix = (count($prefixes) > 1) ? format_plural($item['value'], $prefixes[0], $prefixes[1]) : $prefixes[0];
+ $suffix = (count($suffixes) > 1) ? format_plural($item['value'], $suffixes[0], $suffixes[1]) : $suffixes[0];
+ $output = $prefix . $output . $suffix;
+ }
+ $element[$delta] = array('#markup' => $output);
+ }
+ break;
+
+ case 'number_unformatted':
+ foreach ($items as $delta => $item) {
+ $element[$delta] = array('#markup' => $item['value']);
+ }
+ break;
+ }
+
+ return $element;
+}
+
+/**
+ * Implements hook_field_widget_info().
+ */
+function number_field_widget_info() {
+ return array(
+ 'number' => array(
+ 'label' => t('Text field'),
+ 'field types' => array('number_integer', 'number_decimal', 'number_float'),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_widget_form().
+ */
+function number_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+ $value = isset($items[$delta]['value']) ? $items[$delta]['value'] : '';
+ // Substitute the decimal separator.
+ if ($field['type'] == 'number_decimal' || $field['type'] == 'number_float') {
+ $value = strtr($value, '.', $field['settings']['decimal_separator']);
+ }
+
+ $element += array(
+ '#type' => 'textfield',
+ '#default_value' => $value,
+ // Allow a slightly larger size that the field length to allow for some
+ // configurations where all characters won't fit in input field.
+ '#size' => $field['type'] == 'number_decimal' ? $field['settings']['precision'] + 4 : 12,
+ // Allow two extra characters for signed values and decimal separator.
+ '#maxlength' => $field['type'] == 'number_decimal' ? $field['settings']['precision'] + 2 : 10,
+ // Extract the number type from the field type name for easier validation.
+ '#number_type' => str_replace('number_', '', $field['type']),
+ );
+
+ // Add prefix and suffix.
+ if (!empty($instance['settings']['prefix'])) {
+ $prefixes = explode('|', $instance['settings']['prefix']);
+ $element['#field_prefix'] = field_filter_xss(array_pop($prefixes));
+ }
+ if (!empty($instance['settings']['suffix'])) {
+ $suffixes = explode('|', $instance['settings']['suffix']);
+ $element['#field_suffix'] = field_filter_xss(array_pop($suffixes));
+ }
+
+ $element['#element_validate'][] = 'number_field_widget_validate';
+
+ return array('value' => $element);
+}
+
+/**
+ * FAPI validation of an individual number element.
+ */
+function number_field_widget_validate($element, &$form_state) {
+ $field = field_widget_field($element, $form_state);
+ $instance = field_widget_instance($element, $form_state);
+
+ $type = $element['#number_type'];
+ $value = $element['#value'];
+
+ // Reject invalid characters.
+ if (!empty($value)) {
+ switch ($type) {
+ case 'float':
+ case 'decimal':
+ $regexp = '@([^-0-9\\' . $field['settings']['decimal_separator'] . '])|(.-)@';
+ $message = t('Only numbers and the decimal separator (@separator) allowed in %field.', array('%field' => $instance['label'], '@separator' => $field['settings']['decimal_separator']));
+ break;
+
+ case 'integer':
+ $regexp = '@([^-0-9])|(.-)@';
+ $message = t('Only numbers are allowed in %field.', array('%field' => $instance['label']));
+ break;
+ }
+ if ($value != preg_replace($regexp, '', $value)) {
+ form_error($element, $message);
+ }
+ else {
+ if ($type == 'decimal' || $type == 'float') {
+ // Verify that only one decimal separator exists in the field.
+ if (substr_count($value, $field['settings']['decimal_separator']) > 1) {
+ $message = t('%field: There should only be one decimal separator (@separator).',
+ array(
+ '%field' => t($instance['label']),
+ '@separator' => $field['settings']['decimal_separator'],
+ )
+ );
+ form_error($element, $message);
+ }
+ else {
+ // Substitute the decimal separator; things should be fine.
+ $value = strtr($value, $field['settings']['decimal_separator'], '.');
+ }
+ }
+ form_set_value($element, $value, $form_state);
+ }
+ }
+}
+
+/**
+ * Implements hook_field_widget_error().
+ */
+function number_field_widget_error($element, $error, $form, &$form_state) {
+ form_error($element['value'], $error['message']);
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/number/number.test b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.test
new file mode 100644
index 0000000..88029cd
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/number/number.test
@@ -0,0 +1,155 @@
+<?php
+
+/**
+ * @file
+ * Tests for number.module.
+ */
+
+/**
+ * Tests for number field types.
+ */
+class NumberFieldTestCase extends DrupalWebTestCase {
+ protected $field;
+ protected $instance;
+ protected $web_user;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Number field',
+ 'description' => 'Test the creation of number fields.',
+ 'group' => 'Field types'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_test');
+ $this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content', 'administer content types'));
+ $this->drupalLogin($this->web_user);
+ }
+
+ /**
+ * Test number_decimal field.
+ */
+ function testNumberDecimalField() {
+ // Create a field with settings to validate.
+ $this->field = array(
+ 'field_name' => drupal_strtolower($this->randomName()),
+ 'type' => 'number_decimal',
+ 'settings' => array(
+ 'precision' => 8, 'scale' => 4, 'decimal_separator' => '.',
+ )
+ );
+ field_create_field($this->field);
+ $this->instance = array(
+ 'field_name' => $this->field['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'number',
+ ),
+ 'display' => array(
+ 'default' => array(
+ 'type' => 'number_decimal',
+ ),
+ ),
+ );
+ field_create_instance($this->instance);
+
+ // Display creation form.
+ $this->drupalGet('test-entity/add/test-bundle');
+ $langcode = LANGUAGE_NONE;
+ $this->assertFieldByName("{$this->field['field_name']}[$langcode][0][value]", '', 'Widget is displayed');
+
+ // Submit a signed decimal value within the allowed precision and scale.
+ $value = '-1234.5678';
+ $edit = array(
+ "{$this->field['field_name']}[$langcode][0][value]" => $value,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
+ $id = $match[1];
+ $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
+ $this->assertRaw(round($value, 2), 'Value is displayed.');
+
+ // Try to create entries with more than one decimal separator; assert fail.
+ $wrong_entries = array(
+ '3.14.159',
+ '0..45469',
+ '..4589',
+ '6.459.52',
+ '6.3..25',
+ );
+
+ foreach ($wrong_entries as $wrong_entry) {
+ $this->drupalGet('test-entity/add/test-bundle');
+ $edit = array(
+ "{$this->field['field_name']}[$langcode][0][value]" => $wrong_entry,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertText(
+ t('There should only be one decimal separator (@separator)',
+ array('@separator' => $this->field['settings']['decimal_separator'])),
+ 'Correctly failed to save decimal value with more than one decimal point.'
+ );
+ }
+
+ // Try to create entries with minus sign not in the first position.
+ $wrong_entries = array(
+ '3-3',
+ '4-',
+ '1.3-',
+ '1.2-4',
+ '-10-10',
+ );
+
+ foreach ($wrong_entries as $wrong_entry) {
+ $this->drupalGet('test-entity/add/test-bundle');
+ $edit = array(
+ "{$this->field['field_name']}[$langcode][0][value]" => $wrong_entry,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertText(
+ t('Only numbers and the decimal separator (@separator) allowed in ',
+ array('@separator' => $this->field['settings']['decimal_separator'])),
+ 'Correctly failed to save decimal value with minus sign in the wrong position.'
+ );
+ }
+ }
+
+ /**
+ * Test number_integer field.
+ */
+ function testNumberIntegerField() {
+ // Display the "Add content type" form.
+ $this->drupalGet('admin/structure/types/add');
+
+ // Add a content type.
+ $name = $this->randomName();
+ $type = drupal_strtolower($name);
+ $edit = array('name' => $name, 'type' => $type);
+ $this->drupalPost(NULL, $edit, t('Save and add fields'));
+
+ // Add an integer field to the newly-created type.
+ $label = $this->randomName();
+ $field_name = drupal_strtolower($label);
+ $edit = array(
+ 'fields[_add_new_field][label]'=> $label,
+ 'fields[_add_new_field][field_name]' => $field_name,
+ 'fields[_add_new_field][type]' => 'number_integer',
+ 'fields[_add_new_field][widget_type]' => 'number',
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+
+ // Set the formatter to "number_integer" and to "unformatted", and just
+ // check that the settings summary does not generate warnings.
+ $this->drupalGet("admin/structure/types/manage/$type/display");
+ $edit = array(
+ "fields[field_$field_name][type]" => 'number_integer',
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $edit = array(
+ "fields[field_$field_name][type]" => 'number_unformatted',
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/options/options.api.php b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.api.php
new file mode 100644
index 0000000..86f2b12
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.api.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the Options module.
+ */
+
+/**
+ * Returns the list of options to be displayed for a field.
+ *
+ * Field types willing to enable one or several of the widgets defined in
+ * options.module (select, radios/checkboxes, on/off checkbox) need to
+ * implement this hook to specify the list of options to display in the
+ * widgets.
+ *
+ * @param $field
+ * The field definition.
+ * @param $instance
+ * (optional) The instance definition. The hook might be called without an
+ * $instance parameter in contexts where no specific instance can be targeted.
+ * It is recommended to only use instance level properties to filter out
+ * values from a list defined by field level properties.
+ * @param $entity_type
+ * The entity type the field is attached to.
+ * @param $entity
+ * The entity object the field is attached to, or NULL if no entity
+ * exists (e.g. in field settings page).
+ *
+ * @return
+ * The array of options for the field. Array keys are the values to be
+ * stored, and should be of the data type (string, number...) expected by
+ * the first 'column' for the field type. Array values are the labels to
+ * display within the widgets. The labels should NOT be sanitized,
+ * options.module takes care of sanitation according to the needs of each
+ * widget. The HTML tags defined in _field_filter_xss_allowed_tags() are
+ * allowed, other tags will be filtered.
+ */
+function hook_options_list($field, $instance, $entity_type, $entity) {
+ // Sample structure.
+ $options = array(
+ 0 => t('Zero'),
+ 1 => t('One'),
+ 2 => t('Two'),
+ 3 => t('Three'),
+ );
+
+ // Sample structure with groups. Only one level of nesting is allowed. This
+ // is only supported by the 'options_select' widget. Other widgets will
+ // flatten the array.
+ $options = array(
+ t('First group') => array(
+ 0 => t('Zero'),
+ ),
+ t('Second group') => array(
+ 1 => t('One'),
+ 2 => t('Two'),
+ ),
+ 3 => t('Three'),
+ );
+
+ // In actual implementations, the array of options will most probably depend
+ // on properties of the field. Example from taxonomy.module:
+ $options = array();
+ foreach ($field['settings']['allowed_values'] as $tree) {
+ $terms = taxonomy_get_tree($tree['vid'], $tree['parent']);
+ if ($terms) {
+ foreach ($terms as $term) {
+ $options[$term->tid] = str_repeat('-', $term->depth) . $term->name;
+ }
+ }
+ }
+
+ return $options;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/options/options.info b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.info
new file mode 100644
index 0000000..571afbd
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.info
@@ -0,0 +1,13 @@
+name = Options
+description = Defines selection, check box and radio button widgets for text and numeric fields.
+package = Core
+version = VERSION
+core = 7.x
+dependencies[] = field
+files[] = options.test
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/options/options.module b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.module
new file mode 100644
index 0000000..3862ba7
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.module
@@ -0,0 +1,417 @@
+<?php
+
+/**
+ * @file
+ * Defines selection, check box and radio button widgets for text and numeric fields.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function options_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#options':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t('The Options module defines checkbox, selection, and other input widgets for the Field module. See the <a href="@field-help">Field module help page</a> for more information about fields.', array('@field-help' => url('admin/help/field'))) . '</p>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function options_theme() {
+ return array(
+ 'options_none' => array(
+ 'variables' => array('instance' => NULL, 'option' => NULL),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_widget_info().
+ *
+ * Field type modules willing to use those widgets should:
+ * - Use hook_field_widget_info_alter() to append their field own types to the
+ * list of types supported by the widgets,
+ * - Implement hook_options_list() to provide the list of options.
+ * See list.module.
+ */
+function options_field_widget_info() {
+ return array(
+ 'options_select' => array(
+ 'label' => t('Select list'),
+ 'field types' => array(),
+ 'behaviors' => array(
+ 'multiple values' => FIELD_BEHAVIOR_CUSTOM,
+ ),
+ ),
+ 'options_buttons' => array(
+ 'label' => t('Check boxes/radio buttons'),
+ 'field types' => array(),
+ 'behaviors' => array(
+ 'multiple values' => FIELD_BEHAVIOR_CUSTOM,
+ ),
+ ),
+ 'options_onoff' => array(
+ 'label' => t('Single on/off checkbox'),
+ 'field types' => array(),
+ 'behaviors' => array(
+ 'multiple values' => FIELD_BEHAVIOR_CUSTOM,
+ ),
+ 'settings' => array('display_label' => 0),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_widget_form().
+ */
+function options_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+ // Abstract over the actual field columns, to allow different field types to
+ // reuse those widgets.
+ $value_key = key($field['columns']);
+
+ $type = str_replace('options_', '', $instance['widget']['type']);
+ $multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED;
+ $required = $element['#required'];
+ $has_value = isset($items[0][$value_key]);
+ $properties = _options_properties($type, $multiple, $required, $has_value);
+
+ $entity_type = $element['#entity_type'];
+ $entity = $element['#entity'];
+
+ // Prepare the list of options.
+ $options = _options_get_options($field, $instance, $properties, $entity_type, $entity);
+
+ // Put current field values in shape.
+ $default_value = _options_storage_to_form($items, $options, $value_key, $properties);
+
+ switch ($type) {
+ case 'select':
+ $element += array(
+ '#type' => 'select',
+ '#default_value' => $default_value,
+ // Do not display a 'multiple' select box if there is only one option.
+ '#multiple' => $multiple && count($options) > 1,
+ '#options' => $options,
+ );
+ break;
+
+ case 'buttons':
+ // If required and there is one single option, preselect it.
+ if ($required && count($options) == 1) {
+ reset($options);
+ $default_value = array(key($options));
+ }
+
+ // If this is a single-value field, take the first default value, or
+ // default to NULL so that the form element is properly recognized as
+ // not having a default value.
+ if (!$multiple) {
+ $default_value = $default_value ? reset($default_value) : NULL;
+ }
+
+ $element += array(
+ '#type' => $multiple ? 'checkboxes' : 'radios',
+ // Radio buttons need a scalar value.
+ '#default_value' => $default_value,
+ '#options' => $options,
+ );
+ break;
+
+ case 'onoff':
+ $keys = array_keys($options);
+ $off_value = array_shift($keys);
+ $on_value = array_shift($keys);
+ $element += array(
+ '#type' => 'checkbox',
+ '#default_value' => (isset($default_value[0]) && $default_value[0] == $on_value) ? 1 : 0,
+ '#on_value' => $on_value,
+ '#off_value' => $off_value,
+ );
+ // Override the title from the incoming $element.
+ $element['#title'] = isset($options[$on_value]) ? $options[$on_value] : '';
+
+ if ($instance['widget']['settings']['display_label']) {
+ $element['#title'] = $instance['label'];
+ }
+ break;
+ }
+
+ $element += array(
+ '#value_key' => $value_key,
+ '#element_validate' => array('options_field_widget_validate'),
+ '#properties' => $properties,
+ );
+
+ return $element;
+}
+
+/**
+ * Implements hook_field_widget_settings_form().
+ */
+function options_field_widget_settings_form($field, $instance) {
+ $form = array();
+ if ($instance['widget']['type'] == 'options_onoff') {
+ $form['display_label'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Use field label instead of the "On value" as label'),
+ '#default_value' => $instance['widget']['settings']['display_label'],
+ '#weight' => -1,
+ );
+ }
+ return $form;
+}
+
+/**
+ * Form element validation handler for options element.
+ */
+function options_field_widget_validate($element, &$form_state) {
+ if ($element['#required'] && $element['#value'] == '_none') {
+ form_error($element, t('!name field is required.', array('!name' => $element['#title'])));
+ }
+ // Transpose selections from field => delta to delta => field, turning
+ // multiple selected options into multiple parent elements.
+ $items = _options_form_to_storage($element);
+ form_set_value($element, $items, $form_state);
+}
+
+/**
+ * Describes the preparation steps required by each widget.
+ */
+function _options_properties($type, $multiple, $required, $has_value) {
+ $base = array(
+ 'filter_xss' => FALSE,
+ 'strip_tags' => FALSE,
+ 'empty_option' => FALSE,
+ 'optgroups' => FALSE,
+ );
+
+ $properties = array();
+
+ switch ($type) {
+ case 'select':
+ $properties = array(
+ // Select boxes do not support any HTML tag.
+ 'strip_tags' => TRUE,
+ 'optgroups' => TRUE,
+ );
+ if ($multiple) {
+ // Multiple select: add a 'none' option for non-required fields.
+ if (!$required) {
+ $properties['empty_option'] = 'option_none';
+ }
+ }
+ else {
+ // Single select: add a 'none' option for non-required fields,
+ // and a 'select a value' option for required fields that do not come
+ // with a value selected.
+ if (!$required) {
+ $properties['empty_option'] = 'option_none';
+ }
+ elseif (!$has_value) {
+ $properties['empty_option'] = 'option_select';
+ }
+ }
+ break;
+
+ case 'buttons':
+ $properties = array(
+ 'filter_xss' => TRUE,
+ );
+ // Add a 'none' option for non-required radio buttons.
+ if (!$required && !$multiple) {
+ $properties['empty_option'] = 'option_none';
+ }
+ break;
+
+ case 'onoff':
+ $properties = array(
+ 'filter_xss' => TRUE,
+ );
+ break;
+ }
+
+ return $properties + $base;
+}
+
+/**
+ * Collects the options for a field.
+ */
+function _options_get_options($field, $instance, $properties, $entity_type, $entity) {
+ // Get the list of options.
+ $options = (array) module_invoke($field['module'], 'options_list', $field, $instance, $entity_type, $entity);
+
+ // Sanitize the options.
+ _options_prepare_options($options, $properties);
+
+ if (!$properties['optgroups']) {
+ $options = options_array_flatten($options);
+ }
+
+ if ($properties['empty_option']) {
+ $label = theme('options_none', array('instance' => $instance, 'option' => $properties['empty_option']));
+ $options = array('_none' => $label) + $options;
+ }
+
+ return $options;
+}
+
+/**
+ * Sanitizes the options.
+ *
+ * The function is recursive to support optgroups.
+ */
+function _options_prepare_options(&$options, $properties) {
+ foreach ($options as $value => $label) {
+ // Recurse for optgroups.
+ if (is_array($label)) {
+ _options_prepare_options($options[$value], $properties);
+ }
+ else {
+ if ($properties['strip_tags']) {
+ $options[$value] = strip_tags($label);
+ }
+ if ($properties['filter_xss']) {
+ $options[$value] = field_filter_xss($label);
+ }
+ }
+ }
+}
+
+/**
+ * Transforms stored field values into the format the widgets need.
+ */
+function _options_storage_to_form($items, $options, $column, $properties) {
+ $items_transposed = options_array_transpose($items);
+ $values = (isset($items_transposed[$column]) && is_array($items_transposed[$column])) ? $items_transposed[$column] : array();
+
+ // Discard values that are not in the current list of options. Flatten the
+ // array if needed.
+ if ($properties['optgroups']) {
+ $options = options_array_flatten($options);
+ }
+ $values = array_values(array_intersect($values, array_keys($options)));
+ return $values;
+}
+
+/**
+ * Transforms submitted form values into field storage format.
+ */
+function _options_form_to_storage($element) {
+ $values = array_values((array) $element['#value']);
+ $properties = $element['#properties'];
+
+ // On/off checkbox: transform '0 / 1' into the 'on / off' values.
+ if ($element['#type'] == 'checkbox') {
+ $values = array($values[0] ? $element['#on_value'] : $element['#off_value']);
+ }
+
+ // Filter out the 'none' option. Use a strict comparison, because
+ // 0 == 'any string'.
+ if ($properties['empty_option']) {
+ $index = array_search('_none', $values, TRUE);
+ if ($index !== FALSE) {
+ unset($values[$index]);
+ }
+ }
+
+ // Make sure we populate at least an empty value.
+ if (empty($values)) {
+ $values = array(NULL);
+ }
+
+ $result = options_array_transpose(array($element['#value_key'] => $values));
+ return $result;
+}
+
+/**
+ * Manipulates a 2D array to reverse rows and columns.
+ *
+ * The default data storage for fields is delta first, column names second.
+ * This is sometimes inconvenient for field modules, so this function can be
+ * used to present the data in an alternate format.
+ *
+ * @param $array
+ * The array to be transposed. It must be at least two-dimensional, and
+ * the subarrays must all have the same keys or behavior is undefined.
+ * @return
+ * The transposed array.
+ */
+function options_array_transpose($array) {
+ $result = array();
+ if (is_array($array)) {
+ foreach ($array as $key1 => $value1) {
+ if (is_array($value1)) {
+ foreach ($value1 as $key2 => $value2) {
+ if (!isset($result[$key2])) {
+ $result[$key2] = array();
+ }
+ $result[$key2][$key1] = $value2;
+ }
+ }
+ }
+ }
+ return $result;
+}
+
+/**
+ * Flattens an array of allowed values.
+ *
+ * @param $array
+ * A single or multidimensional array.
+ * @return
+ * A flattened array.
+ */
+function options_array_flatten($array) {
+ $result = array();
+ if (is_array($array)) {
+ foreach ($array as $key => $value) {
+ if (is_array($value)) {
+ $result += options_array_flatten($value);
+ }
+ else {
+ $result[$key] = $value;
+ }
+ }
+ }
+ return $result;
+}
+
+/**
+ * Implements hook_field_widget_error().
+ */
+function options_field_widget_error($element, $error, $form, &$form_state) {
+ form_error($element, $error['message']);
+}
+
+/**
+ * Returns HTML for the label for the empty value for options that are not required.
+ *
+ * The default theme will display N/A for a radio list and '- None -' for a select.
+ *
+ * @param $variables
+ * An associative array containing:
+ * - instance: An array representing the widget requesting the options.
+ *
+ * @ingroup themeable
+ */
+function theme_options_none($variables) {
+ $instance = $variables['instance'];
+ $option = $variables['option'];
+
+ $output = '';
+ switch ($instance['widget']['type']) {
+ case 'options_buttons':
+ $output = t('N/A');
+ break;
+
+ case 'options_select':
+ $output = ($option == 'option_none' ? t('- None -') : t('- Select a value -'));
+ break;
+ }
+
+ return $output;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/options/options.test b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.test
new file mode 100644
index 0000000..7183311
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/options/options.test
@@ -0,0 +1,553 @@
+<?php
+
+/**
+ * @file
+ * Tests for options.module.
+ */
+
+class OptionsWidgetsTestCase extends FieldTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Options widgets',
+ 'description' => "Test the Options widgets.",
+ 'group' => 'Field types'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_test', 'list_test');
+
+ // Field with cardinality 1.
+ $this->card_1 = array(
+ 'field_name' => 'card_1',
+ 'type' => 'list_integer',
+ 'cardinality' => 1,
+ 'settings' => array(
+ // Make sure that 0 works as an option.
+ 'allowed_values' => array(0 => 'Zero', 1 => 'One', 2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>'),
+ ),
+ );
+ $this->card_1 = field_create_field($this->card_1);
+
+ // Field with cardinality 2.
+ $this->card_2 = array(
+ 'field_name' => 'card_2',
+ 'type' => 'list_integer',
+ 'cardinality' => 2,
+ 'settings' => array(
+ // Make sure that 0 works as an option.
+ 'allowed_values' => array(0 => 'Zero', 1 => 'One', 2 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>'),
+ ),
+ );
+ $this->card_2 = field_create_field($this->card_2);
+
+ // Boolean field.
+ $this->bool = array(
+ 'field_name' => 'bool',
+ 'type' => 'list_boolean',
+ 'cardinality' => 1,
+ 'settings' => array(
+ // Make sure that 0 works as a 'on' value'.
+ 'allowed_values' => array(1 => 'Zero', 0 => 'Some <script>dangerous</script> & unescaped <strong>markup</strong>'),
+ ),
+ );
+ $this->bool = field_create_field($this->bool);
+
+ // Create a web user.
+ $this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
+ $this->drupalLogin($this->web_user);
+ }
+
+ /**
+ * Tests the 'options_buttons' widget (single select).
+ */
+ function testRadioButtons() {
+ // Create an instance of the 'single value' field.
+ $instance = array(
+ 'field_name' => $this->card_1['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'options_buttons',
+ ),
+ );
+ $instance = field_create_instance($instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Create an entity.
+ $entity_init = field_test_create_stub_entity();
+ $entity = clone $entity_init;
+ $entity->is_new = TRUE;
+ field_test_entity_save($entity);
+
+ // With no field data, no buttons are checked.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertNoFieldChecked("edit-card-1-$langcode-0");
+ $this->assertNoFieldChecked("edit-card-1-$langcode-1");
+ $this->assertNoFieldChecked("edit-card-1-$langcode-2");
+ $this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', 'Option text was properly filtered.');
+
+ // Select first option.
+ $edit = array("card_1[$langcode]" => 0);
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_1', $langcode, array(0));
+
+ // Check that the selected button is checked.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertFieldChecked("edit-card-1-$langcode-0");
+ $this->assertNoFieldChecked("edit-card-1-$langcode-1");
+ $this->assertNoFieldChecked("edit-card-1-$langcode-2");
+
+ // Unselect option.
+ $edit = array("card_1[$langcode]" => '_none');
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_1', $langcode, array());
+
+ // Check that required radios with one option is auto-selected.
+ $this->card_1['settings']['allowed_values'] = array(99 => 'Only allowed value');
+ field_update_field($this->card_1);
+ $instance['required'] = TRUE;
+ field_update_instance($instance);
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertFieldChecked("edit-card-1-$langcode-99");
+ }
+
+ /**
+ * Tests the 'options_buttons' widget (multiple select).
+ */
+ function testCheckBoxes() {
+ // Create an instance of the 'multiple values' field.
+ $instance = array(
+ 'field_name' => $this->card_2['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'options_buttons',
+ ),
+ );
+ $instance = field_create_instance($instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Create an entity.
+ $entity_init = field_test_create_stub_entity();
+ $entity = clone $entity_init;
+ $entity->is_new = TRUE;
+ field_test_entity_save($entity);
+
+ // Display form: with no field data, nothing is checked.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertNoFieldChecked("edit-card-2-$langcode-0");
+ $this->assertNoFieldChecked("edit-card-2-$langcode-1");
+ $this->assertNoFieldChecked("edit-card-2-$langcode-2");
+ $this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', 'Option text was properly filtered.');
+
+ // Submit form: select first and third options.
+ $edit = array(
+ "card_2[$langcode][0]" => TRUE,
+ "card_2[$langcode][1]" => FALSE,
+ "card_2[$langcode][2]" => TRUE,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0, 2));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertFieldChecked("edit-card-2-$langcode-0");
+ $this->assertNoFieldChecked("edit-card-2-$langcode-1");
+ $this->assertFieldChecked("edit-card-2-$langcode-2");
+
+ // Submit form: select only first option.
+ $edit = array(
+ "card_2[$langcode][0]" => TRUE,
+ "card_2[$langcode][1]" => FALSE,
+ "card_2[$langcode][2]" => FALSE,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertFieldChecked("edit-card-2-$langcode-0");
+ $this->assertNoFieldChecked("edit-card-2-$langcode-1");
+ $this->assertNoFieldChecked("edit-card-2-$langcode-2");
+
+ // Submit form: select the three options while the field accepts only 2.
+ $edit = array(
+ "card_2[$langcode][0]" => TRUE,
+ "card_2[$langcode][1]" => TRUE,
+ "card_2[$langcode][2]" => TRUE,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertText('this field cannot hold more than 2 values', 'Validation error was displayed.');
+
+ // Submit form: uncheck all options.
+ $edit = array(
+ "card_2[$langcode][0]" => FALSE,
+ "card_2[$langcode][1]" => FALSE,
+ "card_2[$langcode][2]" => FALSE,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ // Check that the value was saved.
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array());
+
+ // Required checkbox with one option is auto-selected.
+ $this->card_2['settings']['allowed_values'] = array(99 => 'Only allowed value');
+ field_update_field($this->card_2);
+ $instance['required'] = TRUE;
+ field_update_instance($instance);
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertFieldChecked("edit-card-2-$langcode-99");
+ }
+
+ /**
+ * Tests the 'options_select' widget (single select).
+ */
+ function testSelectListSingle() {
+ // Create an instance of the 'single value' field.
+ $instance = array(
+ 'field_name' => $this->card_1['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'required' => TRUE,
+ 'widget' => array(
+ 'type' => 'options_select',
+ ),
+ );
+ $instance = field_create_instance($instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Create an entity.
+ $entity_init = field_test_create_stub_entity();
+ $entity = clone $entity_init;
+ $entity->is_new = TRUE;
+ field_test_entity_save($entity);
+
+ // Display form.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ // A required field without any value has a "none" option.
+ $this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- Select a value -'))), 'A required select list has a "Select a value" choice.');
+
+ // With no field data, nothing is selected.
+ $this->assertNoOptionSelected("edit-card-1-$langcode", '_none');
+ $this->assertNoOptionSelected("edit-card-1-$langcode", 0);
+ $this->assertNoOptionSelected("edit-card-1-$langcode", 1);
+ $this->assertNoOptionSelected("edit-card-1-$langcode", 2);
+ $this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
+
+ // Submit form: select invalid 'none' option.
+ $edit = array("card_1[$langcode]" => '_none');
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertRaw(t('!title field is required.', array('!title' => $instance['field_name'])), 'Cannot save a required field when selecting "none" from the select list.');
+
+ // Submit form: select first option.
+ $edit = array("card_1[$langcode]" => 0);
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_1', $langcode, array(0));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ // A required field with a value has no 'none' option.
+ $this->assertFalse($this->xpath('//select[@id=:id]//option[@value="_none"]', array(':id' => 'edit-card-1-' . $langcode)), 'A required select list with an actual value has no "none" choice.');
+ $this->assertOptionSelected("edit-card-1-$langcode", 0);
+ $this->assertNoOptionSelected("edit-card-1-$langcode", 1);
+ $this->assertNoOptionSelected("edit-card-1-$langcode", 2);
+
+ // Make the field non required.
+ $instance['required'] = FALSE;
+ field_update_instance($instance);
+
+ // Display form.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ // A non-required field has a 'none' option.
+ $this->assertTrue($this->xpath('//select[@id=:id]//option[@value="_none" and text()=:label]', array(':id' => 'edit-card-1-' . $langcode, ':label' => t('- None -'))), 'A non-required select list has a "None" choice.');
+ // Submit form: Unselect the option.
+ $edit = array("card_1[$langcode]" => '_none');
+ $this->drupalPost('test-entity/manage/' . $entity->ftid . '/edit', $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_1', $langcode, array());
+
+ // Test optgroups.
+
+ $this->card_1['settings']['allowed_values'] = array();
+ $this->card_1['settings']['allowed_values_function'] = 'list_test_allowed_values_callback';
+ field_update_field($this->card_1);
+
+ // Display form: with no field data, nothing is selected
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertNoOptionSelected("edit-card-1-$langcode", 0);
+ $this->assertNoOptionSelected("edit-card-1-$langcode", 1);
+ $this->assertNoOptionSelected("edit-card-1-$langcode", 2);
+ $this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
+ $this->assertRaw('Group 1', 'Option groups are displayed.');
+
+ // Submit form: select first option.
+ $edit = array("card_1[$langcode]" => 0);
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_1', $langcode, array(0));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertOptionSelected("edit-card-1-$langcode", 0);
+ $this->assertNoOptionSelected("edit-card-1-$langcode", 1);
+ $this->assertNoOptionSelected("edit-card-1-$langcode", 2);
+
+ // Submit form: Unselect the option.
+ $edit = array("card_1[$langcode]" => '_none');
+ $this->drupalPost('test-entity/manage/' . $entity->ftid . '/edit', $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_1', $langcode, array());
+ }
+
+ /**
+ * Tests the 'options_select' widget (multiple select).
+ */
+ function testSelectListMultiple() {
+ // Create an instance of the 'multiple values' field.
+ $instance = array(
+ 'field_name' => $this->card_2['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'options_select',
+ ),
+ );
+ $instance = field_create_instance($instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Create an entity.
+ $entity_init = field_test_create_stub_entity();
+ $entity = clone $entity_init;
+ $entity->is_new = TRUE;
+ field_test_entity_save($entity);
+
+ // Display form: with no field data, nothing is selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 0);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 1);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 2);
+ $this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
+
+ // Submit form: select first and third options.
+ $edit = array("card_2[$langcode][]" => array(0 => 0, 2 => 2));
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0, 2));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertOptionSelected("edit-card-2-$langcode", 0);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 1);
+ $this->assertOptionSelected("edit-card-2-$langcode", 2);
+
+ // Submit form: select only first option.
+ $edit = array("card_2[$langcode][]" => array(0 => 0));
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertOptionSelected("edit-card-2-$langcode", 0);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 1);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 2);
+
+ // Submit form: select the three options while the field accepts only 2.
+ $edit = array("card_2[$langcode][]" => array(0 => 0, 1 => 1, 2 => 2));
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertText('this field cannot hold more than 2 values', 'Validation error was displayed.');
+
+ // Submit form: uncheck all options.
+ $edit = array("card_2[$langcode][]" => array());
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array());
+
+ // Test the 'None' option.
+
+ // Check that the 'none' option has no effect if actual options are selected
+ // as well.
+ $edit = array("card_2[$langcode][]" => array('_none' => '_none', 0 => 0));
+ $this->drupalPost('test-entity/manage/' . $entity->ftid . '/edit', $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0));
+
+ // Check that selecting the 'none' option empties the field.
+ $edit = array("card_2[$langcode][]" => array('_none' => '_none'));
+ $this->drupalPost('test-entity/manage/' . $entity->ftid . '/edit', $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array());
+
+ // A required select list does not have an empty key.
+ $instance['required'] = TRUE;
+ field_update_instance($instance);
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertFalse($this->xpath('//select[@id=:id]//option[@value=""]', array(':id' => 'edit-card-2-' . $langcode)), 'A required select list does not have an empty key.');
+
+ // We do not have to test that a required select list with one option is
+ // auto-selected because the browser does it for us.
+
+ // Test optgroups.
+
+ // Use a callback function defining optgroups.
+ $this->card_2['settings']['allowed_values'] = array();
+ $this->card_2['settings']['allowed_values_function'] = 'list_test_allowed_values_callback';
+ field_update_field($this->card_2);
+ $instance['required'] = FALSE;
+ field_update_instance($instance);
+
+ // Display form: with no field data, nothing is selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 0);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 1);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 2);
+ $this->assertRaw('Some dangerous &amp; unescaped markup', 'Option text was properly filtered.');
+ $this->assertRaw('Group 1', 'Option groups are displayed.');
+
+ // Submit form: select first option.
+ $edit = array("card_2[$langcode][]" => array(0 => 0));
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array(0));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertOptionSelected("edit-card-2-$langcode", 0);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 1);
+ $this->assertNoOptionSelected("edit-card-2-$langcode", 2);
+
+ // Submit form: Unselect the option.
+ $edit = array("card_2[$langcode][]" => array('_none' => '_none'));
+ $this->drupalPost('test-entity/manage/' . $entity->ftid . '/edit', $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'card_2', $langcode, array());
+ }
+
+ /**
+ * Tests the 'options_onoff' widget.
+ */
+ function testOnOffCheckbox() {
+ // Create an instance of the 'boolean' field.
+ $instance = array(
+ 'field_name' => $this->bool['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'options_onoff',
+ ),
+ );
+ $instance = field_create_instance($instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Create an entity.
+ $entity_init = field_test_create_stub_entity();
+ $entity = clone $entity_init;
+ $entity->is_new = TRUE;
+ field_test_entity_save($entity);
+
+ // Display form: with no field data, option is unchecked.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertNoFieldChecked("edit-bool-$langcode");
+ $this->assertRaw('Some dangerous &amp; unescaped <strong>markup</strong>', 'Option text was properly filtered.');
+
+ // Submit form: check the option.
+ $edit = array("bool[$langcode]" => TRUE);
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'bool', $langcode, array(0));
+
+ // Display form: check that the right options are selected.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertFieldChecked("edit-bool-$langcode");
+
+ // Submit form: uncheck the option.
+ $edit = array("bool[$langcode]" => FALSE);
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertFieldValues($entity_init, 'bool', $langcode, array(1));
+
+ // Display form: with 'off' value, option is unchecked.
+ $this->drupalGet('test-entity/manage/' . $entity->ftid . '/edit');
+ $this->assertNoFieldChecked("edit-bool-$langcode");
+
+ // Create admin user.
+ $admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer taxonomy'));
+ $this->drupalLogin($admin_user);
+
+ // Create a test field instance.
+ $fieldUpdate = $this->bool;
+ $fieldUpdate['settings']['allowed_values'] = array(0 => 0, 1 => 'MyOnValue');
+ field_update_field($fieldUpdate);
+ $instance = array(
+ 'field_name' => $this->bool['field_name'],
+ 'entity_type' => 'node',
+ 'bundle' => 'page',
+ 'widget' => array(
+ 'type' => 'options_onoff',
+ 'module' => 'options',
+ ),
+ );
+ field_create_instance($instance);
+
+ // Go to the edit page and check if the default settings works as expected
+ $fieldEditUrl = 'admin/structure/types/manage/page/fields/bool';
+ $this->drupalGet($fieldEditUrl);
+
+ $this->assertText(
+ 'Use field label instead of the "On value" as label ',
+ 'Display setting checkbox available.'
+ );
+
+ $this->assertFieldByXPath(
+ '*//label[@for="edit-' . $this->bool['field_name'] . '-und" and text()="MyOnValue "]',
+ TRUE,
+ 'Default case shows "On value"'
+ );
+
+ // Enable setting
+ $edit = array('instance[widget][settings][display_label]' => 1);
+ // Save the new Settings
+ $this->drupalPost($fieldEditUrl, $edit, t('Save settings'));
+
+ // Go again to the edit page and check if the setting
+ // is stored and has the expected effect
+ $this->drupalGet($fieldEditUrl);
+ $this->assertText(
+ 'Use field label instead of the "On value" as label ',
+ 'Display setting checkbox is available'
+ );
+ $this->assertFieldChecked(
+ 'edit-instance-widget-settings-display-label',
+ 'Display settings checkbox checked'
+ );
+ $this->assertFieldByXPath(
+ '*//label[@for="edit-' . $this->bool['field_name'] . '-und" and text()="' . $this->bool['field_name'] . ' "]',
+ TRUE,
+ 'Display label changes label of the checkbox'
+ );
+ }
+}
+
+/**
+ * Test an options select on a list field with a dynamic allowed values function.
+ */
+class OptionsSelectDynamicValuesTestCase extends ListDynamicValuesTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Options select dynamic values',
+ 'description' => 'Test an options select on a list field with a dynamic allowed values function.',
+ 'group' => 'Field types',
+ );
+ }
+
+ /**
+ * Tests the 'options_select' widget (single select).
+ */
+ function testSelectListDynamic() {
+ // Create an entity.
+ $this->entity->is_new = TRUE;
+ field_test_entity_save($this->entity);
+ // Create a web user.
+ $web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
+ $this->drupalLogin($web_user);
+
+ // Display form.
+ $this->drupalGet('test-entity/manage/' . $this->entity->ftid . '/edit');
+ $options = $this->xpath('//select[@id="edit-test-list-und"]/option');
+ $this->assertEqual(count($options), count($this->test) + 1);
+ foreach ($options as $option) {
+ $value = (string) $option['value'];
+ if ($value != '_none') {
+ $this->assertTrue(array_search($value, $this->test));
+ }
+ }
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/text/text.info b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.info
new file mode 100644
index 0000000..4dd9b00
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.info
@@ -0,0 +1,14 @@
+name = Text
+description = Defines simple text field types.
+package = Core
+version = VERSION
+core = 7.x
+dependencies[] = field
+files[] = text.test
+required = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/text/text.install b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.install
new file mode 100644
index 0000000..61be748
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.install
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the text module.
+ */
+
+/**
+ * Implements hook_field_schema().
+ */
+function text_field_schema($field) {
+ switch ($field['type']) {
+ case 'text':
+ $columns = array(
+ 'value' => array(
+ 'type' => 'varchar',
+ 'length' => $field['settings']['max_length'],
+ 'not null' => FALSE,
+ ),
+ );
+ break;
+
+ case 'text_long':
+ $columns = array(
+ 'value' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ );
+ break;
+
+ case 'text_with_summary':
+ $columns = array(
+ 'value' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ 'summary' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ );
+ break;
+ }
+ $columns += array(
+ 'format' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => FALSE,
+ ),
+ );
+ return array(
+ 'columns' => $columns,
+ 'indexes' => array(
+ 'format' => array('format'),
+ ),
+ 'foreign keys' => array(
+ 'format' => array(
+ 'table' => 'filter_format',
+ 'columns' => array('format' => 'format'),
+ ),
+ ),
+ );
+}
+
+/**
+ * Change text field 'format' columns into varchar.
+ */
+function text_update_7000() {
+ $spec = array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => FALSE,
+ );
+ $fields = _update_7000_field_read_fields(array(
+ 'module' => 'text',
+ 'storage_type' => 'field_sql_storage',
+ ));
+ foreach ($fields as $field) {
+ if ($field['deleted']) {
+ $table = "field_deleted_data_{$field['id']}";
+ $revision_table = "field_deleted_revision_{$field['id']}";
+ }
+ else {
+ $table = "field_data_{$field['field_name']}";
+ $revision_table = "field_revision_{$field['field_name']}";
+ }
+ $column = $field['field_name'] . '_' . 'format';
+ db_change_field($table, $column, $column, $spec);
+ db_change_field($revision_table, $column, $column, $spec);
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/text/text.js b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.js
new file mode 100644
index 0000000..f3ae894
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.js
@@ -0,0 +1,49 @@
+
+(function ($) {
+
+/**
+ * Auto-hide summary textarea if empty and show hide and unhide links.
+ */
+Drupal.behaviors.textSummary = {
+ attach: function (context, settings) {
+ $('.text-summary', context).once('text-summary', function () {
+ var $widget = $(this).closest('div.field-type-text-with-summary');
+ var $summaries = $widget.find('div.text-summary-wrapper');
+
+ $summaries.once('text-summary-wrapper').each(function(index) {
+ var $summary = $(this);
+ var $summaryLabel = $summary.find('label');
+ var $full = $widget.find('.text-full').eq(index).closest('.form-item');
+ var $fullLabel = $full.find('label');
+
+ // Create a placeholder label when the field cardinality is
+ // unlimited or greater than 1.
+ if ($fullLabel.length == 0) {
+ $fullLabel = $('<label></label>').prependTo($full);
+ }
+
+ // Setup the edit/hide summary link.
+ var $link = $('<span class="field-edit-link">(<a class="link-edit-summary" href="#">' + Drupal.t('Hide summary') + '</a>)</span>').toggle(
+ function () {
+ $summary.hide();
+ $(this).find('a').html(Drupal.t('Edit summary')).end().appendTo($fullLabel);
+ return false;
+ },
+ function () {
+ $summary.show();
+ $(this).find('a').html(Drupal.t('Hide summary')).end().appendTo($summaryLabel);
+ return false;
+ }
+ ).appendTo($summaryLabel);
+
+ // If no summary is set, hide the summary field.
+ if ($(this).find('.text-summary').val() == '') {
+ $link.click();
+ }
+ return;
+ });
+ });
+ }
+};
+
+})(jQuery);
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/text/text.module b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.module
new file mode 100644
index 0000000..d73814f
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.module
@@ -0,0 +1,611 @@
+<?php
+
+/**
+ * @file
+ * Defines simple text field types.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function text_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#text':
+ $output = '';
+ $output .= '<h3>' . t('About') . '</h3>';
+ $output .= '<p>' . t("The Text module defines various text field types for the Field module. A text field may contain plain text only, or optionally, may use Drupal's <a href='@filter-help'>text filters</a> to securely manage HTML output. Text input fields may be either a single line (text field), multiple lines (text area), or for greater input control, a select box, checkbox, or radio buttons. If desired, the field can be validated, so that it is limited to a set of allowed values. See the <a href='@field-help'>Field module help page</a> for more information about fields.", array('@field-help' => url('admin/help/field'), '@filter-help' => url('admin/help/filter'))) . '</p>';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_field_info().
+ *
+ * Field settings:
+ * - max_length: the maximum length for a varchar field.
+ * Instance settings:
+ * - text_processing: whether text input filters should be used.
+ * - display_summary: whether the summary field should be displayed.
+ * When empty and not displayed the summary will take its value from the
+ * trimmed value of the main text field.
+ */
+function text_field_info() {
+ return array(
+ 'text' => array(
+ 'label' => t('Text'),
+ 'description' => t('This field stores varchar text in the database.'),
+ 'settings' => array('max_length' => 255),
+ 'instance_settings' => array('text_processing' => 0),
+ 'default_widget' => 'text_textfield',
+ 'default_formatter' => 'text_default',
+ ),
+ 'text_long' => array(
+ 'label' => t('Long text'),
+ 'description' => t('This field stores long text in the database.'),
+ 'instance_settings' => array('text_processing' => 0),
+ 'default_widget' => 'text_textarea',
+ 'default_formatter' => 'text_default',
+ ),
+ 'text_with_summary' => array(
+ 'label' => t('Long text and summary'),
+ 'description' => t('This field stores long text in the database along with optional summary text.'),
+ 'instance_settings' => array('text_processing' => 1, 'display_summary' => 0),
+ 'default_widget' => 'text_textarea_with_summary',
+ 'default_formatter' => 'text_default',
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_settings_form().
+ */
+function text_field_settings_form($field, $instance, $has_data) {
+ $settings = $field['settings'];
+
+ $form = array();
+
+ if ($field['type'] == 'text') {
+ $form['max_length'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Maximum length'),
+ '#default_value' => $settings['max_length'],
+ '#required' => TRUE,
+ '#description' => t('The maximum length of the field in characters.'),
+ '#element_validate' => array('element_validate_integer_positive'),
+ // @todo: If $has_data, add a validate handler that only allows
+ // max_length to increase.
+ '#disabled' => $has_data,
+ );
+ }
+
+ return $form;
+}
+
+/**
+ * Implements hook_field_instance_settings_form().
+ */
+function text_field_instance_settings_form($field, $instance) {
+ $settings = $instance['settings'];
+
+ $form['text_processing'] = array(
+ '#type' => 'radios',
+ '#title' => t('Text processing'),
+ '#default_value' => $settings['text_processing'],
+ '#options' => array(
+ t('Plain text'),
+ t('Filtered text (user selects text format)'),
+ ),
+ );
+ if ($field['type'] == 'text_with_summary') {
+ $form['display_summary'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Summary input'),
+ '#default_value' => $settings['display_summary'],
+ '#description' => t('This allows authors to input an explicit summary, to be displayed instead of the automatically trimmed text when using the "Summary or trimmed" display type.'),
+ );
+ }
+
+ return $form;
+}
+
+/**
+ * Implements hook_field_validate().
+ *
+ * Possible error codes:
+ * - 'text_value_max_length': The value exceeds the maximum length.
+ * - 'text_summary_max_length': The summary exceeds the maximum length.
+ */
+function text_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
+ foreach ($items as $delta => $item) {
+ // @todo Length is counted separately for summary and value, so the maximum
+ // length can be exceeded very easily.
+ foreach (array('value', 'summary') as $column) {
+ if (!empty($item[$column])) {
+ if (!empty($field['settings']['max_length']) && drupal_strlen($item[$column]) > $field['settings']['max_length']) {
+ switch ($column) {
+ case 'value':
+ $message = t('%name: the text may not be longer than %max characters.', array('%name' => $instance['label'], '%max' => $field['settings']['max_length']));
+ break;
+
+ case 'summary':
+ $message = t('%name: the summary may not be longer than %max characters.', array('%name' => $instance['label'], '%max' => $field['settings']['max_length']));
+ break;
+ }
+ $errors[$field['field_name']][$langcode][$delta][] = array(
+ 'error' => "text_{$column}_length",
+ 'message' => $message,
+ );
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_field_load().
+ *
+ * Where possible, generate the sanitized version of each field early so that
+ * it is cached in the field cache. This avoids looking up from the filter cache
+ * separately.
+ *
+ * @see text_field_formatter_view()
+ */
+function text_field_load($entity_type, $entities, $field, $instances, $langcode, &$items) {
+ foreach ($entities as $id => $entity) {
+ foreach ($items[$id] as $delta => $item) {
+ // Only process items with a cacheable format, the rest will be handled
+ // by formatters if needed.
+ if (empty($instances[$id]['settings']['text_processing']) || filter_format_allowcache($item['format'])) {
+ $items[$id][$delta]['safe_value'] = isset($item['value']) ? _text_sanitize($instances[$id], $langcode, $item, 'value') : '';
+ if ($field['type'] == 'text_with_summary') {
+ $items[$id][$delta]['safe_summary'] = isset($item['summary']) ? _text_sanitize($instances[$id], $langcode, $item, 'summary') : '';
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_field_is_empty().
+ */
+function text_field_is_empty($item, $field) {
+ if (!isset($item['value']) || $item['value'] === '') {
+ return !isset($item['summary']) || $item['summary'] === '';
+ }
+ return FALSE;
+}
+
+/**
+ * Implements hook_field_formatter_info().
+ */
+function text_field_formatter_info() {
+ return array(
+ 'text_default' => array(
+ 'label' => t('Default'),
+ 'field types' => array('text', 'text_long', 'text_with_summary'),
+ ),
+ 'text_plain' => array(
+ 'label' => t('Plain text'),
+ 'field types' => array('text', 'text_long', 'text_with_summary'),
+ ),
+
+ // The text_trimmed formatter displays the trimmed version of the
+ // full element of the field. It is intended to be used with text
+ // and text_long fields. It also works with text_with_summary
+ // fields though the text_summary_or_trimmed formatter makes more
+ // sense for that field type.
+ 'text_trimmed' => array(
+ 'label' => t('Trimmed'),
+ 'field types' => array('text', 'text_long', 'text_with_summary'),
+ 'settings' => array('trim_length' => 600),
+ ),
+
+ // The 'summary or trimmed' field formatter for text_with_summary
+ // fields displays returns the summary element of the field or, if
+ // the summary is empty, the trimmed version of the full element
+ // of the field.
+ 'text_summary_or_trimmed' => array(
+ 'label' => t('Summary or trimmed'),
+ 'field types' => array('text_with_summary'),
+ 'settings' => array('trim_length' => 600),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_formatter_settings_form().
+ */
+function text_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
+ $display = $instance['display'][$view_mode];
+ $settings = $display['settings'];
+
+ $element = array();
+
+ if (strpos($display['type'], '_trimmed') !== FALSE) {
+ $element['trim_length'] = array(
+ '#title' => t('Trim length'),
+ '#type' => 'textfield',
+ '#size' => 10,
+ '#default_value' => $settings['trim_length'],
+ '#element_validate' => array('element_validate_integer_positive'),
+ '#required' => TRUE,
+ );
+ }
+
+ return $element;
+}
+
+/**
+ * Implements hook_field_formatter_settings_summary().
+ */
+function text_field_formatter_settings_summary($field, $instance, $view_mode) {
+ $display = $instance['display'][$view_mode];
+ $settings = $display['settings'];
+
+ $summary = '';
+
+ if (strpos($display['type'], '_trimmed') !== FALSE) {
+ $summary = t('Trim length') . ': ' . $settings['trim_length'];
+ }
+
+ return $summary;
+}
+
+/**
+ * Implements hook_field_formatter_view().
+ */
+function text_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+ $element = array();
+
+ switch ($display['type']) {
+ case 'text_default':
+ case 'text_trimmed':
+ foreach ($items as $delta => $item) {
+ $output = _text_sanitize($instance, $langcode, $item, 'value');
+ if ($display['type'] == 'text_trimmed') {
+ $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
+ }
+ $element[$delta] = array('#markup' => $output);
+ }
+ break;
+
+ case 'text_summary_or_trimmed':
+ foreach ($items as $delta => $item) {
+ if (!empty($item['summary'])) {
+ $output = _text_sanitize($instance, $langcode, $item, 'summary');
+ }
+ else {
+ $output = _text_sanitize($instance, $langcode, $item, 'value');
+ $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
+ }
+ $element[$delta] = array('#markup' => $output);
+ }
+ break;
+
+ case 'text_plain':
+ foreach ($items as $delta => $item) {
+ $element[$delta] = array('#markup' => strip_tags($item['value']));
+ }
+ break;
+ }
+
+ return $element;
+}
+
+/**
+ * Sanitizes the 'value' or 'summary' data of a text value.
+ *
+ * Depending on whether the field instance uses text processing, data is run
+ * through check_plain() or check_markup().
+ *
+ * @param $instance
+ * The instance definition.
+ * @param $langcode
+ * The language associated to $item.
+ * @param $item
+ * The field value to sanitize.
+ * @param $column
+ * The column to sanitize (either 'value' or 'summary').
+ *
+ * @return
+ * The sanitized string.
+ */
+function _text_sanitize($instance, $langcode, $item, $column) {
+ // If the value uses a cacheable text format, text_field_load() precomputes
+ // the sanitized string.
+ if (isset($item["safe_$column"])) {
+ return $item["safe_$column"];
+ }
+ return $instance['settings']['text_processing'] ? check_markup($item[$column], $item['format'], $langcode) : check_plain($item[$column]);
+}
+
+/**
+ * Generate a trimmed, formatted version of a text field value.
+ *
+ * If the end of the summary is not indicated using the <!--break--> delimiter
+ * then we generate the summary automatically, trying to end it at a sensible
+ * place such as the end of a paragraph, a line break, or the end of a
+ * sentence (in that order of preference).
+ *
+ * @param $text
+ * The content for which a summary will be generated.
+ * @param $format
+ * The format of the content.
+ * If the PHP filter is present and $text contains PHP code, we do not
+ * split it up to prevent parse errors.
+ * If the line break filter is present then we treat newlines embedded in
+ * $text as line breaks.
+ * If the htmlcorrector filter is present, it will be run on the generated
+ * summary (if different from the incoming $text).
+ * @param $size
+ * The desired character length of the summary. If omitted, the default
+ * value will be used. Ignored if the special delimiter is present
+ * in $text.
+ * @return
+ * The generated summary.
+ */
+function text_summary($text, $format = NULL, $size = NULL) {
+
+ if (!isset($size)) {
+ // What used to be called 'teaser' is now called 'summary', but
+ // the variable 'teaser_length' is preserved for backwards compatibility.
+ $size = variable_get('teaser_length', 600);
+ }
+
+ // Find where the delimiter is in the body
+ $delimiter = strpos($text, '<!--break-->');
+
+ // If the size is zero, and there is no delimiter, the entire body is the summary.
+ if ($size == 0 && $delimiter === FALSE) {
+ return $text;
+ }
+
+ // If a valid delimiter has been specified, use it to chop off the summary.
+ if ($delimiter !== FALSE) {
+ return substr($text, 0, $delimiter);
+ }
+
+ // We check for the presence of the PHP evaluator filter in the current
+ // format. If the body contains PHP code, we do not split it up to prevent
+ // parse errors.
+ if (isset($format)) {
+ $filters = filter_list_format($format);
+ if (isset($filters['php_code']) && $filters['php_code']->status && strpos($text, '<?') !== FALSE) {
+ return $text;
+ }
+ }
+
+ // If we have a short body, the entire body is the summary.
+ if (drupal_strlen($text) <= $size) {
+ return $text;
+ }
+
+ // If the delimiter has not been specified, try to split at paragraph or
+ // sentence boundaries.
+
+ // The summary may not be longer than maximum length specified. Initial slice.
+ $summary = truncate_utf8($text, $size);
+
+ // Store the actual length of the UTF8 string -- which might not be the same
+ // as $size.
+ $max_rpos = strlen($summary);
+
+ // How much to cut off the end of the summary so that it doesn't end in the
+ // middle of a paragraph, sentence, or word.
+ // Initialize it to maximum in order to find the minimum.
+ $min_rpos = $max_rpos;
+
+ // Store the reverse of the summary. We use strpos on the reversed needle and
+ // haystack for speed and convenience.
+ $reversed = strrev($summary);
+
+ // Build an array of arrays of break points grouped by preference.
+ $break_points = array();
+
+ // A paragraph near the end of sliced summary is most preferable.
+ $break_points[] = array('</p>' => 0);
+
+ // If no complete paragraph then treat line breaks as paragraphs.
+ $line_breaks = array('<br />' => 6, '<br>' => 4);
+ // Newline only indicates a line break if line break converter
+ // filter is present.
+ if (isset($filters['filter_autop'])) {
+ $line_breaks["\n"] = 1;
+ }
+ $break_points[] = $line_breaks;
+
+ // If the first paragraph is too long, split at the end of a sentence.
+ $break_points[] = array('. ' => 1, '! ' => 1, '? ' => 1, '。' => 0, '؟ ' => 1);
+
+ // Iterate over the groups of break points until a break point is found.
+ foreach ($break_points as $points) {
+ // Look for each break point, starting at the end of the summary.
+ foreach ($points as $point => $offset) {
+ // The summary is already reversed, but the break point isn't.
+ $rpos = strpos($reversed, strrev($point));
+ if ($rpos !== FALSE) {
+ $min_rpos = min($rpos + $offset, $min_rpos);
+ }
+ }
+
+ // If a break point was found in this group, slice and stop searching.
+ if ($min_rpos !== $max_rpos) {
+ // Don't slice with length 0. Length must be <0 to slice from RHS.
+ $summary = ($min_rpos === 0) ? $summary : substr($summary, 0, 0 - $min_rpos);
+ break;
+ }
+ }
+
+ // If the htmlcorrector filter is present, apply it to the generated summary.
+ if (isset($filters['filter_htmlcorrector'])) {
+ $summary = _filter_htmlcorrector($summary);
+ }
+
+ return $summary;
+}
+
+/**
+ * Implements hook_field_widget_info().
+ */
+function text_field_widget_info() {
+ return array(
+ 'text_textfield' => array(
+ 'label' => t('Text field'),
+ 'field types' => array('text'),
+ 'settings' => array('size' => 60),
+ ),
+ 'text_textarea' => array(
+ 'label' => t('Text area (multiple rows)'),
+ 'field types' => array('text_long'),
+ 'settings' => array('rows' => 5),
+ ),
+ 'text_textarea_with_summary' => array(
+ 'label' => t('Text area with a summary'),
+ 'field types' => array('text_with_summary'),
+ 'settings' => array('rows' => 20, 'summary_rows' => 5),
+ ),
+ );
+}
+
+/**
+ * Implements hook_field_widget_settings_form().
+ */
+function text_field_widget_settings_form($field, $instance) {
+ $widget = $instance['widget'];
+ $settings = $widget['settings'];
+
+ if ($widget['type'] == 'text_textfield') {
+ $form['size'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Size of textfield'),
+ '#default_value' => $settings['size'],
+ '#required' => TRUE,
+ '#element_validate' => array('element_validate_integer_positive'),
+ );
+ }
+ else {
+ $form['rows'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Rows'),
+ '#default_value' => $settings['rows'],
+ '#required' => TRUE,
+ '#element_validate' => array('element_validate_integer_positive'),
+ );
+ }
+
+ return $form;
+}
+
+/**
+ * Implements hook_field_widget_form().
+ */
+function text_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+ $summary_widget = array();
+ $main_widget = array();
+
+ switch ($instance['widget']['type']) {
+ case 'text_textfield':
+ $main_widget = $element + array(
+ '#type' => 'textfield',
+ '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
+ '#size' => $instance['widget']['settings']['size'],
+ '#maxlength' => $field['settings']['max_length'],
+ '#attributes' => array('class' => array('text-full')),
+ );
+ break;
+
+ case 'text_textarea_with_summary':
+ $display = !empty($items[$delta]['summary']) || !empty($instance['settings']['display_summary']);
+ $summary_widget = array(
+ '#type' => $display ? 'textarea' : 'value',
+ '#default_value' => isset($items[$delta]['summary']) ? $items[$delta]['summary'] : NULL,
+ '#title' => t('Summary'),
+ '#rows' => $instance['widget']['settings']['summary_rows'],
+ '#description' => t('Leave blank to use trimmed value of full text as the summary.'),
+ '#attached' => array(
+ 'js' => array(drupal_get_path('module', 'text') . '/text.js'),
+ ),
+ '#attributes' => array('class' => array('text-summary')),
+ '#prefix' => '<div class="text-summary-wrapper">',
+ '#suffix' => '</div>',
+ '#weight' => -10,
+ );
+ // Fall through to the next case.
+
+ case 'text_textarea':
+ $main_widget = $element + array(
+ '#type' => 'textarea',
+ '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
+ '#rows' => $instance['widget']['settings']['rows'],
+ '#attributes' => array('class' => array('text-full')),
+ );
+ break;
+ }
+
+ if ($main_widget) {
+ // Conditionally alter the form element's type if text processing is enabled.
+ if ($instance['settings']['text_processing']) {
+ $element = $main_widget;
+ $element['#type'] = 'text_format';
+ $element['#format'] = isset($items[$delta]['format']) ? $items[$delta]['format'] : NULL;
+ $element['#base_type'] = $main_widget['#type'];
+ }
+ else {
+ $element['value'] = $main_widget;
+ }
+ }
+ if ($summary_widget) {
+ $element['summary'] = $summary_widget;
+ }
+
+ return $element;
+}
+
+/**
+ * Implements hook_field_widget_error().
+ */
+function text_field_widget_error($element, $error, $form, &$form_state) {
+ switch ($error['error']) {
+ case 'text_summary_max_length':
+ $error_element = $element[$element['#columns'][1]];
+ break;
+
+ default:
+ $error_element = $element[$element['#columns'][0]];
+ break;
+ }
+
+ form_error($error_element, $error['message']);
+}
+
+/**
+ * Implements hook_field_prepare_translation().
+ */
+function text_field_prepare_translation($entity_type, $entity, $field, $instance, $langcode, &$items, $source_entity, $source_langcode) {
+ // If the translating user is not permitted to use the assigned text format,
+ // we must not expose the source values.
+ $field_name = $field['field_name'];
+ if (!empty($source_entity->{$field_name}[$source_langcode])) {
+ $formats = filter_formats();
+ foreach ($source_entity->{$field_name}[$source_langcode] as $delta => $item) {
+ $format_id = $item['format'];
+ if (!empty($format_id) && !filter_access($formats[$format_id])) {
+ unset($items[$delta]);
+ }
+ }
+ }
+}
+
+/**
+ * Implements hook_filter_format_update().
+ */
+function text_filter_format_update($format) {
+ field_cache_clear();
+}
+
+/**
+ * Implements hook_filter_format_disable().
+ */
+function text_filter_format_disable($format) {
+ field_cache_clear();
+}
diff --git a/kolab.org/www/drupal-7.26/modules/field/modules/text/text.test b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.test
new file mode 100644
index 0000000..2f14738
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/field/modules/text/text.test
@@ -0,0 +1,517 @@
+<?php
+
+/**
+ * @file
+ * Tests for text.module.
+ */
+
+class TextFieldTestCase extends DrupalWebTestCase {
+ protected $instance;
+ protected $admin_user;
+ protected $web_user;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Text field',
+ 'description' => "Test the creation of text fields.",
+ 'group' => 'Field types'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('field_test');
+
+ $this->admin_user = $this->drupalCreateUser(array('administer filters'));
+ $this->web_user = $this->drupalCreateUser(array('access field_test content', 'administer field_test content'));
+ $this->drupalLogin($this->web_user);
+ }
+
+ // Test fields.
+
+ /**
+ * Test text field validation.
+ */
+ function testTextFieldValidation() {
+ // Create a field with settings to validate.
+ $max_length = 3;
+ $this->field = array(
+ 'field_name' => drupal_strtolower($this->randomName()),
+ 'type' => 'text',
+ 'settings' => array(
+ 'max_length' => $max_length,
+ )
+ );
+ field_create_field($this->field);
+ $this->instance = array(
+ 'field_name' => $this->field['field_name'],
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'widget' => array(
+ 'type' => 'text_textfield',
+ ),
+ 'display' => array(
+ 'default' => array(
+ 'type' => 'text_default',
+ ),
+ ),
+ );
+ field_create_instance($this->instance);
+ // Test valid and invalid values with field_attach_validate().
+ $entity = field_test_create_stub_entity();
+ $langcode = LANGUAGE_NONE;
+ for ($i = 0; $i <= $max_length + 2; $i++) {
+ $entity->{$this->field['field_name']}[$langcode][0]['value'] = str_repeat('x', $i);
+ try {
+ field_attach_validate('test_entity', $entity);
+ $this->assertTrue($i <= $max_length, "Length $i does not cause validation error when max_length is $max_length");
+ }
+ catch (FieldValidationException $e) {
+ $this->assertTrue($i > $max_length, "Length $i causes validation error when max_length is $max_length");
+ }
+ }
+ }
+
+ /**
+ * Test widgets.
+ */
+ function testTextfieldWidgets() {
+ $this->_testTextfieldWidgets('text', 'text_textfield');
+ $this->_testTextfieldWidgets('text_long', 'text_textarea');
+ }
+
+ /**
+ * Helper function for testTextfieldWidgets().
+ */
+ function _testTextfieldWidgets($field_type, $widget_type) {
+ // Setup a field and instance
+ $entity_type = 'test_entity';
+ $this->field_name = drupal_strtolower($this->randomName());
+ $this->field = array('field_name' => $this->field_name, 'type' => $field_type);
+ field_create_field($this->field);
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'label' => $this->randomName() . '_label',
+ 'settings' => array(
+ 'text_processing' => TRUE,
+ ),
+ 'widget' => array(
+ 'type' => $widget_type,
+ ),
+ 'display' => array(
+ 'full' => array(
+ 'type' => 'text_default',
+ ),
+ ),
+ );
+ field_create_instance($this->instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Display creation form.
+ $this->drupalGet('test-entity/add/test-bundle');
+ $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget is displayed');
+ $this->assertNoFieldByName("{$this->field_name}[$langcode][0][format]", '1', 'Format selector is not displayed');
+
+ // Submit with some value.
+ $value = $this->randomName();
+ $edit = array(
+ "{$this->field_name}[$langcode][0][value]" => $value,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
+ $id = $match[1];
+ $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
+
+ // Display the entity.
+ $entity = field_test_entity_test_load($id);
+ $entity->content = field_attach_view($entity_type, $entity, 'full');
+ $this->content = drupal_render($entity->content);
+ $this->assertText($value, 'Filtered tags are not displayed');
+ }
+
+ /**
+ * Test widgets + 'formatted_text' setting.
+ */
+ function testTextfieldWidgetsFormatted() {
+ $this->_testTextfieldWidgetsFormatted('text', 'text_textfield');
+ $this->_testTextfieldWidgetsFormatted('text_long', 'text_textarea');
+ }
+
+ /**
+ * Helper function for testTextfieldWidgetsFormatted().
+ */
+ function _testTextfieldWidgetsFormatted($field_type, $widget_type) {
+ // Setup a field and instance
+ $entity_type = 'test_entity';
+ $this->field_name = drupal_strtolower($this->randomName());
+ $this->field = array('field_name' => $this->field_name, 'type' => $field_type);
+ field_create_field($this->field);
+ $this->instance = array(
+ 'field_name' => $this->field_name,
+ 'entity_type' => 'test_entity',
+ 'bundle' => 'test_bundle',
+ 'label' => $this->randomName() . '_label',
+ 'settings' => array(
+ 'text_processing' => TRUE,
+ ),
+ 'widget' => array(
+ 'type' => $widget_type,
+ ),
+ 'display' => array(
+ 'full' => array(
+ 'type' => 'text_default',
+ ),
+ ),
+ );
+ field_create_instance($this->instance);
+ $langcode = LANGUAGE_NONE;
+
+ // Disable all text formats besides the plain text fallback format.
+ $this->drupalLogin($this->admin_user);
+ foreach (filter_formats() as $format) {
+ if ($format->format != filter_fallback_format()) {
+ $this->drupalPost('admin/config/content/formats/' . $format->format . '/disable', array(), t('Disable'));
+ }
+ }
+ $this->drupalLogin($this->web_user);
+
+ // Display the creation form. Since the user only has access to one format,
+ // no format selector will be displayed.
+ $this->drupalGet('test-entity/add/test-bundle');
+ $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget is displayed');
+ $this->assertNoFieldByName("{$this->field_name}[$langcode][0][format]", '', 'Format selector is not displayed');
+
+ // Submit with data that should be filtered.
+ $value = '<em>' . $this->randomName() . '</em>';
+ $edit = array(
+ "{$this->field_name}[$langcode][0][value]" => $value,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
+ $id = $match[1];
+ $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
+
+ // Display the entity.
+ $entity = field_test_entity_test_load($id);
+ $entity->content = field_attach_view($entity_type, $entity, 'full');
+ $this->content = drupal_render($entity->content);
+ $this->assertNoRaw($value, 'HTML tags are not displayed.');
+ $this->assertRaw(check_plain($value), 'Escaped HTML is displayed correctly.');
+
+ // Create a new text format that does not escape HTML, and grant the user
+ // access to it.
+ $this->drupalLogin($this->admin_user);
+ $edit = array(
+ 'format' => drupal_strtolower($this->randomName()),
+ 'name' => $this->randomName(),
+ );
+ $this->drupalPost('admin/config/content/formats/add', $edit, t('Save configuration'));
+ filter_formats_reset();
+ $this->checkPermissions(array(), TRUE);
+ $format = filter_format_load($edit['format']);
+ $format_id = $format->format;
+ $permission = filter_permission_name($format);
+ $rid = max(array_keys($this->web_user->roles));
+ user_role_grant_permissions($rid, array($permission));
+ $this->drupalLogin($this->web_user);
+
+ // Display edition form.
+ // We should now have a 'text format' selector.
+ $this->drupalGet('test-entity/manage/' . $id . '/edit');
+ $this->assertFieldByName("{$this->field_name}[$langcode][0][value]", NULL, 'Widget is displayed');
+ $this->assertFieldByName("{$this->field_name}[$langcode][0][format]", NULL, 'Format selector is displayed');
+
+ // Edit and change the text format to the new one that was created.
+ $edit = array(
+ "{$this->field_name}[$langcode][0][format]" => $format_id,
+ );
+ $this->drupalPost(NULL, $edit, t('Save'));
+ $this->assertRaw(t('test_entity @id has been updated.', array('@id' => $id)), 'Entity was updated');
+
+ // Display the entity.
+ $entity = field_test_entity_test_load($id);
+ $entity->content = field_attach_view($entity_type, $entity, 'full');
+ $this->content = drupal_render($entity->content);
+ $this->assertRaw($value, 'Value is displayed unfiltered');
+ }
+}
+
+class TextSummaryTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Text summary',
+ 'description' => 'Test text_summary() with different strings and lengths.',
+ 'group' => 'Field types',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+ $this->article_creator = $this->drupalCreateUser(array('create article content', 'edit own article content'));
+ }
+
+ /**
+ * Tests an edge case where the first sentence is a question and
+ * subsequent sentences are not. This edge case is documented at
+ * http://drupal.org/node/180425.
+ */
+ function testFirstSentenceQuestion() {
+ $text = 'A question? A sentence. Another sentence.';
+ $expected = 'A question? A sentence.';
+ $this->callTextSummary($text, $expected, NULL, 30);
+ }
+
+ /**
+ * Test summary with long example.
+ */
+ function testLongSentence() {
+ $text = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' . // 125
+ 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ' . // 108
+ 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ' . // 103
+ 'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'; // 110
+ $expected = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' .
+ 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ' .
+ 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.';
+ // First three sentences add up to: 336, so add one for space and then 3 to get half-way into next word.
+ $this->callTextSummary($text, $expected, NULL, 340);
+ }
+
+ /**
+ * Test various summary length edge cases.
+ */
+ function testLength() {
+ // This string tests a number of edge cases.
+ $text = "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>";
+
+ // The summaries we expect text_summary() to return when $size is the index
+ // of each array item.
+ // Using no text format:
+ $expected = array(
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ "<",
+ "<p",
+ "<p>",
+ "<p>\n",
+ "<p>\nH",
+ "<p>\nHi",
+ "<p>\nHi\n",
+ "<p>\nHi\n<",
+ "<p>\nHi\n</",
+ "<p>\nHi\n</p",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ );
+
+ // And using a text format WITH the line-break and htmlcorrector filters.
+ $expected_lb = array(
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ "",
+ "<p></p>",
+ "<p></p>",
+ "<p></p>",
+ "<p></p>",
+ "<p></p>",
+ "<p>\nHi</p>",
+ "<p>\nHi</p>",
+ "<p>\nHi</p>",
+ "<p>\nHi</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>",
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ "<p>\nHi\n</p>\n<p>\nfolks\n<br />\n!\n</p>",
+ );
+
+ // Test text_summary() for different sizes.
+ for ($i = 0; $i <= 37; $i++) {
+ $this->callTextSummary($text, $expected[$i], NULL, $i);
+ $this->callTextSummary($text, $expected_lb[$i], 'plain_text', $i);
+ $this->callTextSummary($text, $expected_lb[$i], 'filtered_html', $i);
+ }
+ }
+
+ /**
+ * Calls text_summary() and asserts that the expected teaser is returned.
+ */
+ function callTextSummary($text, $expected, $format = NULL, $size = NULL) {
+ $summary = text_summary($text, $format, $size);
+ $this->assertIdentical($summary, $expected, format_string('Generated summary "@summary" matches expected "@expected".', array('@summary' => $summary, '@expected' => $expected)));
+ }
+
+ /**
+ * Test sending only summary.
+ */
+ function testOnlyTextSummary() {
+ // Login as article creator.
+ $this->drupalLogin($this->article_creator);
+ // Create article with summary but empty body.
+ $summary = $this->randomName();
+ $edit = array(
+ "title" => $this->randomName(),
+ "body[und][0][summary]" => $summary,
+ );
+ $this->drupalPost('node/add/article', $edit, t('Save'));
+ $node = $this->drupalGetNodeByTitle($edit['title']);
+
+ $this->assertIdentical($node->body['und'][0]['summary'], $summary, 'Article with with summary and no body has been submitted.');
+ }
+}
+
+class TextTranslationTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Text translation',
+ 'description' => 'Check if the text field is correctly prepared for translation.',
+ 'group' => 'Field types',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('locale', 'translation');
+
+ $full_html_format = filter_format_load('full_html');
+ $this->format = $full_html_format->format;
+ $this->admin = $this->drupalCreateUser(array(
+ 'administer languages',
+ 'administer content types',
+ 'access administration pages',
+ 'bypass node access',
+ filter_permission_name($full_html_format),
+ ));
+ $this->translator = $this->drupalCreateUser(array('create article content', 'edit own article content', 'translate content'));
+
+ // Enable an additional language.
+ $this->drupalLogin($this->admin);
+ $edit = array('langcode' => 'fr');
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+
+ // Set "Article" content type to use multilingual support with translation.
+ $edit = array('language_content_type' => 2);
+ $this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
+ $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Article')), 'Article content type has been updated.');
+ }
+
+ /**
+ * Test that a plaintext textfield widget is correctly populated.
+ */
+ function testTextField() {
+ // Disable text processing for body.
+ $edit = array('instance[settings][text_processing]' => 0);
+ $this->drupalPost('admin/structure/types/manage/article/fields/body', $edit, t('Save settings'));
+
+ // Login as translator.
+ $this->drupalLogin($this->translator);
+
+ // Create content.
+ $langcode = LANGUAGE_NONE;
+ $body = $this->randomName();
+ $edit = array(
+ "title" => $this->randomName(),
+ "language" => 'en',
+ "body[$langcode][0][value]" => $body,
+ );
+
+ // Translate the article in french.
+ $this->drupalPost('node/add/article', $edit, t('Save'));
+ $node = $this->drupalGetNodeByTitle($edit['title']);
+ $this->drupalGet("node/$node->nid/translate");
+ $this->clickLink(t('add translation'));
+ $this->assertFieldByXPath("//textarea[@name='body[$langcode][0][value]']", $body, 'The textfield widget is populated.');
+ }
+
+ /**
+ * Check that user that does not have access the field format cannot see the
+ * source value when creating a translation.
+ */
+ function testTextFieldFormatted() {
+ // Make node body multiple.
+ $edit = array('field[cardinality]' => -1);
+ $this->drupalPost('admin/structure/types/manage/article/fields/body', $edit, t('Save settings'));
+ $this->drupalGet('node/add/article');
+ $this->assertFieldByXPath("//input[@name='body_add_more']", t('Add another item'), 'Body field cardinality set to multiple.');
+
+ $body = array(
+ $this->randomName(),
+ $this->randomName(),
+ );
+
+ // Create an article with the first body input format set to "Full HTML".
+ $title = $this->randomName();
+ $edit = array(
+ 'title' => $title,
+ 'language' => 'en',
+ );
+ $this->drupalPost('node/add/article', $edit, t('Save'));
+
+ // Populate the body field: the first item gets the "Full HTML" input
+ // format, the second one "Filtered HTML".
+ $formats = array('full_html', 'filtered_html');
+ $langcode = LANGUAGE_NONE;
+ foreach ($body as $delta => $value) {
+ $edit = array(
+ "body[$langcode][$delta][value]" => $value,
+ "body[$langcode][$delta][format]" => array_shift($formats),
+ );
+ $this->drupalPost('node/1/edit', $edit, t('Save'));
+ $this->assertText($body[$delta], format_string('The body field with delta @delta has been saved.', array('@delta' => $delta)));
+ }
+
+ // Login as translator.
+ $this->drupalLogin($this->translator);
+
+ // Translate the article in french.
+ $node = $this->drupalGetNodeByTitle($title);
+ $this->drupalGet("node/$node->nid/translate");
+ $this->clickLink(t('add translation'));
+ $this->assertNoText($body[0], format_string('The body field with delta @delta is hidden.', array('@delta' => 0)));
+ $this->assertText($body[1], format_string('The body field with delta @delta is shown.', array('@delta' => 1)));
+ }
+}