You are here

properties.module in Dynamic properties 7

This module provides a dynamic property field that can contain an unlimited number of attribute value pairs.

File

properties.module
View source
<?php

/**
 * @file
 * This module provides a dynamic property field that can contain an unlimited number of attribute value pairs.
 */

/**
 * Implements hook_permission().
 */
function properties_permission() {
  return array(
    'administer properties attributes' => array(
      'title' => t('Administer attributes'),
      'description' => t('Allows to create new, edit existing and delete attributes.'),
    ),
    'administer properties categories' => array(
      'title' => t('Administer categories'),
      'description' => t('Allows to create new, edit existing and delete categories.'),
    ),
    'add properties categories' => array(
      'title' => t('Add categories to content'),
      'description' => t('Allows to add existing categories to content.'),
    ),
    'add properties attributes' => array(
      'title' => t('Add attributes to content'),
      'description' => t('Allows to add existing attributes to content or change existing ones.'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function properties_menu() {
  $items['admin/config/content/properties'] = array(
    'title' => 'Properties',
    'description' => 'Administer categories and attributes',
    'access callback' => 'properties_admin_access',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'properties_admin_attributes_list',
    ),
    'file' => 'properties.admin.inc',
  );
  $items['admin/config/content/properties/attributes'] = array(
    'title' => 'Attributes',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'properties_admin_attributes_list',
    ),
    'file' => 'properties.admin.inc',
    'access callback' => 'properties_admin_access',
    'access arguments' => array(
      'attributes',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/config/content/properties/attributes/list'] = array(
    'title' => 'List',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -20,
  );
  $items['admin/config/content/properties/attributes/add'] = array(
    'title' => 'Add attribute',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'properties_admin_attributes_form',
    ),
    'file' => 'properties.admin.inc',
    'access callback' => 'properties_admin_access',
    'access arguments' => array(
      'attributes',
    ),
    'type' => MENU_LOCAL_ACTION,
  );
  $items['admin/config/content/properties/attributes/edit/%properties_attribute'] = array(
    'title' => 'Edit attribute',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'properties_admin_attributes_form',
      6,
    ),
    'file' => 'properties.admin.inc',
    'access callback' => 'properties_admin_access',
    'access arguments' => array(
      'attributes',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => -10,
  );
  $items['admin/config/content/properties/attributes/delete/%properties_attribute'] = array(
    'title' => 'Delete attribute',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'properties_admin_attributes_delete',
      6,
    ),
    'file' => 'properties.admin.inc',
    'access callback' => 'properties_admin_access',
    'access arguments' => array(
      'attributes',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/config/content/properties/categories'] = array(
    'title' => 'Categories',
    'page callback' => 'properties_admin_categories_list',
    'file' => 'properties.admin.inc',
    'access callback' => 'properties_admin_access',
    'access arguments' => array(
      'categories',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 10,
  );
  $items['admin/config/content/properties/categories/list'] = array(
    'title' => 'List',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -20,
  );
  $items['admin/config/content/properties/categories/add'] = array(
    'title' => 'Add category',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'properties_admin_categories_form',
    ),
    'file' => 'properties.admin.inc',
    'access callback' => 'properties_admin_access',
    'access arguments' => array(
      'attributes',
    ),
    'type' => MENU_LOCAL_ACTION,
  );
  $items['admin/config/content/properties/categories/edit/%properties_category'] = array(
    'title' => 'Edit category',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'properties_admin_categories_form',
      6,
    ),
    'file' => 'properties.admin.inc',
    'access callback' => 'properties_admin_access',
    'access arguments' => array(
      'categories',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => -10,
  );
  $items['admin/config/content/properties/categories/delete/%properties_category'] = array(
    'title' => 'Delete category',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'properties_admin_categories_delete',
      6,
    ),
    'file' => 'properties.admin.inc',
    'access callback' => 'properties_admin_access',
    'access arguments' => array(
      'categories',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  $items['properties_autocomplete/%'] = array(
    'title' => 'Properties autocomplete',
    'type' => MENU_CALLBACK,
    'page callback' => 'properties_autocomplete_js',
    'page arguments' => array(
      1,
      2,
    ),
    'access callback' => TRUE,
  );
  return $items;
}

/**
 * Page callback for autocomplete suggestions.
 */
function properties_autocomplete_js($type, $string = NULL) {
  $function = 'properties_' . $type . '_load_paging';
  if (!function_exists($function)) {
    return;
  }
  $suggestions = $function(10, array(
    'search' => $string,
  ));
  $json_suggestions = array();
  foreach ($suggestions as $suggestion) {
    $json_suggestions[$suggestion->name] = t('@name (@label)', array(
      '@name' => $suggestion->name,
      '@label' => $suggestion->label,
    ));
  }
  drupal_json_output((object) $json_suggestions);
}

/**
 * Implements hook_theme().
 */
function properties_theme() {
  return array(
    'properties_admin_categories_attributes_form' => array(
      'render element' => 'element',
      'file' => 'properties.admin.inc',
    ),
    'properties_properties_form' => array(
      'render element' => 'element',
      'template' => 'properties-properties-form',
    ),
  );
}

/**
 * Check access to an administration page.
 *
 * @param $type
 *   Either categories or attributes.
 *
 * @return
 *   TRUE if access is allowed.
 */
function properties_admin_access($type = NULL) {
  if (empty($type)) {
    return user_access('administer properties attributes') || user_access('administer properties categories');
  }
  else {
    return user_access('administer properties ' . $type);
  }
}

/**
 * Implements hook_field_info().
 */
function properties_field_info() {
  return array(
    'properties' => array(
      'label' => t('Dynamic Properties'),
      'description' => t('This field stores a dynamic amount of properties in the database.'),
      'settings' => array(),
      'instance_settings' => array(),
      'default_widget' => 'properties_table',
      'default_formatter' => 'properties_formatter_list',
    ),
  );
}

/**
 * Implements hook_field_schema().
 */
function properties_field_schema($field) {
  $columns = array(
    'attribute' => array(
      'type' => 'varchar',
      'length' => 255,
      'not null' => TRUE,
    ),
    'value' => array(
      'type' => 'varchar',
      'length' => 255,
      'not null' => TRUE,
    ),
    'category' => array(
      'type' => 'varchar',
      'length' => 255,
      'not null' => TRUE,
    ),
  );
  return array(
    'columns' => $columns,
    'indexes' => array(
      'attribute' => array(
        'attribute',
      ),
      'category' => array(
        'category',
      ),
    ),
  );
}

/**
 * Implements hook_field_presave().
 *
 * Converts single item array in key 0 into multiple items, which are stored
 * separately.
 */
function properties_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
  if (!empty($items[0]) && !isset($items[0]['attribute'])) {
    $categories = $items[0];
    $items = array();

    // First step, move items into correct category. Necessary to be able to sort
    // them withing their category.
    foreach ($categories as $cname => $category) {
      if (!is_array($category) || !is_array($category['properties'])) {
        continue;
      }
      foreach ($category['properties'] as $pname => $property) {
        if ($property['category'] != $cname) {

          // Add to new category.
          $categories[$property['category']]['properties'][$property['attribute']] = $property;

          // Remove from current category.
          unset($categories[$cname]['properties'][$property['attribute']]);
        }
      }
    }

    // Next step, order categories and attributes.
    uasort($categories, '_field_sort_items_helper');
    foreach ($categories as $category) {
      if (empty($category['properties']) || !is_array($category['properties'])) {
        continue;
      }
      $properties = $category['properties'];
      uasort($properties, '_field_sort_items_helper');
      foreach ($properties as $property) {

        // If the attribute name was deleted, attribute is empty. Ignore these.
        if (empty($property['attribute'])) {
          continue;
        }

        // Add them in the sorted order to the items array.
        $items[] = array(
          'attribute' => $property['attribute'],
          'value' => $property['value'],
          'category' => $property['category'],
        );
      }
    }
  }
}

/**
 * Implements hook_field_validate().
 */
function properties_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
}

/**
 * Implements hook_field_is_empty().
 */
function properties_field_is_empty($item, $field) {
  return !is_array($item);
}

/**
 * Implements hook_field_formatter_info().
 */
function properties_field_formatter_info() {
  return array(
    'properties_formatter_list' => array(
      'label' => t('Definition list'),
      'field types' => array(
        'properties',
      ),
    ),
    'properties_formatter_table' => array(
      'label' => t('Table'),
      'field types' => array(
        'properties',
      ),
    ),
  );
}

/**
 * Implements hook_field_widget_info().
 */
function properties_field_widget_info() {
  return array(
    'properties_table' => array(
      'label' => t('Properties table'),
      'field types' => array(
        'properties',
      ),
      'settings' => array(),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_CUSTOM,
        'default value' => FIELD_BEHAVIOR_NONE,
      ),
    ),
  );
}

