textauthor.module 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. <?php
  2. /**
  3. * Implements hook_field_info().
  4. */
  5. function textauthor_field_info() {
  6. return array(
  7. 'textauthor' => array(
  8. 'label' => t('Text with author'),
  9. 'description' => t('This field stores varchar text in the database with author.'),
  10. 'default_widget' => 'textauthor',
  11. 'default_formatter' => 'textauthor',
  12. 'instance_settings' => array('text_processing' => 0),
  13. // Support hook_entity_property_info() from contrib "Entity API".
  14. // 'property_type' => 'field_item_textauthor',
  15. // 'property_callbacks' => array('textauthor_field_property_info_callback'),
  16. ),
  17. );
  18. }
  19. /**
  20. * Implements hook_field_instance_settings_form().
  21. */
  22. function textauthor_field_instance_settings_form($field, $instance) {
  23. $settings = $instance['settings'];
  24. $form['text_processing'] = array(
  25. '#type' => 'radios',
  26. '#title' => t('Text processing'),
  27. '#default_value' => $settings['text_processing'],
  28. '#options' => array(
  29. t('Plain text'),
  30. t('Filtered text (user selects text format)'),
  31. ),
  32. );
  33. return $form;
  34. }
  35. /**
  36. * Implements hook_field_load().
  37. *
  38. * Where possible, generate the sanitized version of each field early so that
  39. * it is cached in the field cache. This avoids looking up from the filter cache
  40. * separately.
  41. *
  42. * @see text_field_formatter_view()
  43. */
  44. function textauthor_field_load($entity_type, $entities, $field, $instances, $langcode, &$items) {
  45. foreach ($entities as $id => $entity) {
  46. foreach ($items[$id] as $delta => $item) {
  47. // Only process items with a cacheable format, the rest will be handled
  48. // by formatters if needed.
  49. if (empty($instances[$id]['settings']['text_processing']) || filter_format_allowcache($item['format'])) {
  50. $items[$id][$delta]['safe_value'] = isset($item['value']) ? _text_sanitize($instances[$id], $langcode, $item, 'value') : '';
  51. }
  52. }
  53. }
  54. }
  55. /**
  56. * Implements hook_field_is_empty().
  57. */
  58. function textauthor_field_is_empty($item, $field) {
  59. if (!isset($item['value']) || $item['value'] === '') {
  60. return !isset($item['author']) || $item['author'] === '';
  61. }
  62. return FALSE;
  63. }
  64. /**
  65. * Implements hook_field_widget_info().
  66. */
  67. function textauthor_field_widget_info() {
  68. return array(
  69. 'textauthor' => array(
  70. 'label' => t('Text with author'),
  71. 'field types' => array('textauthor'),
  72. 'settings' => array('size' => 60),
  73. 'behaviors' => array(
  74. 'multiple values' => FIELD_BEHAVIOR_DEFAULT,
  75. 'default value' => FIELD_BEHAVIOR_DEFAULT,
  76. ),
  77. ),
  78. );
  79. }
  80. /**
  81. * Implements hook_field_widget_form().
  82. */
  83. function textauthor_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  84. // dsm($element, '$element');
  85. $main_widget = $element + array(
  86. '#type' => 'textarea',
  87. '#title' => t('Text'),
  88. '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
  89. );
  90. if($instance['settings']['text_processing']){
  91. $element = $main_widget;
  92. $element['#type'] = 'text_format';
  93. $element['#format'] = isset($items[$delta]['format']) ? $items[$delta]['format'] : NULL;
  94. $element['#base_type'] = 'textarea';
  95. }else{
  96. $element['value'] = $main_widget;
  97. }
  98. $element['author'] = array(
  99. '#type' => 'textfield',
  100. '#title' => t('!title Author', array('!title'=>$element['#title'])),
  101. '#default_value' => isset($items[$delta]['author']) ? $items[$delta]['author'] : NULL,
  102. '#weight' => 1,
  103. );
  104. //
  105. return $element;
  106. }
  107. /**
  108. * Implements hook_theme().
  109. */
  110. function textauthor_theme($existing, $type, $theme, $path) {
  111. return array(
  112. 'textauthor_formatter' => array(
  113. 'variables' => array('element' => NULL),
  114. ),
  115. );
  116. }
  117. /**
  118. * Implements hook_field_formatter_info().
  119. */
  120. function textauthor_field_formatter_info() {
  121. return array(
  122. 'textauthor_default' => array(
  123. 'label' => t('Default'),
  124. 'field types' => array('textauthor'),
  125. ),
  126. 'textauthor_plain' => array(
  127. 'label' => t('Plain text'),
  128. 'field types' => array('textauthor'),
  129. ),
  130. // The text_trimmed formatter displays the trimmed version of the
  131. // full element of the field. It is intended to be used with text
  132. // and text_long fields. It also works with text_with_summary
  133. // fields though the text_summary_or_trimmed formatter makes more
  134. // sense for that field type.
  135. 'textauthor_trimmed' => array(
  136. 'label' => t('Trimmed'),
  137. 'field types' => array('textauthor'),
  138. 'settings' => array('trim_length' => 600),
  139. ),
  140. );
  141. }
  142. /**
  143. * Implements hook_field_formatter_settings_form().
  144. */
  145. function textauthor_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  146. $display = $instance['display'][$view_mode];
  147. $settings = $display['settings'];
  148. $element = array();
  149. if (strpos($display['type'], '_trimmed') !== FALSE) {
  150. $element['trim_length'] = array(
  151. '#title' => t('Trim length'),
  152. '#type' => 'textfield',
  153. '#size' => 10,
  154. '#default_value' => $settings['trim_length'],
  155. '#element_validate' => array('element_validate_integer_positive'),
  156. '#required' => TRUE,
  157. );
  158. }
  159. return $element;
  160. }
  161. /**
  162. * Implements hook_field_formatter_view().
  163. */
  164. function textauthor_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  165. if ($display['type'] == 'textauthor_default' || $display['type'] == 'textauthor_trimmed') {
  166. foreach ($items as $delta => $item) {
  167. $output = _textauthor_sanitize($instance, $langcode, $item, 'value');
  168. if ($display['type'] == 'textauthor_trimmed') {
  169. $output = textauthor_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']);
  170. }
  171. $items[$delta]['output_value'] = $output;
  172. }
  173. }
  174. $elements = array();
  175. foreach ($items as $delta => $item) {
  176. $elements[$delta] = array(
  177. '#markup' => theme('textauthor_formatter', array('element' => $item, 'field' => $instance)),
  178. );
  179. }
  180. return $elements;
  181. }
  182. /**
  183. * Sanitizes the 'value' or 'summary' data of a text value.
  184. *
  185. * Depending on whether the field instance uses text processing, data is run
  186. * through check_plain() or check_markup().
  187. *
  188. * @param $instance
  189. * The instance definition.
  190. * @param $langcode
  191. * The language associated to $item.
  192. * @param $item
  193. * The field value to sanitize.
  194. * @param $column
  195. * The column to sanitize (either 'value' or 'summary').
  196. *
  197. * @return
  198. * The sanitized string.
  199. */
  200. function _textauthor_sanitize($instance, $langcode, $item, $column) {
  201. // If the value uses a cacheable text format, text_field_load() precomputes
  202. // the sanitized string.
  203. if (isset($item["safe_$column"])) {
  204. return $item["safe_$column"];
  205. }
  206. return $instance['settings']['text_processing'] ? check_markup($item[$column], $item['format'], $langcode) : check_plain($item[$column]);
  207. }
  208. /**
  209. * Generate a trimmed, formatted version of a text field value.
  210. *
  211. * If the end of the summary is not indicated using the <!--break--> delimiter
  212. * then we generate the summary automatically, trying to end it at a sensible
  213. * place such as the end of a paragraph, a line break, or the end of a
  214. * sentence (in that order of preference).
  215. *
  216. * @param $text
  217. * The content for which a summary will be generated.
  218. * @param $format
  219. * The format of the content.
  220. * If the PHP filter is present and $text contains PHP code, we do not
  221. * split it up to prevent parse errors.
  222. * If the line break filter is present then we treat newlines embedded in
  223. * $text as line breaks.
  224. * If the htmlcorrector filter is present, it will be run on the generated
  225. * summary (if different from the incoming $text).
  226. * @param $size
  227. * The desired character length of the summary. If omitted, the default
  228. * value will be used. Ignored if the special delimiter is present
  229. * in $text.
  230. * @return
  231. * The generated summary.
  232. */
  233. function textauthor_summary($text, $format = NULL, $size = NULL) {
  234. if (!isset($size)) {
  235. // What used to be called 'teaser' is now called 'summary', but
  236. // the variable 'teaser_length' is preserved for backwards compatibility.
  237. $size = variable_get('teaser_length', 600);
  238. }
  239. // Find where the delimiter is in the body
  240. $delimiter = strpos($text, '<!--break-->');
  241. // If the size is zero, and there is no delimiter, the entire body is the summary.
  242. if ($size == 0 && $delimiter === FALSE) {
  243. return $text;
  244. }
  245. // If a valid delimiter has been specified, use it to chop off the summary.
  246. if ($delimiter !== FALSE) {
  247. return substr($text, 0, $delimiter);
  248. }
  249. // We check for the presence of the PHP evaluator filter in the current
  250. // format. If the body contains PHP code, we do not split it up to prevent
  251. // parse errors.
  252. if (isset($format)) {
  253. $filters = filter_list_format($format);
  254. if (isset($filters['php_code']) && $filters['php_code']->status && strpos($text, '<?') !== FALSE) {
  255. return $text;
  256. }
  257. }
  258. // If we have a short body, the entire body is the summary.
  259. if (drupal_strlen($text) <= $size) {
  260. return $text;
  261. }
  262. // If the delimiter has not been specified, try to split at paragraph or
  263. // sentence boundaries.
  264. // The summary may not be longer than maximum length specified. Initial slice.
  265. $summary = truncate_utf8($text, $size);
  266. // Store the actual length of the UTF8 string -- which might not be the same
  267. // as $size.
  268. $max_rpos = strlen($summary);
  269. // How much to cut off the end of the summary so that it doesn't end in the
  270. // middle of a paragraph, sentence, or word.
  271. // Initialize it to maximum in order to find the minimum.
  272. $min_rpos = $max_rpos;
  273. // Store the reverse of the summary. We use strpos on the reversed needle and
  274. // haystack for speed and convenience.
  275. $reversed = strrev($summary);
  276. // Build an array of arrays of break points grouped by preference.
  277. $break_points = array();
  278. // A paragraph near the end of sliced summary is most preferable.
  279. $break_points[] = array('</p>' => 0);
  280. // If no complete paragraph then treat line breaks as paragraphs.
  281. $line_breaks = array('<br />' => 6, '<br>' => 4);
  282. // Newline only indicates a line break if line break converter
  283. // filter is present.
  284. if (isset($filters['filter_autop'])) {
  285. $line_breaks["\n"] = 1;
  286. }
  287. $break_points[] = $line_breaks;
  288. // If the first paragraph is too long, split at the end of a sentence.
  289. $break_points[] = array('. ' => 1, '! ' => 1, '? ' => 1, '。' => 0, '؟ ' => 1);
  290. // Iterate over the groups of break points until a break point is found.
  291. foreach ($break_points as $points) {
  292. // Look for each break point, starting at the end of the summary.
  293. foreach ($points as $point => $offset) {
  294. // The summary is already reversed, but the break point isn't.
  295. $rpos = strpos($reversed, strrev($point));
  296. if ($rpos !== FALSE) {
  297. $min_rpos = min($rpos + $offset, $min_rpos);
  298. }
  299. }
  300. // If a break point was found in this group, slice and stop searching.
  301. if ($min_rpos !== $max_rpos) {
  302. // Don't slice with length 0. Length must be <0 to slice from RHS.
  303. $summary = ($min_rpos === 0) ? $summary : substr($summary, 0, 0 - $min_rpos);
  304. break;
  305. }
  306. }
  307. // If the htmlcorrector filter is present, apply it to the generated summary.
  308. if (isset($filters['filter_htmlcorrector'])) {
  309. $summary = _filter_htmlcorrector($summary);
  310. }
  311. return $summary;
  312. }
  313. function theme_textauthor_formatter($vars){
  314. $output = '';
  315. $output .= '<article class="'.$vars['element']['format'].'">'.$vars['element']['output_value'].'</article>';
  316. if($vars['element']['author'] != '')
  317. $output .= '<address rel="author">'.$vars['element']['author'].'</address>';
  318. return $output;
  319. }
  320. /**
  321. * Implements hook_field_prepare_translation().
  322. */
  323. function textauthor_field_prepare_translation($entity_type, $entity, $field, $instance, $langcode, &$items, $source_entity, $source_langcode) {
  324. // If the translating user is not permitted to use the assigned text format,
  325. // we must not expose the source values.
  326. $field_name = $field['field_name'];
  327. if (!empty($source_entity->{$field_name}[$source_langcode])) {
  328. $formats = filter_formats();
  329. foreach ($source_entity->{$field_name}[$source_langcode] as $delta => $item) {
  330. $format_id = $item['format'];
  331. if (!empty($format_id) && !filter_access($formats[$format_id])) {
  332. unset($items[$delta]);
  333. }
  334. }
  335. }
  336. }
  337. /**
  338. * Implements hook_filter_format_update().
  339. */
  340. function textauthor_filter_format_update($format) {
  341. field_cache_clear();
  342. }
  343. /**
  344. * Implements hook_filter_format_disable().
  345. */
  346. function textauthor_filter_format_disable($format) {
  347. field_cache_clear();
  348. }
  349. /**
  350. * Implementation of hook_migrate_api().
  351. */
  352. function textauthor_migrate_api() {
  353. $api = array(
  354. 'api' => 2,
  355. );
  356. return $api;
  357. }