Index: node_gallery.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/node_gallery/node_gallery.admin.inc,v
retrieving revision 1.11.2.48
diff -u -p -r1.11.2.48 node_gallery.admin.inc
--- node_gallery.admin.inc	16 Dec 2010 17:59:37 -0000	1.11.2.48
+++ node_gallery.admin.inc	17 Dec 2010 16:54:57 -0000
@@ -377,6 +377,12 @@ function node_gallery_relationship_setti
     '#default_value' => $relationship['settings']['manage_images_show_gallery_list'],
     '#description' => t('If checked, a gallery dropdown list will be displayed on each image on the Manage Images tab allowing you to move images between galleries.'),
   );
+  $form['node_gallery_manage_images_settings']['manage_images_enable_rotation'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable the rotation images on the manage images page'),
+    '#default_value' => $relationship['settings']['manage_images_enable_rotation'],
+    '#description' => t('If checked, image rotation operations will be displayed on each image on the Manage Images tab.'),
+  );
   $form['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Save'),
Index: node_gallery.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/node_gallery/node_gallery.css,v
retrieving revision 1.4.2.11
diff -u -p -r1.4.2.11 node_gallery.css
--- node_gallery.css	27 Nov 2010 22:49:07 -0000	1.4.2.11
+++ node_gallery.css	16 Dec 2010 12:37:21 -0000
@@ -9,8 +9,38 @@ table.image-navigator td.image-navigator
 table.image-navigator td.image-navigator-last {width:15%; text-align:left}
 table.image-navigator td.image-navigator-gallery-link {width:20%; text-align:right}
 
+/**
+ * Helper classes *not* meant for styling.
+ */
 .ng3-hidden { display:none; }
 
+.ng3-rotate-90 {
+  transform: rotate(90deg);
+  -moz-transform:rotate(90deg);
+  -webkit-transform:rotate(90deg);
+  -o-transform:rotate(90deg);
+  filter:progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=0, M12=-1,M21=1, M22=0);
+  -ms-filter:progid:DXImageTransform.Microsoft.Matrix(M11=0, M12=-1, M21=1, M22=0,sizingMethod='auto expand');
+}
+
+.ng3-rotate-180 {
+  transform:rotate(180deg);
+  -moz-transform:rotate(180deg);
+  -webkit-transform:rotate(180deg);
+  -o-transform:rotate(180deg);
+  filter:progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=-1, M12=0,M21=0, M22=-1);
+  -ms-filter:progid:DXImageTransform.Microsoft.Matrix(M11=-1, M12=0, M21=0, M22=-1,sizingMethod='auto expand');
+}
+
+.ng3-rotate-270 {
+  transform:rotate(-90deg);
+  -moz-transform:rotate(-90deg);
+  -webkit-transform:rotate(-90deg);
+  -o-transform:rotate(-90deg);
+  filter:progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand', M11=0, M12=1,M21=-1, M22=0);
+  -ms-filter:progid:DXImageTransform.Microsoft.Matrix(M11=0, M12=1, M21=-1, M22=0,sizingMethod='auto expand');
+}
+
 /**
  * Fix for word-wrap on the manage images page.
  */
Index: node_gallery.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/node_gallery/node_gallery.inc,v
retrieving revision 1.2.2.93
diff -u -p -r1.2.2.93 node_gallery.inc
--- node_gallery.inc	16 Dec 2010 17:59:37 -0000	1.2.2.93
+++ node_gallery.inc	17 Dec 2010 16:54:57 -0000
@@ -152,6 +152,7 @@ function node_gallery_relationship_setti
     'manage_images_fields' => array('title' => 'title'),
     'manage_images_per_page' => 20,
     'manage_images_show_gallery_list' => TRUE,
+    'manage_images_enable_rotation' => TRUE,
   );
   drupal_alter('node_gallery_relationship_default_settings', $settings);
   return $settings;
@@ -1000,6 +1001,21 @@ function node_gallery_batch_sorting_fini
   drupal_set_message($message);
 }
 