/**
 * Returns an empty attribute object.
 */
function properties_empty_attribute() {
  return (object) array(
    'label' => '',
    'name' => '',
  );
}

/**
 * Implements hook_field_widget_form().
 */
function properties_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $elements = array();
  $field_name = $field['field_name'];
  $values = isset($form_state['values'][$field_name][$langcode]['listing']) ? $form_state['values'][$field_name][$langcode]['listing'] : array();
  drupal_add_css(drupal_get_path('module', 'properties') . '/properties.css');
  drupal_add_js(drupal_get_path('module', 'properties') . '/properties.js');
  $categories =& $form_state[$field_name]['categories'];
  if (!isset($form_state[$field_name]['categories'])) {
    $categories = array();

    // Set up initial content.
    foreach ($items as $item) {

      // First, load the category if necessary.
      if (empty($categories[$item['category']])) {
        $category = properties_category_load($item['category']);
        if (!empty($category)) {
          $categories[$category->name] = array(
            '_weight' => count($form_state[$field_name]['categories']),
            'category' => $category,
            'properties' => array(),
          );
        }
        else {

          // If the category was deleted, ignore it and everything within it.
          continue;
        }
      }
      $attribute = properties_attribute_load($item['attribute']);
      if (!empty($attribute)) {
        $form_state[$field_name]['categories'][$item['category']]['properties'][$attribute->name] = array(
          'attribute' => $attribute->name,
          'category' => $item['category'],
          'value' => $item['value'],
          '_weight' => count($form_state[$field_name]['categories'][$item['category']]['properties']),
        );
      }
    }
  }
  $category_list = array();
  foreach ($categories as $category_name => $item) {
    $category_list[$item['category']->name] = $item['category']->label;

    // Update weight from values.
    if (isset($values[$category_name])) {
      $item['_weight'] = $values[$category_name]['_weight'];
    }

    // Move attributes to new category.
    foreach ($item['properties'] as $delta => $property) {
      if (isset($values[$category_name]['properties'][$property['attribute']])) {
        $property['_weight'] = $values[$category_name]['properties'][$property['attribute']]['_weight'];
        $property['category'] = $values[$category_name]['properties'][$property['attribute']]['category'];
      }
      if ($property['category'] != $category_name) {
        $categories[$property['category']]['properties'][$property['attribute']] = $property;
        unset($categories[$category_name]['properties'][$property['attribute']]);
      }
    }
    usort($item['properties'], '_field_sort_items_helper');
  }
  uasort($categories, '_field_sort_items_helper');
  $id = 'properties-' . drupal_html_id($field_name) . '-wrapper';
  $elements['listing'] = array();
  $tabindex = 1;
  foreach ($categories as $category_name => $category) {
    $elements['listing'][$category_name]['#label'] = $category['category']->label;

    // Only used to trick tabledrag.js.
    $elements['listing'][$category_name]['category'] = array(
      '#type' => 'hidden',
      '#value' => '',
    );
    $elements['listing'][$category_name]['name'] = array(
      '#type' => 'hidden',
      '#value' => $category['category']->name,
    );
    $elements['listing'][$category_name]['_weight'] = array(
      '#type' => 'weight',
      '#title' => t('Weight for category @category', array(
        '@category' => $category['category']->label,
      )),
      '#title_display' => 'invisible',
      '#default_value' => $category['_weight'],
      '#access' => user_access('add properties categories'),
      '#weight' => 100,
    );
    $elements['listing'][$category_name]['delete'] = array(
      '#type' => 'submit',
      '#value' => t('Delete'),
      '#submit' => array(
        'properties_widget_remove_category',
      ),
      '#limit_validation_errors' => array(
        array(
          $field_name,
          $langcode,
        ),
      ),
      '#access' => user_access('add properties categories'),
      '#name' => $category_name,
      '#ajax' => array(
        'callback' => 'properties_widget_update_js',
        'wrapper' => $id,
        'effect' => 'fade',
      ),
    );
    foreach ($category['properties'] as $delta => $property) {
      $element = array();
      $element['category'] = array(
        '#type' => user_access('add properties attributes') ? 'select' : 'hidden',
        '#default_value' => $property['category'],
        '#options' => $category_list,
      );
      $element['attribute'] = array(
        '#type' => 'textfield',
        '#default_value' => $property['attribute'],
        '#autocomplete_path' => 'properties_autocomplete/attribute',
        '#maxlength' => 255,
        '#size' => 32,
        '#access' => user_access('add properties attributes'),
        '#ajax' => array(
          'callback' => 'properties_admin_update_label_js',
          'wrapper' => 'attributes-wrapper',
          'effect' => 'fade',
        ),
      );
      $element['label'] = array(
        '#markup' => !empty($property['attribute']) ? check_plain(properties_attribute_load($property['attribute'])->label) : '',
      );
      $element['value'] = array(
        '#type' => 'textfield',
        '#default_value' => $property['value'],
        '#maxlength' => 255,
        '#size' => 32,
        '#attributes' => array(
          'tabindex' => $tabindex,
          'class' => array(
            'properties-value',
          ),
        ),
      );
      $element['_weight'] = array(
        '#type' => 'weight',
        '#title' => t('Weight for property @property', array(
          '@property' => $element['label']['#markup'],
        )),
        '#title_display' => 'invisible',
        '#default_value' => $property['_weight'],
        '#weight' => 100,
        '#access' => user_access('add properties attributes'),
      );
      $element['delete'] = array(
        '#type' => 'submit',
        '#value' => t('Delete'),
        '#submit' => array(
          'properties_widget_remove_attribute',
        ),
        '#access' => user_access('add properties attributes'),
        '#name' => $property['attribute'],
        '#limit_validation_errors' => array(
          array(
            $field_name,
            $langcode,
          ),
        ),
        '#ajax' => array(
          'callback' => 'properties_widget_update_js',
          'wrapper' => $id,
          'effect' => 'fade',
        ),
      );
      $elements['listing'][$category_name]['properties'][$property['attribute']] = $element;
      $tabindex++;
    }
  }
  $elements += array(
    '#properties_table_id' => $id,
    '#type' => 'fieldset',
    '#theme' => 'properties_properties_form',
    '#title' => t('Properties'),
    '#description' => t('Add properties to this content. Start by adding categories to it.'),
    '#prefix' => '<div id="' . $id . '">',
    '#suffix' => '</div>',
    '#max_delta' => $delta,
    // Container for action form elements, might be empty.
    'actions' => array(),
  );
  if (user_access('add properties categories')) {
    $elements['actions']['new_category'] = array(
      '#type' => 'textfield',
      '#default_value' => '',
      '#autocomplete_path' => 'properties_autocomplete/category',
      '#maxlength' => 255,
      '#size' => 32,
    );
    $elements['actions']['add_category'] = array(
      '#name' => $field_name . $langcode,
      '#type' => 'submit',
      '#value' => t('Add category'),
      '#limit_validation_errors' => array(
        array(
          $field_name,
          $langcode,
        ),
      ),
      '#submit' => array(
        'properties_widget_add_category_submit',
      ),
      '#ajax' => array(
        'callback' => 'properties_widget_update_js',
        'wrapper' => $id,
        'effect' => 'fade',
      ),
    );
  }
  if (!empty($category_list) && user_access('add properties attributes')) {
    $elements['actions']['attribute_category'] = array(
      '#type' => 'select',
      '#options' => $category_list,
    );
    $elements['actions']['new_attribute'] = array(
      '#type' => 'textfield',
      '#default_value' => '',
      '#autocomplete_path' => 'properties_autocomplete/attribute',
      '#maxlength' => 255,
      '#size' => 32,
    );
    $elements['actions']['add_attribute'] = array(
      '#name' => $field_name . $langcode,
      '#type' => 'submit',
      '#value' => t('Add attribute'),
      '#limit_validation_errors' => array(
        array(
          $field_name,
          $langcode,
        ),
      ),
      '#submit' => array(
        'properties_widget_add_attribute_submit',
      ),
      '#ajax' => array(
        'callback' => 'properties_widget_update_js',
        'wrapper' => $id,
        'effect' => 'fade',
      ),
    );
  }
  return $elements;
}

