Aleksander Machniak
2012-06-28 669747a81c4b2ff823d1f20dc50899163c0a8a4a
Merge branch 'master' of github.com:roundcube/roundcubemail
9 files modified
228 ■■■■■ changed files
program/include/rcube_imap_cache.php 117 ●●●● patch | view | raw | blame | history
program/include/rcube_imap_generic.php 27 ●●●● patch | view | raw | blame | history
program/js/app.js 35 ●●●● patch | view | raw | blame | history
program/steps/addressbook/edit.inc 15 ●●●●● patch | view | raw | blame | history
skins/default/addressbook.css 7 ●●●●● patch | view | raw | blame | history
skins/default/templates/contactadd.html 1 ●●●● patch | view | raw | blame | history
skins/default/templates/contactedit.html 2 ●●● patch | view | raw | blame | history
skins/larry/addressbook.css 23 ●●●●● patch | view | raw | blame | history
skins/larry/templates/contactedit.html 1 ●●●● patch | view | raw | blame | history
program/include/rcube_imap_cache.php
@@ -516,7 +516,6 @@
                    .($uids !== null ? " AND uid IN (".$this->db->array2list((array)$uids, 'integer').")" : ""),
                $this->userid, $mailbox);
        }
    }
@@ -917,16 +916,15 @@
            return;
        }
        // NOTE: make sure the mailbox isn't selected, before
        // enabling QRESYNC and invoking SELECT
        if ($this->imap->conn->selected !== null) {
            $this->imap->conn->close();
        }
        // Enable QRESYNC
        $res = $this->imap->conn->enable($qresync ? 'QRESYNC' : 'CONDSTORE');
        if (!is_array($res)) {
        if ($res === false) {
            return;
        }
        // Close mailbox if already selected to get most recent data
        if ($this->imap->conn->selected == $mailbox) {
            $this->imap->conn->close();
        }
        // Get mailbox data (UIDVALIDITY, HIGHESTMODSEQ, counters, etc.)
@@ -952,8 +950,10 @@
            return;
        }
        // Get known uids
        $uids = array();
        $uids    = array();
        $removed = array();
        // Get known UIDs
        $sql_result = $this->db->query(
            "SELECT uid"
            ." FROM ".$this->db->table_name('cache_messages')
@@ -962,74 +962,69 @@
            $this->userid, $mailbox);
        while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
          $uids[] = $sql_arr['uid'];
            $uids[] = $sql_arr['uid'];
        }
        // No messages in database, nothing to sync
        if (empty($uids)) {
            return;
        }
        // Synchronize messages data
        if (!empty($uids)) {
            // Get modified flags and vanished messages
            // UID FETCH 1:* (FLAGS) (CHANGEDSINCE 0123456789 VANISHED)
            $result = $this->imap->conn->fetch($mailbox,
                $uids, true, array('FLAGS'), $index['modseq'], $qresync);
        // Get modified flags and vanished messages
        // UID FETCH 1:* (FLAGS) (CHANGEDSINCE 0123456789 VANISHED)
        $result = $this->imap->conn->fetch($mailbox,
            !empty($uids) ? $uids : '1:*', true, array('FLAGS'),
            $index['modseq'], $qresync);
        $invalidated = false;
        if (!empty($result)) {
            foreach ($result as $id => $msg) {
                $uid = $msg->uid;
                // Remove deleted message
                if ($this->skip_deleted && !empty($msg->flags['DELETED'])) {
                    $this->remove_message($mailbox, $uid);
                    if (!$invalidated) {
                        $invalidated = true;
                        // Invalidate thread indexes (?)
                        $this->remove_thread($mailbox);
            if (!empty($result)) {
                foreach ($result as $id => $msg) {
                    $uid = $msg->uid;
                    // Remove deleted message
                    if ($this->skip_deleted && !empty($msg->flags['DELETED'])) {
                        $removed[] = $uid;
                        // Invalidate index
                        $index['valid'] = false;
                        continue;
                    }
                    continue;
                }
                $flags = 0;
                if (!empty($msg->flags)) {
                    foreach ($this->flags as $idx => $flag)
                        if (!empty($msg->flags[$flag]))
                            $flags += $idx;
                }
                    $flags = 0;
                    if (!empty($msg->flags)) {
                        foreach ($this->flags as $idx => $flag) {
                            if (!empty($msg->flags[$flag])) {
                                $flags += $idx;
                            }
                        }
                    }
                $this->db->query(
                    "UPDATE ".$this->db->table_name('cache_messages')
                    ." SET flags = ?, changed = ".$this->db->now()
                    ." WHERE user_id = ?"
                        ." AND mailbox = ?"
                        ." AND uid = ?"
                        ." AND flags <> ?",
                    $flags, $this->userid, $mailbox, $uid, $flags);
                    $this->db->query(
                        "UPDATE ".$this->db->table_name('cache_messages')
                        ." SET flags = ?, changed = ".$this->db->now()
                        ." WHERE user_id = ?"
                            ." AND mailbox = ?"
                            ." AND uid = ?"
                            ." AND flags <> ?",
                        $flags, $this->userid, $mailbox, $uid, $flags);
                }
            }
        }
        // Get VANISHED
        if ($qresync) {
            $mbox_data = $this->imap->folder_data($mailbox);
            // VANISHED found?
            if ($qresync) {
                $mbox_data = $this->imap->folder_data($mailbox);
            // Removed messages
            if (!empty($mbox_data['VANISHED'])) {
                // Removed messages found
                $uids = rcube_imap_generic::uncompressMessageSet($mbox_data['VANISHED']);
                if (!empty($uids)) {
                    // remove messages from database
                    $this->remove_message($mailbox, $uids);
                    // Invalidate thread indexes (?)
                    $this->remove_thread($mailbox);
                    $removed = array_merge($removed, $uids);
                    // Invalidate index
                    $index['valid'] = false;
                }
            }
            // remove messages from database
            if (!empty($removed)) {
                $this->remove_message($mailbox, $removed);
            }
        }
        // Invalidate thread index (?)
        if (!$index['valid']) {
            $this->remove_thread($mailbox);
        }
        $sort_field = $index['sort_field'];
program/include/rcube_imap_generic.php
@@ -1472,14 +1472,31 @@
     */
    function enable($extension)
    {
        if (empty($extension))
        if (empty($extension)) {
            return false;
        }
        if (!$this->hasCapability('ENABLE'))
        if (!$this->hasCapability('ENABLE')) {
            return false;
        }
        if (!is_array($extension))
        if (!is_array($extension)) {
            $extension = array($extension);
        }
        if (!empty($this->extensions_enabled)) {
            // check if all extensions are already enabled
            $diff = array_diff($extension, $this->extensions_enabled);
            if (empty($diff)) {
                return $extension;
            }
            // Make sure the mailbox isn't selected, before enabling extension(s)
            if ($this->selected !== null) {
                $this->close();
            }
        }
        list($code, $response) = $this->execute('ENABLE', $extension);
@@ -1487,7 +1504,9 @@
            $response = substr($response, 10); // remove prefix "* ENABLED "
            $result   = (array) $this->tokenizeResponse($response);
            return $result;
            $this->extensions_enabled = array_unique(array_merge((array)$this->extensions_enabled, $result));
            return $this->extensions_enabled;
        }
        return false;
program/js/app.js
@@ -4715,11 +4715,11 @@
  {
    if (form && form.elements._photo.value) {
      this.async_upload_form(form, 'upload-photo', function(e) {
        rcmail.set_busy(false, null, rcmail.photo_upload_id);
        rcmail.set_busy(false, null, rcmail.file_upload_id);
      });
      // display upload indicator
      this.photo_upload_id = this.set_busy(true, 'uploading');
      this.file_upload_id = this.set_busy(true, 'uploading');
    }
  };
@@ -4734,8 +4734,8 @@
  this.photo_upload_end = function()
  {
    this.set_busy(false, null, this.photo_upload_id);
    delete this.photo_upload_id;
    this.set_busy(false, null, this.file_upload_id);
    delete this.file_upload_id;
  };
  this.set_photo_actions = function(id)
@@ -6254,7 +6254,7 @@
    // prepare multipart form data composition
    var files = e.target.files || e.dataTransfer.files,
      formdata = window.FormData ? new FormData() : null,
      fieldname = this.env.filedrop.fieldname || '_file',
      fieldname = (this.env.filedrop.fieldname || '_file') + (this.env.filedrop.single ? '' : '[]'),
      boundary = '------multipartformboundary' + (new Date).getTime(),
      dashdash = '--', crlf = '\r\n',
      multipart = dashdash + boundary + crlf;
@@ -6269,7 +6269,8 @@
        content = '<span>' + (multiple ? ref.get_label('uploadingmany') : files[0].name) + '</span>';
      // add to attachments list
      ref.add2attachment_list(ts, { name:'', html:content, classname:'uploading', complete:false });
      if (!ref.add2attachment_list(ts, { name:'', html:content, classname:'uploading', complete:false }))
        ref.file_upload_id = ref.set_busy(true, 'uploading');
      // complete multipart content and post request
      multipart += dashdash + boundary + dashdash + crlf;
@@ -6277,7 +6278,7 @@
      $.ajax({
        type: 'POST',
        dataType: 'json',
        url: ref.url(ref.env.filedrop.action||'upload', { _id:ref.env.compose_id||'', _uploadid:ts, _remote:1 }),
        url: ref.url(ref.env.filedrop.action||'upload', { _id:ref.env.compose_id||ref.env.cid||'', _uploadid:ts, _remote:1 }),
        contentType: formdata ? false : 'multipart/form-data; boundary=' + boundary,
        processData: false,
        data: formdata || multipart,
@@ -6289,7 +6290,7 @@
    // get contents of all dropped files
    var last = this.env.filedrop.single ? 0 : files.length - 1;
    for (var i=0, f; i <= last && (f = files[i]); i++) {
    for (var j=0, i=0, f; j <= last && (f = files[i]); i++) {
      if (!f.name) f.name = f.fileName;
      if (!f.size) f.size = f.fileSize;
      if (!f.type) f.type = 'application/octet-stream';
@@ -6306,8 +6307,8 @@
      // do it the easy way with FormData (FF 4+, Chrome 5+, Safari 5+)
      if (formdata) {
        formdata.append(fieldname + '[]', f);
        if (i == last)
        formdata.append(fieldname, f);
        if (j == last)
          return submit_data();
      }
      // use FileReader supporetd by Firefox 3.6
@@ -6315,33 +6316,35 @@
        var reader = new FileReader();
        // closure to pass file properties to async callback function
        reader.onload = (function(file, i) {
        reader.onload = (function(file, j) {
          return function(e) {
            multipart += 'Content-Disposition: form-data; name="' + fieldname + '[]"';
            multipart += 'Content-Disposition: form-data; name="' + fieldname + '"';
            multipart += '; filename="' + (f.name_bin || file.name) + '"' + crlf;
            multipart += 'Content-Length: ' + file.size + crlf;
            multipart += 'Content-Type: ' + file.type + crlf + crlf;
            multipart += e.target.result + crlf;
            multipart += dashdash + boundary + crlf;
            if (i == last)  // we're done, submit the data
            if (j == last)  // we're done, submit the data
              return submit_data();
          }
        })(f,i);
        })(f,j);
        reader.readAsBinaryString(f);
      }
      // Firefox 3
      else if (f.getAsBinary) {
        multipart += 'Content-Disposition: form-data; name="' + fieldname + '[]"';
        multipart += 'Content-Disposition: form-data; name="' + fieldname + '"';
        multipart += '; filename="' + (f.name_bin || f.name) + '"' + crlf;
        multipart += 'Content-Length: ' + f.size + crlf;
        multipart += 'Content-Type: ' + f.type + crlf + crlf;
        multipart += f.getAsBinary() + crlf;
        multipart += dashdash + boundary +crlf;
        if (i == last)
        if (j == last)
          return submit_data();
      }
      j++;
    }
  };
program/steps/addressbook/edit.inc
@@ -262,12 +262,27 @@
}
/**
 * Register container as active area to drop photos onto
 */
function rcmail_photo_drop_area($attrib)
{
    global $OUTPUT;
    if ($attrib['id']) {
        $OUTPUT->add_gui_object('filedrop', $attrib['id']);
        $OUTPUT->set_env('filedrop', array('action' => 'upload-photo', 'fieldname' => '_photo', 'single' => 1, 'filter' => '^image/.+'));
    }
}
$OUTPUT->add_handlers(array(
    'contactedithead' => 'rcmail_contact_edithead',
    'contacteditform' => 'rcmail_contact_editform',
    'contactphoto'    => 'rcmail_contact_photo',
    'photouploadform' => 'rcmail_upload_photo_form',
    'sourceselector'  => 'rcmail_source_selector',
    'filedroparea'    => 'rcmail_photo_drop_area',
));
if ($RCMAIL->action == 'add' && $OUTPUT->template_exists('contactadd'))
skins/default/addressbook.css
@@ -310,6 +310,13 @@
    width: 60px;
}
#contactpic.droptarget.hover {
    background-color: #f0f0ee;
    box-shadow: 0 0 5px 0 #999;
    -moz-box-shadow: 0 0 5px 0 #999;
    -o-box-shadow: 0 0 5px 0 #999;
}
#contactphoto .formlinks
{
    margin-top: 0.5em;
skins/default/templates/contactadd.html
@@ -33,6 +33,7 @@
</div>
<roundcube:object name="photoUploadForm" id="upload-form" size="30" class="popupmenu" />
<roundcube:object name="fileDropArea" id="contactpic" />
<script type="text/javascript">rcube_init_tabs('contacttabs')</script>
skins/default/templates/contactedit.html
@@ -22,7 +22,6 @@
  </div>
  <roundcube:object name="contactedithead" id="contacthead" size="16" form="editform" />
  <div style="clear:both"></div>
  <div id="contacttabs">
    <roundcube:object name="contacteditform" size="40" textareacols="60" deleteIcon="/images/icons/delete.png" form="editform" />
  </div>
@@ -34,6 +33,7 @@
</div>
<roundcube:object name="photoUploadForm" id="upload-form" size="30" class="popupmenu" />
<roundcube:object name="fileDropArea" id="contactpic" />
<script type="text/javascript">rcube_init_tabs('contacttabs')</script>
skins/larry/addressbook.css
@@ -160,6 +160,29 @@
#contactpic img {
    width: 112px;
    visibility: inherit;
}
#contactpic.droptarget {
    background-image: url(images/filedrop.png);
    background-position: center;
    background-repeat: no-repeat;
}
#contactpic.droptarget.hover {
    background-color: #d9ecf4;
    box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9);
    -moz-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9);
    -webkit-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9);
    -o-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9);
}
#contactpic.droptarget.active img {
    opacity: 0.15;
}
#contactpic.droptarget.hover img {
    opacity: 0.05;
}
#contacthead {
skins/larry/templates/contactedit.html
@@ -19,6 +19,7 @@
    <div id="contactphoto">
        <roundcube:object name="contactphoto" id="contactpic" placeholder="/images/contactpic.png" />
        <roundcube:if condition="env:photocol" />
        <roundcube:object name="fileDropArea" id="contactpic" />
        <div class="formlinks">
            <roundcube:button command="upload-photo" id="uploadformlink" type="link" label="replacephoto" class="iconlink upload disabled" classAct="iconlink upload active" onclick="UI.show_uploadform();return false" condition="env:photocol" /><br/>
            <roundcube:button command="delete-photo" type="link" label="delete" class="iconlink delete disabled" classAct="iconlink delete active" condition="env:photocol" />