image_styles_admin.inc 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <?php
  2. /**
  3. * @file Include file for image_styles_admin routines that do not need to be
  4. * loaded on each request.
  5. */
  6. /**
  7. * Menu callback: Duplicates an image style and redirects to the image styles
  8. * overview page.
  9. *
  10. * @param array $style
  11. * An image style array.
  12. *
  13. * @see image_style_name_validate()
  14. */
  15. function image_styles_admin_duplicate_page_callback($style) {
  16. $duplicate_style = image_styles_admin_duplicate($style);
  17. drupal_set_message(t('Style %name has been duplicated to %new_name.', array('%name' => $style['name'], '%new_name' => $duplicate_style['name'])));
  18. drupal_goto('admin/config/media/image-styles');
  19. }
  20. /**
  21. * Duplicates an image style and saves it.
  22. *
  23. * @param array $style
  24. * An image style array.
  25. * @param string|null $new_style_name
  26. * The preferred name for the new style. If left empty, the new name will be
  27. * based on the name of the style to duplicate. In both cases and when
  28. * necessary, the new name will be made unique by adding some suffix to it.
  29. *
  30. * @return array
  31. * An image style array with the newly created copy of the given style.
  32. *
  33. * @see image_style_name_validate()
  34. */
  35. function image_styles_admin_duplicate($style, $new_style_name = NULL) {
  36. // Find a unique name for copy.
  37. // Step 1: Find the base: name without things like '-copy' or '-copy-1'
  38. $style_name_base = empty($new_style_name) ? $style['name'] : $new_style_name;
  39. if (preg_match('/-copy(-\d+)?$/', $style_name_base)) {
  40. $style_name_base = substr($style_name_base, 0, strpos($style_name_base, '-copy'));
  41. }
  42. // Step 2: Add -copy to it (if the name comes from the current style).
  43. if (empty($new_style_name)) {
  44. $style_name_base .= '-copy';
  45. }
  46. // Step 3: Ensure the new name will be unique.
  47. $i = 0;
  48. $style_name = $style_name_base;
  49. $styles = image_styles();
  50. while (isset($styles[$style_name])) {
  51. $i++;
  52. $style_name = $style_name_base . '-' . $i;
  53. }
  54. $style['name'] = $style_name;
  55. // Unset isid to save it as a new style.
  56. unset($style['isid']);
  57. $style = image_style_save($style);
  58. // Save copies of each effect with the new image style ID (isid).
  59. foreach ($style['effects'] as &$effect) {
  60. // Unset ieid to save it as a new effect.
  61. unset($effect['ieid']);
  62. $effect['isid'] = $style['isid'];
  63. $effect = image_effect_save($effect);
  64. }
  65. return $style;
  66. }
  67. /**
  68. * drupal_get_form callback: form to export an image style.
  69. *
  70. * @param array $style
  71. * An image style array.
  72. */
  73. function image_styles_admin_export_form($form, $form_state, $style) {
  74. drupal_set_title(format_string('%page_name @style_name', array('%page_name' => t('Export image style'), '@style_name' => $style['name'])), PASS_THROUGH);
  75. $form['serialized_style'] = array(
  76. '#type' => 'textarea',
  77. '#rows' => 5,
  78. '#title' => t('Image style export data'),
  79. '#default_value' => serialize($style),
  80. '#attributes' => array('readonly' =>'readonly'),
  81. '#description' => t('Copy the contents of this field to the clipboard and, on another site, paste it in the textarea of an %page_title page.', array('%page_title' => t('Import image style'))),
  82. );
  83. return $form;
  84. }
  85. /**
  86. * drupal_get_form callback: form to import an image style.
  87. *
  88. * @param array $style
  89. * An image style array.
  90. */
  91. function image_styles_admin_import_form($form, $form_state) {
  92. $form['serialized_style'] = array(
  93. '#type' => 'textarea',
  94. '#rows' => 5,
  95. '#title' => t('Image style import data'),
  96. '#default_value' => '',
  97. '#required' => TRUE,
  98. '#description' => t('Paste the contents of the textarea of an %page_title page into this field.', array('%page_title' => t('Export image style'))),
  99. );
  100. $form['actions'] = array('#type' => 'actions');
  101. $form['actions']['submit'] = array(
  102. '#type' => 'submit',
  103. '#value' => t('Import'),
  104. );
  105. return $form;
  106. }
  107. /**
  108. * Callback to validate the import style form.
  109. */
  110. function image_styles_admin_import_form_validate($form, &$form_state) {
  111. if (image_styles_admin_import_extract_style($form_state['values']['serialized_style']) === FALSE) {
  112. form_set_error('serialized_style', t('The %field cannot be imported as an image style.', array('%field' => t('Image style import data'))));
  113. }
  114. }
  115. /**
  116. * Callback to process form submission of the import style form.
  117. */
  118. function image_styles_admin_import_form_submit($form, &$form_state) {
  119. $style = image_styles_admin_import_extract_style($form_state['values']['serialized_style']);
  120. // Import the style by "duplicating" it, but prevent adding the -copy suffix
  121. // by passing the requested name as 2nd parameter.
  122. $new_style = image_styles_admin_duplicate($style, $style['name']);
  123. if ($new_style['name'] === $style['name']) {
  124. drupal_set_message(t('Style %name has been imported.', array('%name' => $style['name'])));
  125. }
  126. else {
  127. drupal_set_message(t('Style %name has been imported as %new_name.', array('%name' => $style['name'], '%new_name' => $new_style['name'])));
  128. }
  129. drupal_goto('admin/config/media/image-styles');
  130. }
  131. /**
  132. * Unserializes and validates a string into image style data.
  133. *
  134. * @param string $import
  135. * The string representation of a @see serialize()'d image style array.
  136. *
  137. * @return array|false
  138. * An image style array or false if the string could not be unserialized into
  139. * image style data.
  140. */
  141. function image_styles_admin_import_extract_style($import) {
  142. $style = @unserialize($import);
  143. // Check if the contents of the textarea could be unserialized into an array.
  144. if (!is_array($style)) {
  145. return FALSE;
  146. }
  147. // Check if the required keys are available, we will ignore the other.
  148. $style = array_intersect_key($style, array('name' => 0, 'effects' => 0));
  149. if (count($style) !== 2) {
  150. return FALSE;
  151. }
  152. // 'name' must be "machine name" string
  153. if (!is_string($style['name']) || preg_match('/[0-9a-z_\-]+/', $style['name']) !== 1) {
  154. return FALSE;
  155. }
  156. // 'effects' must be an array
  157. if (!is_array($style['effects'])) {
  158. return FALSE;
  159. }
  160. // Check effects elements
  161. foreach ($style['effects'] as &$effect) {
  162. // an effect must be an array.
  163. if (!is_array($effect)) {
  164. return FALSE;
  165. }
  166. // Check if the required keys are available, we will ignore the other.
  167. $effect = array_intersect_key($effect, array('weight' => 0, 'name' => 0, 'data' => 0));
  168. if (count($effect) !== 3) {
  169. return FALSE;
  170. }
  171. // effect weight must be an integer (data type in table is int, not float).
  172. if (!is_int($effect['weight']) && $effect['weight'] !== (string) (int) $effect['weight']) {
  173. return FALSE;
  174. }
  175. // effect name must be a string
  176. if (!is_string($effect['name'])) {
  177. return FALSE;
  178. }
  179. // Check whether the effect data is an array.
  180. if (!is_array($effect['data'])) {
  181. return FALSE;
  182. }
  183. }
  184. // @todo: are there any security implications for creating styles like this?
  185. // - Unserialize() is save in itself: it only creates data (except possibly
  186. // for__wakeup(), but that can only be in already existing code: safe
  187. // - Not expected array entries are removed (array_intersect_key): safe
  188. // - Basic types are checked: safe
  189. // - Effect data array is not checked. Possibly unsafe?! The effect data array
  190. // contains the effect parameters. Normally these are entered and validated
  191. // via a form and subsequently saved in the database (serialized as here).
  192. // The form validation is not executed on import and thus the data may
  193. // contain invalid values. This is acceptable as it can also be done by
  194. // operating directly on the database. In Drupal this is not normally
  195. // checked for during processing: error messages will make clear that the
  196. // data has been played with. Can incorrect dat be abused? It may contain:
  197. // - incorrect types: we do not know the data structure of the various
  198. // effects, so we cannot check that and have to accept it as it comes.
  199. // Effects should check_plain in summary theme and convert to int/float
  200. // whenever possible befoire using it in commands.
  201. // - PHP code, but that may be valid content for the text or custom effects:
  202. // Effects should check_plain in summary theme and convert to int/float
  203. // whenever possible befoire using it in commands.
  204. // @todo: if the style contains an effect that contains PHP code, the user
  205. // should need the 'use PHP for settings' permission.
  206. // - HTML and or JS code: when used as parameter, this normally won't hurt.
  207. // When showing on the screen (summary theme), proper escaping should
  208. // suffice and is needed anyway: responsibility of effect.
  209. return $style;
  210. }