/**
 * Submit callback to add a category.
 */
function properties_widget_add_category_submit($form, &$form_state) {
  $field_name = $form_state['triggering_element']['#parents'][0];
  $langcode = $form[$field_name]['#language'];
  $values = $form_state['values'][$field_name][$langcode]['actions'];
  if (!isset($form_state[$field_name]['categories'][$values['new_category']])) {
    if (!properties_widget_add_category($values['new_category'], $form_state, $field_name, $langcode)) {
      drupal_set_message(t('Category empty or invalid.'), 'error');
    }
  }
  $form_state['rebuild'] = TRUE;
}

/**
 * Add a category to the widget form.
 *
 * @param $category_name
 *   Machine name of the category or category to add.
 * @param $form_state
 *   Form state.
 * @param $field_name
 *   Name of the field.
 * @param $langcode
 *   Language code.
 */
function properties_widget_add_category($category, &$form_state, $field_name, $langcode) {
  if (is_string($category)) {
    $category = properties_category_load($category);
  }
  if (!empty($category)) {
    $form_state[$field_name]['categories'][$category->name] = array(
      '_weight' => count($form_state[$field_name]['categories']),
      'category' => $category,
      'properties' => array(),
    );
    foreach ($category->attributes as $property) {
      $form_state[$field_name]['categories'][$category->name]['properties'][$property->name] = array(
        'attribute' => $property->name,
        'category' => $category->name,
        'value' => '',
        '_weight' => count($form_state[$field_name]['categories'][$category->name]['properties']),
      );
    }

    // Clear the new category form field.
    unset($form_state['input'][$field_name][$langcode]['new_category']);
    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Submit callback to add an attribute.
 */
function properties_widget_add_attribute_submit($form, &$form_state) {
  $field_name = $form_state['triggering_element']['#parents'][0];
  $langcode = $form[$field_name]['#language'];
  $values = $form_state['values'][$field_name][$langcode]['actions'];
  $attribute = properties_attribute_load($values['new_attribute']);
  if (!empty($attribute)) {
    $form_state[$field_name]['categories'][$values['attribute_category']]['properties'][$attribute->name] = array(
      'attribute' => $attribute->name,
      'category' => $values['attribute_category'],
      'value' => '',
      '_weight' => count($form_state[$field_name]['categories'][$values['attribute_category']]['properties']),
    );

    // Clear the new category form field.
    unset($form_state['input'][$field_name][$langcode]['new_attribute']);
  }
  else {
    drupal_set_message(t('Attribute empty or invalid.'), 'error');
  }
  $form_state['rebuild'] = TRUE;
}

/**
 * Submit callback to remove a single attribute.
 */
function properties_widget_remove_attribute($form, &$form_state) {
  $field_name = $form_state['triggering_element']['#parents'][0];
  $langcode = $form[$field_name]['#language'];
  $values = $form_state['values'][$field_name][$langcode];

  // @todo: Verify that this always works.
  $category_name = $form_state['triggering_element']['#parents'][3];
  $attribute_name = $form_state['triggering_element']['#parents'][5];
  unset($form_state[$field_name]['categories'][$category_name]['properties'][$attribute_name]);
  $form_state['rebuild'] = TRUE;
}

/**
 * Submit callback to remove a category.
 */
function properties_widget_remove_category($form, &$form_state) {
  $field_name = $form_state['triggering_element']['#parents'][0];
  $langcode = $form[$field_name]['#language'];
  $values = $form_state['values'][$field_name][$langcode];

  // @todo: Verify that this always works.
  $category_name = $form_state['triggering_element']['#parents'][3];
  unset($form_state[$field_name]['categories'][$category_name]);
  $form_state['rebuild'] = TRUE;
}

/**
 * AJAX callback that returns the properties widget.
 */
function properties_widget_update_js($form, $form_state) {
  $field_name = $form_state['triggering_element']['#parents'][0];
  return $form[$field_name];
}

/**
 * Implements hook_field_formatter_view().
 */
function properties_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  $items_by_category = array();
  foreach ($items as $item) {
    if (!isset($items_by_category[$item['category']])) {
      $items_by_category[$item['category']] = array();
    }
    $items_by_category[$item['category']][] = $item;
  }
  $categories = array();
  if (!empty($items_by_category)) {
    $categories = properties_category_load_multiple(array_keys($items_by_category));
  }
  switch ($display['type']) {
    case 'properties_formatter_list':
      $element[0]['#prefix'] = '<div class="profile">';
      $element[0]['#suffix'] = '</div>';
      foreach ($items_by_category as $category => $items_in_category) {
        $element[0][] = array(
          '#type' => 'user_profile_category',
          '#title' => check_plain($categories[$category]->label),
        );
        foreach ($items_in_category as $item) {
          if (isset($categories[$category]->attributes[$item['attribute']])) {
            $label = $categories[$category]->attributes[$item['attribute']]->label;
          }
          elseif (!empty($item['attribute'])) {
            $label = properties_attribute_load($item['attribute'])->label;
          }
          else {
            continue;
          }
          $element[0][] = array(
            '#type' => 'user_profile_item',
            '#title' => $label,
            '#markup' => check_plain($item['value']),
          );
        }
      }
      break;
    case 'properties_formatter_table':
      $rows = array();
      foreach ($items_by_category as $category => $items_in_category) {
        $rows[] = array(
          array(
            'data' => check_plain($categories[$category]->label),
            'colspan' => 2,
            'header' => TRUE,
            'class' => array(
              drupal_html_class($category),
            ),
          ),
        );
        foreach ($items_in_category as $item) {
          if (isset($categories[$category]->attributes[$item['attribute']])) {
            $label = $categories[$category]->attributes[$item['attribute']]->label;
          }
          else {
            $label = properties_attribute_load($item['attribute'])->label;
          }
          $rows[] = array(
            array(
              'data' => check_plain($label),
              'class' => drupal_html_class($item['attribute']) . '-label',
            ),
            array(
              'data' => check_plain($item['value']),
              'class' => drupal_html_class($item['attribute']) . '-data',
            ),
          );
        }
      }
      $element[0]['#markup'] = theme('table', array(
        'rows' => $rows,
      ));
      break;
  }
  return $element;
}

/**
 * Calls an API function.
 *
 * Additional arguments can be passed to the function, these will be forwarded
 * to the API implementation.
 *
 * @param $type
 *   API type, e.g. attribute or category.
 * @param $action
 *   Name of the API function, eg load, save, delete.
 */
function _properties_get_call($type, $action) {
  return variable_get('properties_api', 'properties_sql') . '_properties_' . $type . '_' . $action;
}

/**
 * Save an attribute object.
 *
 * @param $attribute
 *   Attribute object.
 */
function properties_attribute_save($attribute) {
  $function_name = _properties_get_call('attribute', 'save');
  return $function_name($attribute);
}

/**
 * Delete an attribute.
 *
 * @param $attribute
 *   Attribute object.
 */
function properties_attribute_delete($attribute) {
  $function_name = _properties_get_call('attribute', 'delete');
  return $function_name($attribute);
}

/**
 * Load an attribute based on the name.
 *
 * @param $name
 *   Machine readable name of the attribute.
 */
function properties_attribute_load($name) {
  if (empty($name)) {
    return FALSE;
  }
  $function_name = _properties_get_call('attribute', 'load');
  return $function_name($name);
}

/**
 * Load multiple attributes based on their names.
 *
 * @param $names
 *   Array of machine readable name of the attributes.
 */
function properties_attribute_load_multiple($names = array()) {
  $function_name = _properties_get_call('attribute', 'load_multiple');
  return $function_name($names);
}

/**
 * Load a paged amount of attributes.
 *
 * @param $per_page
 *   Number of attributes per page.
 */
function properties_attribute_load_paging($per_page, array $options = array()) {
  $function_name = _properties_get_call('attribute', 'load_paging');
  return $function_name($per_page, $options);
}

/**
 * Save an category object.
 *
 * @param $category
 *   Category object.
 */
function properties_category_save($category) {
  foreach ($category->attributes as $delta => $attribute) {

    // New attribute, save it first.
    if (!empty($attribute->new)) {
      properties_attribute_save($attribute);
    }

    // Empty attribute, remove from array.
    if (empty($attribute->name)) {
      unset($category->attributes[$delta]);
    }
  }
  $function_name = _properties_get_call('category', 'save');
  return $function_name($category);
}

/**
 * Delete an category.
 *
 * @param $category
 *   Category object.
 */
function properties_category_delete($category) {
  $function_name = _properties_get_call('category', 'delete');
  return $function_name($category);
}

/**
 * Load an category based on the name.
 *
 * @param $name
 *   Machine readable name of the category.
 */
function properties_category_load($name) {
  if (empty($name)) {
    return FALSE;
  }
  $function_name = _properties_get_call('category', 'load');
  return $function_name($name);
}

/**
 * Load multiple categories based on their names.
 *
 * @param $names
 *   Array of machine readable name of the categories.
 */
function properties_category_load_multiple($names = array()) {
  $function_name = _properties_get_call('category', 'load_multiple');
  return $function_name($names);
}

/**
 * Load a paged amount of categories.
 * @param $per_page
 *   Number of categories per page.
 */
function properties_category_load_paging($per_page, array $options = array()) {
  $function_name = _properties_get_call('category', 'load_paging');
  return $function_name($per_page, $options);
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function properties_form_field_ui_field_edit_form_alter(&$form, &$form_state) {
  if ($form['#field']['type'] == 'properties') {

    // Hide the cardinality setting on the field settings for properties fields.
    $form['field']['cardinality']['#default_value'] = FIELD_CARDINALITY_UNLIMITED;
    $form['field']['cardinality']['#access'] = FALSE;
  }
}

/**
 * Extracts properties fields from entity.
 */
function properties_extract_fields($entity_type, $entity) {
  $properties_fields = array();

  // Load fields attached to the entity.
  $fields = field_info_instances($entity_type, field_extract_bundle($entity_type, $entity));

  // Get the display language for the entity.
  $languages = field_language($entity_type, $entity);

  // Loop over fields.
  foreach ($fields as $field_name => $field) {
    $field_info = field_info_field_by_id($field['field_id']);

    // Only handle properties fields.
    if ($field_info['type'] == 'properties') {

      // Load content for the configured language.
      $field_all_languages = $entity->{$field_name};

      // Field is empty.
      if (empty($field_all_languages) || !isset($field_all_languages[$languages[$field_name]])) {
        continue;
      }
      $properties_fields[$field_name] = $field_all_languages[$languages[$field_name]];
    }
  }
  return $properties_fields;
}

/**
 * Implements hook_tmgmt_source_translation_structure().
 *
 * Allows properties' values to be extracted and translated within TMGMT.
 */
function properties_tmgmt_source_translation_structure($field_name, $entity_type, $entity, $field_instance) {
  $field_lang = field_language($entity_type, $entity, $field_name);
  $structure = array();
  if (!empty($entity->{$field_name}[$field_lang])) {
    $structure['#label'] = check_plain($field_instance['label']);
    foreach ($entity->{$field_name}[$field_lang] as $delta => $value) {
      $structure[$delta]['#label'] = t('Delta #@delta', array(
        '@delta' => $delta,
      ));
      $attribute = properties_attribute_load($value['attribute']);

      // Translatable user text of attribute.
      $structure[$delta]['value'] = array(
        '#label' => $attribute->label,
        '#text' => $value['value'],
        '#translate' => TRUE,
      );

      // Machine name for attribute.
      $structure[$delta]['attribute'] = array(
        '#label' => $structure['#label'],
        '#text' => $value['attribute'],
        '#translate' => FALSE,
      );

      // Machine name for category.
      $structure[$delta]['category'] = array(
        '#label' => $structure['#label'],
        '#text' => $value['category'],
        '#translate' => FALSE,
      );
    }
  }
  return $structure;
}

Functions

Namesort descending Description
properties_admin_access Check access to an administration page.
properties_attribute_delete Delete an attribute.
properties_attribute_load Load an attribute based on the name.
properties_attribute_load_multiple Load multiple attributes based on their names.
properties_attribute_load_paging Load a paged amount of attributes.
properties_attribute_save Save an attribute object.
properties_autocomplete_js Page callback for autocomplete suggestions.
properties_category_delete Delete an category.
properties_category_load Load an category based on the name.
properties_category_load_multiple Load multiple categories based on their names.
properties_category_load_paging Load a paged amount of categories.
properties_category_save Save an category object.
properties_empty_attribute Returns an empty attribute object.
properties_extract_fields Extracts properties fields from entity.
properties_field_formatter_info Implements hook_field_formatter_info().
properties_field_formatter_view Implements hook_field_formatter_view().
properties_field_info Implements hook_field_info().
properties_field_is_empty Implements hook_field_is_empty().
properties_field_presave Implements hook_field_presave().
properties_field_schema Implements hook_field_schema().
properties_field_validate Implements hook_field_validate().
properties_field_widget_form Implements hook_field_widget_form().
properties_field_widget_info Implements hook_field_widget_info().
properties_form_field_ui_field_edit_form_alter Implements hook_form_FORM_ID_alter().
properties_menu Implements hook_menu().
properties_permission Implements hook_permission().
properties_theme Implements hook_theme().
properties_tmgmt_source_translation_structure Implements hook_tmgmt_source_translation_structure().
properties_widget_add_attribute_submit Submit callback to add an attribute.
properties_widget_add_category Add a category to the widget form.
properties_widget_add_category_submit Submit callback to add a category.
properties_widget_remove_attribute Submit callback to remove a single attribute.
properties_widget_remove_category Submit callback to remove a category.
properties_widget_update_js AJAX callback that returns the properties widget.
_properties_get_call Calls an API function.