better_formats.module 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. <?php
  2. /**
  3. * @file
  4. * Enhances Drupal's core text format settings.
  5. */
  6. /**
  7. * Implements of hook_perm().
  8. */
  9. function better_formats_permission() {
  10. $entities = entity_get_info();
  11. $perms = array(
  12. 'show format tips' => array(
  13. 'title' => t('Show format tips'),
  14. 'description' => t('Toggle display of format description help.'),
  15. ),
  16. 'show more format tips link' => array(
  17. 'title' => t('Show more format tips link'),
  18. 'description' => t('Toggle display of the "More information about text formats" link.'),
  19. ),
  20. );
  21. foreach ($entities as $type => $info) {
  22. if ($info['fieldable']) {
  23. $perms['show format selection for ' . $type] = array(
  24. 'title' => t('Show format selection for @entitys', array('@entity' => $type)),
  25. );
  26. }
  27. }
  28. return $perms;
  29. }
  30. /**
  31. * Implements hook_menu().
  32. */
  33. function better_formats_menu() {
  34. $items = array();
  35. $items['admin/config/content/formats/settings'] = array(
  36. 'title' => 'Settings',
  37. 'description' => 'Manage text formats',
  38. 'page callback' => 'drupal_get_form',
  39. 'page arguments' => array('better_formats_admin_settings_form'),
  40. 'access arguments' => array('administer filters'),
  41. 'type' => MENU_LOCAL_TASK,
  42. 'weight' => 3,
  43. 'file' => 'better_formats.admin_settings.inc',
  44. );
  45. /*
  46. $items['admin/config/content/formats/defaults'] = array(
  47. 'title' => 'Defaults',
  48. 'description' => 'Manage text formats',
  49. 'page callback' => 'drupal_get_form',
  50. 'page arguments' => array('better_formats_defaults_admin_form'),
  51. 'access arguments' => array('administer filters'),
  52. 'type' => MENU_LOCAL_TASK,
  53. 'weight' => 2,
  54. 'file' => 'better_formats.admin_defaults.inc',
  55. );
  56. */
  57. return $items;
  58. }
  59. /**
  60. * Implements of hook_element_info_alter().
  61. */
  62. function better_formats_element_info_alter(&$type) {
  63. // Our process callback must run immediately after filter_process_format().
  64. $filter_process_format_location = array_search('filter_process_format', $type['text_format']['#process']);
  65. $replacement = array('filter_process_format', 'better_formats_filter_process_format');
  66. array_splice($type['text_format']['#process'], $filter_process_format_location, 1, $replacement);
  67. }
  68. /**
  69. * Process callback for form elements that have a text format selector attached.
  70. *
  71. * This callback runs after filter_process_format() and performs additional
  72. * modifications to the form element.
  73. *
  74. * @see filter_process_format()
  75. */
  76. function better_formats_filter_process_format($element) {
  77. // Before we make any modifications to the element, record whether or not
  78. // filter_process_format() has determined that (for security reasons) the
  79. // user is not allowed to make any changes to this field. (This will happen
  80. // if the user does not have permission to use the currently-assigned text
  81. // format.)
  82. $access_denied_for_security = isset($element['format']['#access']) && !$element['format']['#access'];
  83. // Now hide several parts of the element for cosmetic reasons (depending on
  84. // the permissions of the current user).
  85. $show_selection = TRUE;
  86. if (isset($element['#entity_type'])) {
  87. $show_selection = user_access('show format selection for ' . $element['#entity_type']);
  88. }
  89. $show_tips = user_access('show format tips');
  90. $show_tips_link = user_access('show more format tips link');
  91. if (!$show_selection) {
  92. $element['format']['format']['#access'] = FALSE;
  93. }
  94. if (!$show_tips) {
  95. $element['format']['guidelines']['#access'] = FALSE;
  96. }
  97. if (!$show_tips_link) {
  98. $element['format']['help']['#access'] = FALSE;
  99. }
  100. // If the element represents a field attached to an entity, we may need to
  101. // adjust the allowed text format options. However, we don't want to touch
  102. // this if filter_process_format() has determined that (for security reasons)
  103. // the user is not allowed to make any changes; in that case, Drupal core
  104. // will hide the format selector and force the field to be saved with its
  105. // current values, and we should not do anything to alter that process.
  106. if (isset($element['#entity_type']) && !$access_denied_for_security) {
  107. $instance_info = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
  108. $bf = isset($instance_info['settings']['better_formats']) ? $instance_info['settings']['better_formats'] : NULL;
  109. // Need to only do this on create forms.
  110. if (!empty($element['#entity']) && !empty($element['#entity_type'])) {
  111. list($eid, $vid, $bundle) = entity_extract_ids($element['#entity_type'], $element['#entity']);
  112. if (empty($eid) && isset($bf) && !empty($bf['default_order_toggle']) && !empty($bf['default_order_wrapper']['formats'])) {
  113. $order = $bf['default_order_wrapper']['formats'];
  114. uasort($order, 'better_formats_text_format_sort');
  115. $options = array();
  116. foreach ($order as $id => $weight) {
  117. if (isset($element['format']['format']['#options'][$id])) {
  118. $options[$id] = $element['format']['format']['#options'][$id];
  119. }
  120. }
  121. $element['format']['format']['#options'] = $options;
  122. $options_keys = array_keys($options);
  123. $element['format']['format']['#default_value'] = array_shift($options_keys);
  124. }
  125. }
  126. if (isset($bf) && !empty($bf['allowed_formats_toggle']) && !empty($bf['allowed_formats'])) {
  127. // Filter the list of available formats to those allowed on this field.
  128. $allowed_fields = array_filter($bf['allowed_formats']);
  129. $options = &$element['format']['format']['#options'];
  130. $options = array_intersect_key($options, $allowed_fields);
  131. // If there is only one allowed format, deny access to the text format
  132. // selector for cosmetic reasons, just like filter_process_format() does.
  133. if (count($options) == 1) {
  134. $element['format']['format']['#access'] = FALSE;
  135. $show_selection = FALSE;
  136. }
  137. // If there are no allowed formats, we need to deny access to the entire
  138. // field, since it doesn't make sense to add or edit content that does
  139. // not have a text format.
  140. if (empty($options)) {
  141. $element['#access'] = FALSE;
  142. }
  143. // Otherwise, if the current default format is no longer one of the
  144. // allowed options, a new default format must be assigned.
  145. elseif (!isset($options[$element['format']['format']['#default_value']])) {
  146. // If there is no text in the field, it is safe to automatically assign
  147. // a new default format. We pick the first available option to be
  148. // consistent with what filter_default_format() does.
  149. if (!isset($element['value']['#default_value']) || $element['value']['#default_value']==='') {
  150. $formats = array_keys($options);
  151. $element['format']['format']['#default_value'] = reset($formats);
  152. }
  153. // Otherwise, it is unsafe to automatically assign a new default format
  154. // (since this will display the content in a way that was not
  155. // originally intended and might be dangerous, e.g. if the content
  156. // contains an attempted XSS attack). A human must explicitly decide
  157. // which new format to assign, so we force the field to be required but
  158. // with no default value, similar to what filter_process_format() does.
  159. // Although filter_process_format() limits this functionality to users
  160. // with the 'administer filters' permission, we can allow it for any
  161. // user here since we know that the user already has permission to use
  162. // the current format; thus, there is no danger of exposing unformatted
  163. // text (for example, raw PHP code) that they are otherwise not allowed
  164. // to see.
  165. else {
  166. $element['format']['format']['#required'] = TRUE;
  167. $element['format']['format']['#default_value'] = NULL;
  168. // Force access to the format selector (it may have been denied
  169. // previously for cosmetic reasons).
  170. $element['format']['#access'] = TRUE;
  171. $element['format']['format']['#access'] = TRUE;
  172. }
  173. }
  174. }
  175. }
  176. // If the user is not supposed to see the text format selector, hide all
  177. // guidelines except those associated with the default format. We need to do
  178. // this at the end, since the above code may have altered the default format.
  179. if (!$show_selection && isset($element['format']['format']['#default_value'])) {
  180. foreach (element_children($element['format']['guidelines']) as $format) {
  181. if ($format != $element['format']['format']['#default_value']) {
  182. $element['format']['guidelines'][$format]['#access'] = FALSE;
  183. }
  184. }
  185. }
  186. // Hide the entire text format fieldset if the user is not supposed to see
  187. // anything inside it.
  188. if (!$show_selection && !$show_tips && !$show_tips_link) {
  189. $element['format']['#type'] = 'container';
  190. }
  191. return $element;
  192. }
  193. /**
  194. * Sort text formats by weight.
  195. */
  196. function better_formats_text_format_sort($a, $b) {
  197. return $a['weight'] > $b['weight'];
  198. }
  199. /**
  200. * Implements hook_form_FORM_ID_alter().
  201. */
  202. function better_formats_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
  203. $settings = $form['#instance']['settings'];
  204. // Only alter fields with text processing and if admin has chosen.
  205. $text_processing = isset($settings['text_processing']);
  206. if ($text_processing && variable_get('better_formats_per_field_core', 0)) {
  207. // Add a submit handler to save default values on empty fields.
  208. $form['#submit'][] = 'better_formats_form_field_ui_edit_form_submit';
  209. }
  210. // If the field is a format-using text field, allow the admin to configure
  211. // which formats are allowed here.
  212. if ($text_processing) {
  213. // We have to set an explicit weight here so that we can put the allowed
  214. // formats list after it.
  215. $form['instance']['settings']['text_processing']['#weight'] = -3;
  216. $bf_settings = isset($settings['better_formats']) ? $settings['better_formats'] : array();
  217. // Add in our formats table
  218. $form['instance']['settings'] += better_formats_field_settings_form($bf_settings);
  219. }
  220. }
  221. /**
  222. * Build the settings form for Field API fields.
  223. *
  224. * @param $bf_form
  225. * The existing better formats settings form from the form element.
  226. */
  227. function better_formats_field_settings_form($bf_form = array()) {
  228. $formats = filter_formats();
  229. $form = array();
  230. $form['better_formats'] = array(
  231. '#tree' => TRUE,
  232. '#type' => 'fieldset',
  233. '#title' => t('Text Formats'),
  234. '#weight' => -2,
  235. '#states' => array(
  236. 'visible' => array(
  237. ':input[name="instance[settings][text_processing]"]' => array('value' => '1'),
  238. ),
  239. ),
  240. );
  241. foreach ($formats as $format_id => $format) {
  242. $allowed_options[$format_id] = $format->name;
  243. }
  244. $allowed_toggle_default = isset($bf_form['allowed_formats_toggle']) ? $bf_form['allowed_formats_toggle'] : FALSE;
  245. $allowed_defaults = isset($bf_form['allowed_formats']) ? $bf_form['allowed_formats'] : array();
  246. if (empty($allowed_defaults)) {
  247. $allowed_defaults = array_keys($allowed_options);
  248. }
  249. $form['better_formats']['allowed_formats_toggle'] = array(
  250. '#type' => 'checkbox',
  251. '#title' => t('Limit allowed text formats'),
  252. '#description' => t('Check the allowed formats below. If checked available text formats can be chosen.'),
  253. '#weight' => 1,
  254. '#default_value' => $allowed_toggle_default,
  255. );
  256. $form['better_formats']['allowed_formats'] = array(
  257. '#type' => 'checkboxes',
  258. '#title' => t('Allowed formats'),
  259. '#options' => $allowed_options,
  260. '#description' => t('Select the text formats allowed for this field. Note that not all of these may appear on the form if a user does not have permission to use them. <strong>Warning:</strong> This affects existing content which may leave you unable to edit some fields. If that happens you must allow the format that field was saved in here.'),
  261. '#weight' => 2,
  262. '#default_value' => $allowed_defaults,
  263. '#states' => array(
  264. 'visible' => array(
  265. ':input[name="instance[settings][text_processing]"]' => array('value' => '1'),
  266. ':input[name="instance[settings][better_formats][allowed_formats_toggle]"]' => array('checked' => TRUE),
  267. ),
  268. ),
  269. );
  270. $order_toggle_default = isset($bf_form['default_order_toggle']) ? $bf_form['default_order_toggle'] : FALSE;
  271. $form['better_formats']['default_order_toggle'] = array(
  272. '#type' => 'checkbox',
  273. '#title' => t('Override default order'),
  274. '#description' => t('Override the global order that will determine the default text format a user will get <strong>only on entity creation</strong>.'),
  275. '#weight' => 3,
  276. '#default_value' => $order_toggle_default,
  277. );
  278. $form['better_formats']['default_order_wrapper'] = array(
  279. //'#tree' => TRUE,
  280. '#type' => 'container',
  281. '#theme' => 'better_formats_field_default_order',
  282. '#weight' => 4,
  283. '#states' => array(
  284. 'visible' => array(
  285. ':input[name="instance[settings][text_processing]"]' => array('value' => '1'),
  286. ':input[name="instance[settings][better_formats][default_order_toggle]"]' => array('checked' => TRUE),
  287. ),
  288. ),
  289. );
  290. foreach ($formats as $key => $format) {
  291. $default = isset($bf_form['default_order_wrapper']['formats'][$key]) ? $bf_form['default_order_wrapper']['formats'][$key] : NULL;
  292. $rows[$key]['name'] = array('#markup' => $format->name);
  293. $rows[$key]['weight'] = array(
  294. '#type' => 'weight',
  295. '#default_value' => isset($default['weight']) ? $default['weight'] : $format->weight,
  296. '#delta' => 50,
  297. );
  298. $rows[$key]['#weight'] = isset($default['weight']) ? $default['weight'] : $format->weight;
  299. }
  300. $form['better_formats']['default_order_wrapper']['formats'] = $rows;
  301. return $form;
  302. }
  303. /**
  304. * Submit handler for field instance edit form.
  305. *
  306. * Copied and slightly modifed from field_ui_field_edit_form_submit().
  307. * @see field_ui_field_edit_form_submit()
  308. */
  309. function better_formats_form_field_ui_edit_form_submit($form, &$form_state) {
  310. $instance = $form_state['values']['instance'];
  311. $field = $form_state['values']['field'];
  312. // Only act on fields that have text processing enabled.
  313. if ($instance['settings']['text_processing'] == 1) {
  314. // Update any field settings that have changed.
  315. $field_source = field_info_field($instance['field_name']);
  316. $field = array_merge($field_source, $field);
  317. field_update_field($field);
  318. // Handle the default value.
  319. if (isset($form['instance']['default_value_widget'])) {
  320. $element = $form['instance']['default_value_widget'];
  321. // Extract field values.
  322. $items = array();
  323. field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
  324. // Commenting out the below line to not remove emtpy fields.
  325. //field_default_submit(NULL, NULL, $field, $instance, LANGUAGE_NONE, $items, $element, $form_state);
  326. $instance['default_value'] = $items ? $items : NULL;
  327. }
  328. // Retrieve the stored instance settings to merge with the incoming values.
  329. $instance_source = field_read_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
  330. $instance = array_merge($instance_source, $instance);
  331. field_update_instance($instance);
  332. }
  333. // Messaging and redirect removed as this is added in the default handler.
  334. }
  335. /**
  336. * Implements hook_theme()
  337. */
  338. function better_formats_theme() {
  339. return array(
  340. 'better_formats_field_default_order' => array(
  341. 'render element' => 'form',
  342. )
  343. );
  344. }
  345. /**
  346. * Theme format default by role on field settings form.
  347. */
  348. function theme_better_formats_field_default_order(&$variables) {
  349. $form = $variables['form'];
  350. $rows = array();
  351. $output = drupal_render($form['override']);
  352. if (is_array($form)) {
  353. $order_form =& $form['formats'];
  354. $order_form['#sorted'] = FALSE;
  355. foreach (element_children($order_form, TRUE) as $name) {
  356. $order_form[$name]['weight']['#attributes']['class'][] = 'format-order-weight';
  357. $rows[] = array(
  358. 'data' => array(
  359. drupal_render($order_form[$name]['name']),
  360. drupal_render($order_form[$name]['weight']),
  361. ),
  362. 'class' => array('draggable'),
  363. );
  364. }
  365. }
  366. $header = array(
  367. t('Format'),
  368. t('Weight'),
  369. );
  370. $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'format-order')));
  371. drupal_add_tabledrag('format-order', 'order', 'sibling', 'format-order-weight', NULL, NULL, TRUE);
  372. return $output;
  373. }