From 9b3fdc25c171d2b2461af42224ea16ad6c032c49 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Fri, 19 Mar 2010 07:20:12 -0400
Subject: [PATCH] - Implemented messages copying using drag&drop + SHIFT (#1484086)

---
 skins/default/common.css                |   38 +++++++
 CHANGELOG                               |    1 
 program/steps/mail/copy.inc             |   59 +++++++++++
 skins/default/templates/mail.html       |    7 +
 program/include/rcube_imap.php          |   57 ++++++++++-
 program/localization/en_US/messages.inc |    1 
 program/steps/mail/func.inc             |    5 
 program/localization/en_US/labels.inc   |    2 
 program/localization/pl_PL/labels.inc   |    2 
 program/include/rcube_template.php      |    2 
 program/localization/pl_PL/messages.inc |    3 
 skins/default/functions.js              |    6 +
 program/js/app.js                       |   68 ++++++++++++-
 13 files changed, 235 insertions(+), 16 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 9a03013..4fb3198 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG RoundCube Webmail
 ===========================
 
+- Implemented messages copying using drag&drop + SHIFT (#1484086)
 - Improved performance of folders operations (#1486525)
 - Fix blocked.gif attachment is not attached to the message (#1486516)
 - Managesieve: import from Horde-INGO
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index 1e3c09d..651ecd6 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -2202,6 +2202,13 @@
     $to_mbox = $this->mod_mailbox($to_mbox);
     $from_mbox = $from_mbox ? $this->mod_mailbox($from_mbox) : $this->mailbox;
 
+    // convert the list of uids to array
+    $a_uids = is_string($uids) ? explode(',', $uids) : (is_array($uids) ? $uids : NULL);
+
+    // exit if no message uids are specified
+    if (!is_array($a_uids) || empty($a_uids))
+      return false;
+
     // make sure mailbox exists
     if ($to_mbox != 'INBOX' && !$this->mailbox_exists($tbox))
       {
@@ -2210,13 +2217,6 @@
       else
         return false;
       }
-
-    // convert the list of uids to array
-    $a_uids = is_string($uids) ? explode(',', $uids) : (is_array($uids) ? $uids : NULL);
-
-    // exit if no message uids are specified
-    if (!is_array($a_uids) || empty($a_uids))
-      return false;
 
     // flag messages as read before moving them
     $config = rcmail::get_instance()->config;
@@ -2270,6 +2270,49 @@
 
 
   /**
+   * Copy a message from one mailbox to another
+   *
+   * @param string List of UIDs to copy, separated by comma
+   * @param string Target mailbox
+   * @param string Source mailbox
+   * @return boolean True on success, False on error
+   */
+  function copy_message($uids, $to_mbox, $from_mbox='')
+  {
+    $fbox = $from_mbox;
+    $tbox = $to_mbox;
+    $to_mbox = $this->mod_mailbox($to_mbox);
+    $from_mbox = $from_mbox ? $this->mod_mailbox($from_mbox) : $this->mailbox;
+
+    // convert the list of uids to array
+    $a_uids = is_string($uids) ? explode(',', $uids) : (is_array($uids) ? $uids : NULL);
+
+    // exit if no message uids are specified
+    if (!is_array($a_uids) || empty($a_uids))
+      return false;
+
+    // make sure mailbox exists
+    if ($to_mbox != 'INBOX' && !$this->mailbox_exists($tbox))
+      {
+      if (in_array($tbox, $this->default_folders))
+        $this->create_mailbox($tbox, true);
+      else
+        return false;
+      }
+
+    // copy messages
+    $iil_copy = iil_C_Copy($this->conn, join(',', $a_uids), $from_mbox, $to_mbox);
+    $copied = !($iil_copy === false || $iil_copy < 0);
+
+    if ($copied) {
+      $this->_clear_messagecount($to_mbox);
+    }
+
+    return $copied;
+  }
+
+
+  /**
    * Mark messages as deleted and expunge mailbox
    *
    * @param string List of UIDs to move, separated by comma
diff --git a/program/include/rcube_template.php b/program/include/rcube_template.php
index 6de3272..ad498c9 100755
--- a/program/include/rcube_template.php
+++ b/program/include/rcube_template.php
@@ -860,7 +860,7 @@
         if (!$attrib['href']) {
             $attrib['href'] = '#';
         }
-        if ($command) {
+        if ($command && !$attrib['onclick']) {
             $attrib['onclick'] = sprintf(
                 "return %s.command('%s','%s',this)",
                 JS_OBJECT_NAME,
diff --git a/program/js/app.js b/program/js/app.js
index 87ee76c..7c86bb0 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -197,7 +197,7 @@
         
         if (this.env.action=='show' || this.env.action=='preview')
           {
-          this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete',
+          this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'copy', 'delete',
             'open', 'mark', 'edit', 'viewsource', 'download', 'print', 'load-attachment', 'load-headers', true);
 
           if (this.env.next_uid)
@@ -672,6 +672,11 @@
           this.move_messages(props);
         else if (this.task == 'addressbook' && this.drag_active)
           this.copy_contact(null, props);
+        break;
+
+      case 'copy':
+        if (this.task == 'mail')
+          this.copy_messages(props);
         break;
 
       case 'mark':
@@ -1194,10 +1199,14 @@
 
     // handle mouse release when dragging
     if (this.drag_active && model && this.env.last_folder_target) {
+      var mbox = model[this.env.last_folder_target].id;
+
       $(this.get_folder_li(this.env.last_folder_target)).removeClass('droptarget');
-      this.command('moveto', model[this.env.last_folder_target].id);
       this.env.last_folder_target = null;
       list.draglayer.hide();
+
+      if (!this.drag_menu(e, mbox))
+        this.command('moveto', mbox);
     }
     
     // reset 'pressed' buttons
@@ -1207,6 +1216,29 @@
           this.button_out(this.buttons_sel[id], id);
       this.buttons_sel = {};
     }
+  };
+
+  this.drag_menu = function(e, mbox)
+  {
+    var modkey = rcube_event.get_modifier(e);
+    var menu = $('#'+this.gui_objects.message_dragmenu);
+
+    if (menu && modkey == SHIFT_KEY) {
+      var pos = rcube_event.get_mouse_pos(e);
+      this.env.drag_mbox = mbox;
+      menu.css({top: (pos.y-10)+'px', left: (pos.x-10)+'px'}).show();
+      return true;
+    }
+  };
+
+  this.drag_menu_action = function(action)
+  {
+    var menu = $('#'+this.gui_objects.message_dragmenu);
+    if (menu) {
+      menu.hide();
+    }
+    this.command(action, this.env.drag_mbox);
+    this.env.drag_mbox = null;
   };
 
   this.drag_start = function(list)
@@ -1389,12 +1421,12 @@
       {
       this.enable_command('reply', 'reply-all', 'forward', false);
       this.enable_command('show', 'print', 'open', 'edit', 'download', 'viewsource', selected);
-      this.enable_command('delete', 'moveto', 'mark', (list.selection.length > 0 ? true : false));
+      this.enable_command('delete', 'moveto', 'copy', 'mark', (list.selection.length > 0 ? true : false));
       }
     else
       {
       this.enable_command('show', 'reply', 'reply-all', 'forward', 'print', 'edit', 'open', 'download', 'viewsource', selected);
-      this.enable_command('delete', 'moveto', 'mark', (list.selection.length > 0 ? true : false));
+      this.enable_command('delete', 'moveto', 'copy', 'mark', (list.selection.length > 0 ? true : false));
       }
 
     // start timer for message preview (wait for double click)
@@ -2119,6 +2151,32 @@
       $(row.obj).addClass('unroot');
     else
       $(row.obj).removeClass('unroot');
+    };
+
+  // copy selected messages to the specified mailbox
+  this.copy_messages = function(mbox)
+    {
+    // exit if current or no mailbox specified or if selection is empty
+    if (!mbox || mbox == this.env.mailbox || (!this.env.uid && (!this.message_list || !this.message_list.get_selection().length)))
+      return;
+
+    var add_url = '&_target_mbox='+urlencode(mbox)+'&_from='+(this.env.action ? this.env.action : '');
+    var a_uids = new Array();
+
+    if (this.env.uid)
+      a_uids[0] = this.env.uid;
+    else
+    {
+      var selection = this.message_list.get_selection();
+      var id;
+      for (var n=0; n<selection.length; n++) {
+        id = selection[n];
+        a_uids[a_uids.length] = id;
+      }
+    }
+
+    // send request to server
+    this.http_post('copy', '_uid='+a_uids.join(',')+'&_mbox='+urlencode(this.env.mailbox)+add_url, false);
     };
 
   // move selected messages to the specified mailbox
@@ -4625,7 +4683,7 @@
           if (this.env.contentframe)
             this.show_contentframe(false);
           // disable commands useless when mailbox is empty
-          this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 
+          this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'copy', 'delete', 
             'mark', 'viewsource', 'open', 'edit', 'download', 'print', 'load-attachment', 
             'purge', 'expunge', 'select-all', 'select-none', 'sort',
             'expand-all', 'expand-unread', 'collapse-all', false);
diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc
index 3059692..1a1291c 100644
--- a/program/localization/en_US/labels.inc
+++ b/program/localization/en_US/labels.inc
@@ -59,6 +59,8 @@
 $labels['threadsfromto'] = 'Threads $from to $to of $count';
 $labels['messagenrof'] = 'Message $nr of $count';
 
+$labels['copy']     = 'Copy';
+$labels['move']     = 'Move';
 $labels['moveto']   = 'Move to...';
 $labels['download'] = 'Download';
 
diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc
index b0a812c..e8c5502 100644
--- a/program/localization/en_US/messages.inc
+++ b/program/localization/en_US/messages.inc
@@ -86,6 +86,7 @@
 $messages['sourceisreadonly'] = 'This address source is read only';
 $messages['errorsavingcontact'] = 'Could not save the contact address';
 $messages['movingmessage'] = 'Moving message...';
+$messages['copyingmessage'] = 'Copying message...';
 $messages['receiptsent'] = 'Successfully sent a read receipt';
 $messages['errorsendingreceipt'] = 'Could not send the receipt';
 $messages['nodeletelastidentity'] = 'You cannot delete this identity, it\'s your last one.';
diff --git a/program/localization/pl_PL/labels.inc b/program/localization/pl_PL/labels.inc
index b2897b2..d6af5ac 100644
--- a/program/localization/pl_PL/labels.inc
+++ b/program/localization/pl_PL/labels.inc
@@ -52,6 +52,8 @@
 $labels['messagesfromto'] = 'Wiadomości od $from do $to z $count';
 $labels['messagenrof'] = 'Wiadomość $nr z $count';
 $labels['moveto'] = 'Przenieś do...';
+$labels['move'] = 'Przenieś';
+$labels['copy'] = 'Kopiuj';
 $labels['download'] = 'Pobierz';
 $labels['filename'] = 'Nazwa pliku';
 $labels['filesize'] = 'Rozmiar pliku';
diff --git a/program/localization/pl_PL/messages.inc b/program/localization/pl_PL/messages.inc
index 314b585..641d1ef 100644
--- a/program/localization/pl_PL/messages.inc
+++ b/program/localization/pl_PL/messages.inc
@@ -6,7 +6,7 @@
  | language/pl_PL/messages.inc                                           |
  |                                                                       |
  | Language file of the RoundCube Webmail client                         |
- | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland                 |
+ | Copyright (C) 2005-2010, RoundCube Dev. - Switzerland                 |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  +-----------------------------------------------------------------------+
@@ -89,6 +89,7 @@
 $messages['sourceisreadonly'] = 'Źródło adresu jest tylko do odczytu';
 $messages['errorsavingcontact'] = 'Nie można było zapisać adresu kontaktu';
 $messages['movingmessage'] = 'Przenoszenie wiadomości...';
+$messages['copyingmessage'] = 'Kopiowanie wiadomości...';
 $messages['receiptsent'] = 'Pomyślnie wysłano potwierdzenie dostarczenia';
 $messages['errorsendingreceipt'] = 'Nie można wysłać potwierdzenia';
 $messages['nodeletelastidentity'] = 'Nie można skasować tej tożsamości, ponieważ jest ostatnią.';
diff --git a/program/steps/mail/copy.inc b/program/steps/mail/copy.inc
new file mode 100644
index 0000000..e2270a7
--- /dev/null
+++ b/program/steps/mail/copy.inc
@@ -0,0 +1,59 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/copy.inc                                           |
+ |                                                                       |
+ | This file is part of the RoundCube Webmail client                     |
+ | Copyright (C) 2005-2010, RoundCube Dev. - Switzerland                 |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ | PURPOSE:                                                              |
+ |   Copy the submitted messages to a specific mailbox                   |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: Aleksander Machniak <alec@alec.pl>                            |
+ +-----------------------------------------------------------------------+
+
+ $Id$
+
+*/
+
+// only process ajax requests
+if (!$OUTPUT->ajax_call)
+  return;
+
+// count messages before changing anything
+$old_count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL');
+$old_pages = ceil($old_count / $IMAP->page_size);
+
+// move messages
+if (!empty($_POST['_uid']) && !empty($_POST['_target_mbox'])) {
+    $count = sizeof(explode(',', ($uids = get_input_value('_uid', RCUBE_INPUT_POST))));
+    $target = get_input_value('_target_mbox', RCUBE_INPUT_POST);
+    $mbox = get_input_value('_mbox', RCUBE_INPUT_POST);
+
+    $copied = $IMAP->copy_message($uids, $target, $mbox);
+  
+    if (!$copied) {
+        // send error message
+	if ($_POST['_from'] != 'show')
+          $OUTPUT->command('list_mailbox');
+        $OUTPUT->show_message('errorcopying', 'error');
+        $OUTPUT->send();
+        exit;
+    }
+
+    rcmail_send_unread_count($target, true);
+
+    $OUTPUT->command('set_quota', rcmail_quota_content());
+}
+// unknown action or missing query param
+else {
+    exit;
+}
+
+// send response
+$OUTPUT->send();
+
+?>
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 09a2492..e8600ef 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -130,7 +130,8 @@
     $OUTPUT->set_env('junk_mailbox', $CONFIG['junk_mbox']);
 
   if (!$OUTPUT->ajax_call)
-    $OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash', 'movingmessage');
+    $OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash',
+      'movingmessage', 'copyingmessage', 'copy', 'move');
 
   $OUTPUT->set_pagetitle(rcmail_localize_foldername($mbox_name));
   }
@@ -1457,7 +1458,6 @@
   return false;
 }
 
-
 function rcmail_search_filter($attrib)
 {
   global $OUTPUT, $CONFIG;
@@ -1490,6 +1490,7 @@
   return $out;										
 }
 
+
 // register UI objects
 $OUTPUT->add_handlers(array(
   'mailboxlist' => 'rcmail_mailbox_list',
diff --git a/skins/default/common.css b/skins/default/common.css
index 5977087..c010128 100644
--- a/skins/default/common.css
+++ b/skins/default/common.css
@@ -349,6 +349,44 @@
   -webkit-box-shadow: #999 1px 1px 12px;
 }
 
+.popupmenu ul
+{
+  margin: -4px 0;
+  padding: 0;
+  list-style: none;
+}
+
+.popupmenu ul li
+{
+  font-size: 11px;
+  white-space: nowrap;
+  min-width: 100px;
+  margin: 3px -4px;
+}
+
+.popupmenu li a
+{
+  display: block;
+  color: #a0a0a0;
+  padding: 2px 10px;
+  text-decoration: none;
+  min-height: 14px;
+}
+
+.popupmenu li a.active,
+.popupmenu li a.active:active,
+.popupmenu li a.active:visited
+{
+  color: #333;
+}
+
+.popupmenu li a.active:hover
+{
+  color: #fff;
+  background-color: #c00;
+}
+
+    
 
 /***** common table settings ******/
 
diff --git a/skins/default/functions.js b/skins/default/functions.js
index 4ee2a9d..6d0641d 100644
--- a/skins/default/functions.js
+++ b/skins/default/functions.js
@@ -125,6 +125,7 @@
   this.searchmenu = $('#searchmenu');
   this.messagemenu = $('#messagemenu');
   this.listmenu = $('#listmenu');
+  this.dragmessagemenu = $('#dragmessagemenu');
 }
 
 rcube_mail_ui.prototype = {
@@ -259,6 +260,8 @@
     this.show_markmenu(false);
   else if (this.messagemenu && this.messagemenu.is(':visible') && target != rcube_find_object('messagemenulink'))
     this.show_messagemenu(false);
+  else if (this.dragmessagemenu && this.dragmessagemenu.is(':visible') && !rcube_mouse_is_over(evt, rcube_find_object('dragmessagemenu')))
+    this.dragmessagemenu.hide();
   else if (this.listmenu && this.listmenu.is(':visible') && target != rcube_find_object('listmenulink')) {
     var menu = rcube_find_object('listmenu');
     while (target.parentNode) {
@@ -290,6 +293,8 @@
       this.show_messagemenu(false);
     if (this.listmenu && this.listmenu.is(':visible'))
       this.show_listmenu(false);
+    if (this.dragmessagemenu && this.dragmessagemenu.is(':visible'))
+      this.dragmessagemenu.hide();
   }
 }
 
@@ -304,4 +309,5 @@
   rcube_event.add_listener({ object:rcmail_ui, method:'body_keypress', event:'keypress' });
   rcmail.addEventListener('menu-open', 'open_listmenu', rcmail_ui);
   rcmail.addEventListener('menu-save', 'save_listmenu', rcmail_ui);
+  rcmail.gui_object('message_dragmenu', 'dragmessagemenu');
 }
diff --git a/skins/default/templates/mail.html b/skins/default/templates/mail.html
index c66bc58..206f101 100644
--- a/skins/default/templates/mail.html
+++ b/skins/default/templates/mail.html
@@ -152,6 +152,13 @@
 <roundcube:button command="reset-search" id="searchreset" image="/images/icons/reset.gif" title="resetsearch" />
 </div>
 
+<div id="dragmessagemenu" class="popupmenu">
+  <ul>
+    <li><roundcube:button command="moveto" onclick="return rcmail.drag_menu_action('moveto')" label="move" classAct="active" /></li>
+    <li><roundcube:button command="copy" onclick="return rcmail.drag_menu_action('copy')" label="copy" classAct="active" /></li>
+  </ul>
+</div>
+
 <div id="listmenu" class="popupmenu">
 <fieldset class="thinbordered"><legend><roundcube:label name="listmode" /></legend>
   <ul class="toolbarmenu">

--
Gitblit v1.9.1