<?php

/**
 * Implements hook_field_info().
 */
function textauthor_field_info() {
  return array(
    'textauthor' => array(
      'label' => t('Text with author'),
      'description' => t('This field stores varchar text in the database with author.'),
      'default_widget' => 'textauthor',
      'default_formatter' => 'textauthor',
      'instance_settings' => array('text_processing' => 0),
      // Support hook_entity_property_info() from contrib "Entity API".
      // 'property_type' => 'field_item_textauthor',
      // 'property_callbacks' => array('textauthor_field_property_info_callback'),
    ),
  );
}

/**
 * Implements hook_field_instance_settings_form().
 */
function textauthor_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)'),
    ),
  );

  return $form;
}

/**
 * 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 textauthor_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') : '';
      }
    }
  }
}

/**
 * Implements hook_field_is_empty().
 */
function textauthor_field_is_empty($item, $field) {
  if (!isset($item['value']) || $item['value'] === '') {
    return !isset($item['author']) || $item['author'] === '';
  }
  return FALSE;
}

/**
 * Implements hook_field_widget_info().
 */
function textauthor_field_widget_info() {
  return array(
    'textauthor' => array(
      'label' => t('Text with author'),
      'field types' => array('textauthor'),
      'settings' => array('size' => 60),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
        'default value' => FIELD_BEHAVIOR_DEFAULT,
      ),
    ),
  );
}

/**
 * Implements hook_field_widget_form().
 */
function textauthor_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  // dsm($element, '$element');
  
  $main_widget = $element + array(
    '#type' => 'textarea',
    '#title' => t('Text'),
    '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
  );
  
  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'] = 'textarea';
  }else{
    $element['value'] = $main_widget;
  }
  
  $element['author'] = array(
    '#type' => 'textfield',
    '#title' => t('!title Author', array('!title'=>$element['#title'])),
    '#default_value' => isset($items[$delta]['author']) ? $items[$delta]['author'] : NULL,
    '#weight' => 1,
  );
  // 
  return $element;
  
}

/**
 * Implements hook_theme().
 */
function textauthor_theme($existing, $type, $theme, $path) {
  return array(
    'textauthor_formatter' => array(
      'variables' => array('element' => NULL),
    ),
  );
  
}

/**
  * Implements hook_field_formatter_info().
 */
function textauthor_field_formatter_info() {
  return array(
    'textauthor_default' => array(
      'label' => t('Default'),
      'field types' => array('textauthor'),
    ),
    'textauthor_plain' => array(
      'label' => t('Plain text'),
      'field types' => array('textauthor'),
    ),

    // 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.
    'textauthor_trimmed' => array(
      'label' => t('Trimmed'),
      'field types' => array('textauthor'),
      'settings' => array('trim_length' => 600),
    ),
  );
}

/**
 * Implements hook_field_formatter_settings_form().
 */
function textauthor_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_view().
 */
function textauthor_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  if ($display['type'] == 'textauthor_default' || $display['type'] == 'textauthor_trimmed') {
    foreach ($items as $delta => $item) {
      $output = _textauthor_sanitize($instance, $langcode, $item, 'value');
      if ($display['type'] == 'textauthor_trimmed') {
        $output = textauthor_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
      }
      $items[$delta]['output_value'] = $output;
    }
  }
  
  $elements = array();
  foreach ($items as $delta => $item) {
    $elements[$delta] = array(
      '#markup' => theme('textauthor_formatter', array('element' => $item, 'field' => $instance)),
    );
  }
  return $elements;
}


/**
 * 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 _textauthor_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 textauthor_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;
}

function theme_textauthor_formatter($vars){
  $output = '';
  $output .= '<article class="'.$vars['element']['format'].'">'.$vars['element']['output_value'].'</article>';
  if($vars['element']['author'] != '')
    $output .= '<address rel="author">'.$vars['element']['author'].'</address>';
  return $output;
}

/**
 * Implements hook_field_prepare_translation().
 */
function textauthor_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 textauthor_filter_format_update($format) {
  field_cache_clear();
}

/**
 * Implements hook_filter_format_disable().
 */
function textauthor_filter_format_disable($format) {
  field_cache_clear();
}

/**
 * Implementation of hook_migrate_api().
 */
function textauthor_migrate_api() {
  $api = array(
    'api' => 2,
  );
  return $api;
}