+function node_gallery_batch_rotate_callback($filepath, $degrees, &$context) {
+  $result = 0;
+  $image = imageapi_image_open($filepath);
+  if ($image !== FALSE) {
+    $result = imageapi_image_rotate($image, $degrees);
+    global $conf;
+    $conf['imageapi_jpeg_quality'] = 100;
+    imageapi_image_close($image);
+    imagecache_image_flush($filepath);
+  } else {
+    watchdog('node_gallery', t('Could not open image for rotation'));
+  }
+  $context['results'][] = $result;
+}
+
 /**
  * Compare taxonomy on an unsaved node object versus a node_load()ed object
  * @param $old
Index: node_gallery.pages.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/node_gallery/node_gallery.pages.inc,v
retrieving revision 1.12.2.76
diff -u -p -r1.12.2.76 node_gallery.pages.inc
--- node_gallery.pages.inc	16 Dec 2010 17:59:37 -0000	1.12.2.76
+++ node_gallery.pages.inc	17 Dec 2010 16:54:58 -0000
@@ -190,6 +190,18 @@ function node_gallery_manage_images_form
       $image = node_load($nid);
       $options[$nid] = '';
       $form['images'][$nid]['remove'] = array('#type' => 'checkbox', '#default_value' => $file->remove);
+      if ($relationship['settings']['manage_images_enable_rotation']) {
+        $form['images'][$nid]['rotate'] = array(
+          '#type' => 'radios',
+          '#options' => array(
+            0 => t('None'),
+            90 => t('90° CW'),
+            270 => t('90° CCW'),
+            180 => t('180°')
+          ),
+          '#default_value' => 0,
+        );
+      }
       if ($relationship['settings']['manage_images_show_gallery_list']) {
         $form['images'][$nid]['gid'] = array('#type' => 'select', '#title' => t('Gallery'), '#default_value' => $gallery->nid, '#options' => $gallery_list);
       }
@@ -299,6 +311,14 @@ function node_gallery_manage_images_subm
         $op_images['update'][] = $image_node;
       }
     }
+    if ($form_values['rotate']) {
+      $allowed_degrees = array('90', '270', '180');
+      $degrees = (int)$form_values['rotate'];
+      if (in_array($degrees, $allowed_degrees)) {
+        $node = $form['images'][$image_node->nid]['edit_form']['#node'];
+        $op_images['rotate'][] = array($node->{$relationship['imagefield_name']}[0]['filepath'], $degrees);
+      }
+    }
   }
   while (!empty($op_images['update'])) {
     $node = array_shift($op_images['update']);
@@ -308,6 +328,10 @@ function node_gallery_manage_images_subm
     $nid = array_shift($op_images['delete']);
     $operations[] = array('node_gallery_image_delete_process', array($nid));
   }
+  while (!empty($op_images['rotate'])) {
+    $params = array_shift($op_images['rotate']);
+    $operations[] = array('node_gallery_batch_rotate_callback', $params);
+  }
   if (!empty($operations)) {
     $batch = array(
       'operations' => $operations,
Index: js/ng_manage_images.js
===================================================================
RCS file: js/ng_manage_images.js
diff -N js/ng_manage_images.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ js/ng_manage_images.js	16 Dec 2010 12:41:13 -0000
@@ -0,0 +1,15 @@
+/**
+ * @file
+ * jQuery additions to manage images page.
+ */
+
+/**
+ * Image rotation preview.
+ */
+Drupal.behaviors.nodeGalleryRotateImagePreview = function (context) {
+  $(':input:radio[name^="images"][name$="\[rotate\]"]', context).each(function () {
+    $(this).click(function () {
+      $(this).parents('tr:first').find('img.imagecache-node-gallery-admin-thumbnail').removeClass().addClass('imagecache-node-gallery-admin-thumbnail ng3-rotate-'+$(this).val());
+    });
+  });
+};
Index: theme/theme.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/node_gallery/theme/theme.inc,v
retrieving revision 1.2.2.42
diff -u -p -r1.2.2.42 theme.inc
--- theme/theme.inc	16 Dec 2010 14:29:02 -0000	1.2.2.42
+++ theme/theme.inc	17 Dec 2010 16:56:26 -0000
@@ -11,9 +11,13 @@ function theme_node_gallery_manage_image
   drupal_add_js('misc/tableselect.js');
   // get fieldname to retrieve the filepath for the thumbnail without loading the node
   $relationship = node_gallery_get_relationship($form['#gallery']->type);
+  drupal_add_js(drupal_get_path('module', 'node_gallery').'/js/ng_manage_images.js');
   $field_name = $relationship['imagefield_name'];
   $thumb_imagecache = $form['#thumb_imagecache'];
   $header = array(array('data' => t('Delete'), 'class' => 'select-all'), t('Preview'), t('Edit'), t('Cover'));
+  if ($relationship['settings']['manage_images_enable_rotation']) {
+    $header = array(array('data' => t('Delete'), 'class' => 'select-all'), t('Preview'), t('Edit'), t('Rotation'), t('Cover'));
+  }
   foreach (element_children($form['images']) as $nid) {
     $element = &$form['images'][$nid];
     $filepath = node_gallery_get_image_filepath($element['edit_form']['nid']['#value'], $field_name);
@@ -26,6 +30,9 @@ function theme_node_gallery_manage_image
       $edit .= drupal_render($element['gid']);
     }
     $row[] = $edit;
+    if ($relationship['settings']['manage_images_enable_rotation']) {
+      $row[] = drupal_render($element['rotate']);
+    }
     if ($form['is_cover']) {
       $row[] = drupal_render($form['is_cover'][$nid]);
     }
