diff options
author | Ajay Garg <ajay@activitycentral.com> | 2012-08-26 11:34:32 (GMT) |
---|---|---|
committer | Ajay Garg <ajay@activitycentral.com> | 2012-08-26 11:34:32 (GMT) |
commit | 55db4d555690d3ef1700d9e19eb999c95ddb2f3d (patch) | |
tree | 6669c03a2698fd9c45c049c6ce3176a5ebee9a83 | |
parent | f51090169d96f46ba3fcee16baeb467b6159dabc (diff) |
1-to-N feature via School Server
-rw-r--r-- | rpms/sugar/0130-1-to-N-feature-via-School-Server.patch | 1426 |
1 files changed, 1426 insertions, 0 deletions
diff --git a/rpms/sugar/0130-1-to-N-feature-via-School-Server.patch b/rpms/sugar/0130-1-to-N-feature-via-School-Server.patch new file mode 100644 index 0000000..b2babe3 --- /dev/null +++ b/rpms/sugar/0130-1-to-N-feature-via-School-Server.patch @@ -0,0 +1,1426 @@ +From 5a3da2cb5bea8c15b2cbc0ba76d1cb96e4beaced Mon Sep 17 00:00:00 2001 +From: Ajay Garg <ajay@activitycentral.com> +Date: Sun, 26 Aug 2012 14:53:11 +0530 +Subject: [sugar PATCH] 1-to-N feature via School Server +Organization: Sugar Labs Foundation +Signed-off-by: Ajay Garg <ajay@activitycentral.com> +--- + + + + src/jarabe/journal/journalactivity.py | 17 ++- + src/jarabe/journal/journaltoolbox.py | 44 +++++- + src/jarabe/journal/model.py | 285 +++++++++++++++++++++------------ + src/jarabe/journal/palettes.py | 201 ++++++++++++++++++++---- + src/jarabe/journal/volumestoolbar.py | 41 ++--- + src/jarabe/journal/webdavmanager.py | 146 ++++++++++------- + src/jarabe/view/palettes.py | 31 +---- + src/webdav/Connection.py | 11 +- + src/webdav/WebdavClient.py | 16 ++- + src/webdav/davlib.py | 9 +- + 10 files changed, 536 insertions(+), 265 deletions(-) + +diff --git a/src/jarabe/journal/journalactivity.py b/src/jarabe/journal/journalactivity.py +index fc4773d..5f2a734 100644 +--- a/src/jarabe/journal/journalactivity.py ++++ b/src/jarabe/journal/journalactivity.py +@@ -180,10 +180,14 @@ class JournalActivity(JournalWindow): + self._check_available_space() + + def __volume_error_cb(self, gobject, message, severity): +- alert = ErrorAlert(title=severity, msg=message) +- alert.connect('response', self.__alert_response_cb) +- self.add_alert(alert) +- alert.show() ++ self.update_title_and_message(self._error_alert, severity, ++ message) ++ self._callback = None ++ self._data = None ++ self.update_alert(self._error_alert) ++ ++ def _show_alert(self, message, severity): ++ self.__volume_error_cb(None, message, severity) + + def _volume_error_cb(self, gobject, message, severity): + self.update_error_alert(severity, message, None, None) +@@ -449,6 +453,8 @@ class JournalActivity(JournalWindow): + self.remove_alert(self._current_alert) + self.add_alert(alert) + ++ self.remove_alert(self._current_alert) ++ self.add_alert(alert) + self._current_alert = alert + self._current_alert.show() + show_normal_cursor() +@@ -475,6 +481,9 @@ class JournalActivity(JournalWindow): + self._data = data + self.update_alert(self._confirmation_alert) + ++ def update_progress(self, fraction): ++ self.get_toolbar_box().update_progress(fraction) ++ + def get_metadata_list(self, selected_state): + metadata_list = [] + +diff --git a/src/jarabe/journal/journaltoolbox.py b/src/jarabe/journal/journaltoolbox.py +index 6b2494e..b1c0cac 100644 +--- a/src/jarabe/journal/journaltoolbox.py ++++ b/src/jarabe/journal/journaltoolbox.py +@@ -78,6 +78,16 @@ class MainToolbox(Toolbox): + self.add_toolbar(_('Search'), self.search_toolbar) + self.search_toolbar.show() + ++ self._info_widget = MultiSelectEntriesInfoWidget() ++ self.add(self._info_widget) ++ self._info_widget.hide() ++ ++ def update_progress(self, fraction): ++ self._info_widget.update_progress(fraction) ++ ++ def hide_info_widget(self): ++ self._info_widget.hide() ++ + + class SearchToolbar(gtk.Toolbar): + __gtype_name__ = 'SearchToolbar' +@@ -532,6 +542,9 @@ class EditToolbox(Toolbox): + def get_current_entry_number(self): + return self.edit_toolbar._get_current_entry_number() + ++ def update_progress(self, fraction): ++ self.edit_toolbar.get_multi_select_info_widget().update_progress(fraction) ++ + + class EditToolbar(gtk.Toolbar): + def __init__(self): +@@ -552,6 +565,9 @@ class EditToolbar(gtk.Toolbar): + + self.show_all() + ++ def get_multi_select_info_widget(self): ++ return self._multi_select_info_widget ++ + def _set_total_number_of_entries(self, total): + self._multi_select_info_widget.set_total_number_of_entries(total) + +@@ -703,16 +719,40 @@ class MultiSelectEntriesInfoWidget(gtk.ToolItem): + def __init__(self): + gtk.ToolItem.__init__(self) + ++ self._box = gtk.VBox() + self._selected_entries = 0 + + self._label = gtk.Label() +- self.add(self._label) ++ self._box.pack_start(self._label, expand=False) ++ ++ self._progress_label = gtk.Label() ++ self._box.pack_start(self._progress_label, expand=False) ++ ++ self.add(self._box) + + self.show_all() ++ self._box.show_all() ++ self._progress_label.hide() + + def set_total_number_of_entries(self, total): + self._total = total + ++ def update_progress(self, fraction): ++ percent = '%.02f' % (fraction * 100) ++ ++ # TRANS: Do not translate %.02f ++ text = '%.02f%% complete' % (fraction * 100) ++ if (str(percent) != '100.00') and (str(percent).endswith('00')): ++ self._progress_label.set_text(text) ++ self._progress_label.show() ++ self.show_all() ++ gtk.gdk.window_process_all_updates() ++ else: ++ self._progress_label.hide() ++ from jarabe.journal.journalactivity import get_journal ++ if not get_journal().is_editing_mode_present(): ++ self.hide() ++ + def update_text(self, primary_text, secondary_text, special_action, + update_selected_entries): + # If "special_action" is None, +@@ -749,6 +789,8 @@ class MultiSelectEntriesInfoWidget(gtk.ToolItem): + self._label.set_text(message) + self._label.show() + ++ gtk.gdk.window_process_all_updates() ++ + def get_current_entry_number(self): + return self._selected_entries + +diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py +index 422e947..cc17956 100644 +--- a/src/jarabe/journal/model.py ++++ b/src/jarabe/journal/model.py +@@ -43,7 +43,7 @@ from sugar import dispatch + from sugar import mime + from sugar import util + +-from jarabe.journal.webdavmanager import get_remote_webdav_share_metadata ++from jarabe.journal import webdavmanager + + DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore' + DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore' +@@ -58,6 +58,7 @@ PROPERTIES = ['activity', 'activity_id', 'buddies', 'bundle_id', + MIN_PAGES_TO_CACHE = 3 + MAX_PAGES_TO_CACHE = 5 + ++WEBDAV_MOUNT_POINT = '/tmp' + JOURNAL_METADATA_DIR = '.Sugar-Metadata' + + _datastore = None +@@ -66,6 +67,43 @@ updated = dispatch.Signal() + deleted = dispatch.Signal() + + ++SCHOOL_SERVER_IP_ADDRESS_OR_DNS_NAME_PATH = \ ++ '/desktop/sugar/network/school_server_ip_address_or_dns_name' ++ ++client = gconf.client_get_default() ++SCHOOL_SERVER_IP_ADDRESS_OR_DNS_NAME = \ ++ client.get_string(SCHOOL_SERVER_IP_ADDRESS_OR_DNS_NAME_PATH) ++ ++ ++def _get_mount_point(path): ++ dir_path = os.path.dirname(path) ++ while dir_path: ++ if os.path.ismount(dir_path): ++ return dir_path ++ else: ++ dir_path = dir_path.rsplit(os.sep, 1)[0] ++ return None ++ ++ ++def extract_ip_address_or_dns_name_from_locally_mounted_remote_share_path(path): ++ """ ++ Path is of type :: ++ ++ /tmp/1.2.3.4/webdav/a.txt; OR ++ /tmp/this.is.dns.name/a.txt ++ """ ++ return path.split('/')[2] ++ ++ ++def is_mount_point_for_locally_mounted_remote_share(mount_point): ++ """ ++ The mount-point can be either of the ip-Address, or the DNS name. ++ More importantly, whatever the "name" be, it does NOT have a ++ forward-slash. ++ """ ++ return mount_point.find('/') == -1 ++ ++ + class _Cache(object): + + __gtype_name__ = 'model_Cache' +@@ -430,8 +468,8 @@ class InplaceResultSet(BaseResultSet): + + + class RemoteShareResultSet(object): +- def __init__(self, ip_address, query): +- self._ip_address = ip_address ++ def __init__(self, ip_address_or_dns_name, query): ++ self._ip_address_or_dns_name = ip_address_or_dns_name + self._file_list = [] + + self.ready = dispatch.Signal() +@@ -464,7 +502,11 @@ class RemoteShareResultSet(object): + self._sort = query.get('order_by', ['+timestamp'])[0] + + def setup(self): +- metadata_list_complete = get_remote_webdav_share_metadata(self._ip_address) ++ try: ++ metadata_list_complete = webdavmanager.get_remote_webdav_share_metadata(self._ip_address_or_dns_name) ++ except Exception, e: ++ metadata_list_complete = [] ++ + for metadata in metadata_list_complete: + + add_to_list = False +@@ -557,11 +599,6 @@ def _get_file_metadata(path, stat, fetch_preview=True): + dir_path = os.path.dirname(path) + metadata = _get_file_metadata_from_json(dir_path, filename, fetch_preview) + if metadata: +- # For Documents/Shares/Mounted-Drives. +- # Special case: for locally-mounted-remote-files, ensure that +- # "metadata['filesize' is already present before-hand. This +- # will have to be done at the time of fetching +- # webdav-properties per resource. + if 'filesize' not in metadata: + if stat is not None: + metadata['filesize'] = stat.st_size +@@ -678,56 +715,13 @@ def find(query_, page_size): + return InplaceResultSet(query, page_size, mount_points[0]) + + +-def is_mount_point_for_locally_mounted_remote_share(mount_point): +- import re +- +- pattern = '[1-9][0-9]{0,2}\.[0-9]{0,3}\.[0-9]{0,3}\.[0-9]{0,3}' +- if re.match(pattern, mount_point) is None: +- return False +- return True +- +- +-def _get_mount_point(path): +- dir_path = os.path.dirname(path) +- while dir_path: +- if os.path.ismount(dir_path): +- return dir_path +- else: +- dir_path = dir_path.rsplit(os.sep, 1)[0] +- return None +- +- +-def is_locally_mounted_remote_share(path): +- return string.find(path, '/tmp/') == 0 +- +- +-def extract_ip_address_from_locally_mounted_remote_share_path(path): +- """ +- Path is of type :: +- +- /tmp/127.0.0.1/webdav/a.txt +- """ +- return path.split('/')[2] +- +- + def get(object_id): + """Returns the metadata for an object + """ + if (object_id[0] == '/'): +- """ +- For Documents/Shares/Mounted-Drives/Locally-Mounted-Remote-Shares, +- where ".Sugar-Metadata" folder exists. +- +- The only thing is that, for locally-mounted-remote-shares, the +- "file" is not physically present. +- """ + if os.path.exists(object_id): +- # if the file is physically present, derive filesize-metadata +- # by physical examination of the file. + stat = os.stat(object_id) + else: +- # if the file is remote, derive file-metadata by fetching +- # properties remotely (webdav properties). + stat = None + + metadata = _get_file_metadata(object_id, stat) +@@ -812,7 +806,21 @@ def delete(object_id): + def copy(metadata, mount_point): + """Copies an object to another mount point + """ ++ # In all cases (except one), "copy" means the actual duplication of ++ # the content. ++ # Only in case of remote downloading, the content is first copied ++ # to "/tmp" folder. In those cases, copying would refer to a mere ++ # renaming. ++ transfer_ownership = False ++ ++ from jarabe.journal.journalactivity import get_mount_point ++ current_mount_point = get_mount_point() ++ ++ if is_mount_point_for_locally_mounted_remote_share(current_mount_point): ++ transfer_ownership = True ++ + metadata = get(metadata['uid']) ++ + if mount_point == '/' and metadata['icon-color'] == '#000000,#ffffff': + client = gconf.client_get_default() + metadata['icon-color'] = client.get_string('/desktop/sugar/user/color') +@@ -823,7 +831,7 @@ def copy(metadata, mount_point): + metadata['mountpoint'] = mount_point + del metadata['uid'] + +- return write(metadata, file_path, transfer_ownership=False) ++ return write(metadata, file_path, transfer_ownership=transfer_ownership) + + + def write(metadata, file_path='', update_mtime=True, transfer_ownership=True): +@@ -845,13 +853,69 @@ def write(metadata, file_path='', update_mtime=True, transfer_ownership=True): + object_id = _get_datastore().create(dbus.Dictionary(metadata), + file_path, + transfer_ownership) +- else: +- # HACK: For documents: modify the mount-point +- from jarabe.journal.journalactivity import get_mount_point +- if get_mount_point() == get_documents_path(): +- metadata['mountpoint'] = get_documents_path() ++ elif metadata.get('mountpoint', '/') == WEBDAV_MOUNT_POINT: ++ filename = metadata['title'] ++ ++ ip_address_or_dns_name = SCHOOL_SERVER_IP_ADDRESS_OR_DNS_NAME ++ webdavmanager.get_remote_webdav_share_metadata(ip_address_or_dns_name) ++ ++ data_webdav_manager = \ ++ webdavmanager.get_data_webdav_manager(ip_address_or_dns_name) ++ metadata_webdav_manager = \ ++ webdavmanager.get_metadata_webdav_manager(ip_address_or_dns_name) ++ ++ ++ # If we get a resource by this name, there is already an entry ++ # on the server with this name; we do not want to do any ++ # overwrites. ++ data_resource = webdavmanager.get_resource_by_resource_key(data_webdav_manager, ++ '/webdav/' + filename) ++ metadata_resource = webdavmanager.get_resource_by_resource_key(metadata_webdav_manager, ++ '/webdav/.Sugar-Metadata/' + filename + '.metadata') ++ if (data_resource is not None) or (metadata_resource is not None): ++ raise Exception(_('Entry already present on the server with ' ++ 'this name. Try again after renaming.')) ++ ++ # No entry for this name present. ++ # So, first write the metadata- and preview-file to temporary ++ # locations. ++ metadata_file_path, preview_file_path = \ ++ _write_metadata_and_preview_files_and_return_file_paths(metadata, ++ filename) ++ ++ # Finally, ++ # Upload the data file. ++ webdavmanager.add_resource_by_resource_key(data_webdav_manager, ++ filename, ++ file_path) ++ ++ # Upload the preview file. ++ if preview_file_path is not None: ++ webdavmanager.add_resource_by_resource_key(metadata_webdav_manager, ++ filename + '.preview', ++ preview_file_path) ++ ++ # Upload the metadata file. ++ # ++ # Note that this needs to be the last step. If there was any ++ # error uploading the data- or the preview-file, control would ++ # not reach here. ++ # ++ # In other words, the control reaches here only if the data- ++ # and the preview- files have been uploaded. Finally, IF this ++ # file is successfully uploaded, we have the guarantee that all ++ # files for a particular journal entry are in place. ++ webdavmanager.add_resource_by_resource_key(metadata_webdav_manager, ++ filename + '.metadata', ++ metadata_file_path) ++ ++ ++ object_id = 'doesn\'t matter' + +- object_id = _write_entry_on_external_device(metadata, file_path) ++ else: ++ object_id = _write_entry_on_external_device(metadata, ++ file_path, ++ transfer_ownership) + + return object_id + +@@ -876,41 +940,17 @@ def _rename_entry_on_external_device(file_path, destination_path, + 'for file=%s', ofile, old_fname) + + +-def _write_entry_on_external_device(metadata, file_path): +- """Create and update an entry copied from the +- DS to an external storage device. +- +- Besides copying the associated file a file for the preview +- and one for the metadata are stored in the hidden directory +- .Sugar-Metadata. +- +- This function handles renames of an entry on the +- external device and avoids name collisions. Renames are +- handled failsafe. +- +- """ +- if 'uid' in metadata and os.path.exists(metadata['uid']): +- file_path = metadata['uid'] +- +- if not file_path or not os.path.exists(file_path): +- raise ValueError('Entries without a file cannot be copied to ' +- 'removable devices') +- +- if not metadata.get('title'): +- metadata['title'] = _('Untitled') +- file_name = get_file_name(metadata['title'], metadata['mime_type']) +- +- destination_path = os.path.join(metadata['mountpoint'], file_name) +- if destination_path != file_path: +- file_name = get_unique_file_name(metadata['mountpoint'], file_name) +- destination_path = os.path.join(metadata['mountpoint'], file_name) +- clean_name, extension_ = os.path.splitext(file_name) +- metadata['title'] = clean_name +- ++def _write_metadata_and_preview_files_and_return_file_paths(metadata, ++ file_name): + metadata_copy = metadata.copy() + metadata_copy.pop('mountpoint', None) + metadata_copy.pop('uid', None) +- metadata_copy.pop('filesize', None) ++ ++ ++ # For copying to School-Server, we need to retain this property. ++ # Else wise, I have no idea why this property is being removed !! ++ if metadata.get('mountpoint', '/') != WEBDAV_MOUNT_POINT: ++ metadata_copy.pop('filesize', None) + + metadata_dir_path = os.path.join(metadata['mountpoint'], + JOURNAL_METADATA_DIR) +@@ -939,25 +979,58 @@ def _write_entry_on_external_device(metadata, file_path): + os.close(fh) + os.rename(fn, os.path.join(metadata_dir_path, preview_fname)) + +- if not os.path.dirname(destination_path) == os.path.dirname(file_path): +- shutil.copy(file_path, destination_path) ++ metadata_destination_path = os.path.join(metadata_dir_path, file_name + '.metadata') ++ if preview: ++ preview_destination_path = os.path.join(metadata_dir_path, preview_fname) + else: +- _rename_entry_on_external_device(file_path, destination_path, +- metadata_dir_path) ++ preview_destination_path = None ++ ++ return (metadata_destination_path, preview_destination_path) ++ ++ ++def _write_entry_on_external_device(metadata, file_path, ++ transfer_ownership): ++ """Create and update an entry copied from the ++ DS to an external storage device. + +- # For "Shares" folder, we need to set the permissions of the newly +- # copied file to 0777, else it will not be accessible by "httpd" +- # service. +- if metadata['mountpoint'] == '/var/www/web1/web': +- fd = os.open(destination_path, os.O_RDONLY) +- os.fchmod(fd, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) +- os.close(fd) ++ Besides copying the associated file a file for the preview ++ and one for the metadata are stored in the hidden directory ++ .Sugar-Metadata. + +- metadata_file_path = os.path.join(metadata_dir_path, file_name + '.metadata') +- fd = os.open(metadata_file_path, os.O_RDONLY) +- os.fchmod(fd, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) +- os.close(fd) ++ This function handles renames of an entry on the ++ external device and avoids name collisions. Renames are ++ handled failsafe. + ++ """ ++ if 'uid' in metadata and os.path.exists(metadata['uid']): ++ file_path = metadata['uid'] ++ ++ if not file_path or not os.path.exists(file_path): ++ raise ValueError('Entries without a file cannot be copied to ' ++ 'removable devices') ++ ++ if not metadata.get('title'): ++ metadata['title'] = _('Untitled') ++ file_name = get_file_name(metadata['title'], metadata['mime_type']) ++ ++ destination_path = os.path.join(metadata['mountpoint'], file_name) ++ if destination_path != file_path: ++ file_name = get_unique_file_name(metadata['mountpoint'], file_name) ++ destination_path = os.path.join(metadata['mountpoint'], file_name) ++ clean_name, extension_ = os.path.splitext(file_name) ++ metadata['title'] = clean_name ++ ++ _write_metadata_and_preview_files_and_return_file_paths(metadata, ++ file_name) ++ metadata_dir_path = os.path.join(metadata['mountpoint'], ++ JOURNAL_METADATA_DIR) ++ ++ if (os.path.dirname(destination_path) == os.path.dirname(file_path)) or \ ++ (transfer_ownership == True): ++ _rename_entry_on_external_device(file_path, destination_path, ++ metadata_dir_path) ++ else: ++ shutil.copy(file_path, destination_path) + + object_id = destination_path + created.send(None, object_id=object_id) +diff --git a/src/jarabe/journal/palettes.py b/src/jarabe/journal/palettes.py +index 66dcadc..dc3a691 100644 +--- a/src/jarabe/journal/palettes.py ++++ b/src/jarabe/journal/palettes.py +@@ -30,6 +30,7 @@ import gio + import glib + import time + import socket ++import dbus + + from sugar import _sugarext + +@@ -44,14 +45,13 @@ from jarabe.model import friends + from jarabe.model import filetransfer + from jarabe.model import mimeregistry + from jarabe.journal import misc +-from jarabe.journal import model ++from jarabe.journal import model, webdavmanager + from jarabe.journal.journalwindow import freeze_ui, \ + unfreeze_ui, \ + show_normal_cursor, \ + show_waiting_cursor + + from webdav.Connection import WebdavError +-from jarabe.journal.webdavmanager import get_resource_by_ip_address_and_resource_key + + + friends_model = friends.get_model() +@@ -59,6 +59,50 @@ friends_model = friends.get_model() + _copy_menu_helper = None + _current_action_item = None + ++ ++class PassphraseDialog(gtk.Dialog): ++ def __init__(self, callback, metadata): ++ gtk.Dialog.__init__(self, flags=gtk.DIALOG_MODAL) ++ self._callback = callback ++ self._metadata = metadata ++ self.set_title(_('Passphrase required')) ++ self.set_has_separator(False) ++ ++ # TRANS: Please do not translate the '%s'. ++ label_text = _('Please enter the passphrase for %s' % metadata['title']) ++ label = gtk.Label(label_text) ++ self.vbox.pack_start(label) ++ ++ self.add_buttons(gtk.STOCK_OK, gtk.RESPONSE_OK) ++ self.set_default_response(gtk.RESPONSE_OK) ++ self.set_has_separator(True) ++ self.add_key_entry() ++ ++ self.connect('response', self._key_dialog_response_cb) ++ self.show_all() ++ ++ def add_key_entry(self): ++ self._entry = gtk.Entry() ++ self._entry.connect('activate', self._entry_activate_cb) ++ self.vbox.pack_start(self._entry) ++ self.vbox.set_spacing(6) ++ self.vbox.show_all() ++ ++ self._entry.grab_focus() ++ ++ def _entry_activate_cb(self, entry): ++ self.response(gtk.RESPONSE_OK) ++ ++ def get_response_object(self): ++ return self._response ++ ++ def _key_dialog_response_cb(self, widget, response_id): ++ if response_id == gtk.RESPONSE_OK: ++ self.hide() ++ gobject.idle_add(self._callback, self._metadata, ++ self._entry.get_text()) ++ ++ + class ObjectPalette(Palette): + + __gtype_name__ = 'ObjectPalette' +@@ -528,6 +572,7 @@ class ActionItem(gobject.GObject): + This is the stage, just after EVERY metadata has been + processed. + """ ++ self._hide_info_widget_for_single_mode() + + # Toggle the corresponding checkbox - but only for batch-mode. + if self._batch_mode and self._auto_deselect_source_entries: +@@ -573,6 +618,25 @@ class ActionItem(gobject.GObject): + from jarabe.journal.journalactivity import get_journal + get_journal().get_list_view().refresh() + ++ def _handle_single_mode_notification(self, message, severity): ++ from jarabe.journal.journalactivity import get_journal ++ journal = get_journal() ++ ++ journal._show_alert(message, severity) ++ self._hide_info_widget_for_single_mode() ++ ++ def _hide_info_widget_for_single_mode(self): ++ if (not self._batch_mode): ++ from jarabe.journal.journalactivity import get_journal ++ journal = get_journal() ++ ++ journal.get_toolbar_box().hide_info_widget() ++ ++ def _unhide_info_widget_for_single_mode(self): ++ if not self._batch_mode: ++ from jarabe.journal.journalactivity import get_journal ++ get_journal().update_progress(0) ++ + def _handle_error_alert(self, error_message, metadata): + """ + This handles any error scenarios. Examples are of entries that +@@ -593,19 +657,11 @@ class ActionItem(gobject.GObject): + + current_len = len(self._metadata_list) + +- # TRANS: Do not translate the two %d, and the three %s. +- info_alert_message = _('( %d / %d ) Error while %s %s : %s') % ( +- self._metadata_list_initial_len - current_len, +- self._metadata_list_initial_len, +- self._get_info_alert_title(), +- metadata['title'], +- error_message) +- + # Only show the alert, if allowed to. + if self._show_not_completed_ops_info: + from jarabe.journal.journalactivity import get_journal +- get_journal().update_confirmation_alert(self._get_info_alert_title() + ' ...', +- info_alert_message, ++ get_journal().update_confirmation_alert(_('Error'), ++ error_message, + self._process_error_skipping, + metadata) + else: +@@ -639,19 +695,55 @@ class ActionItem(gobject.GObject): + if model.is_mount_point_for_locally_mounted_remote_share(current_mount_point): + file_path = metadata['uid'] + filename = os.path.basename(file_path) +- ip_address = model.extract_ip_address_from_locally_mounted_remote_share_path(file_path) +- resource = get_resource_by_ip_address_and_resource_key(ip_address, '/webdav/' + filename) +- download_file_path = '/tmp/' + ip_address + '/' + filename ++ ip_address_or_dns_name = \ ++ model.extract_ip_address_or_dns_name_from_locally_mounted_remote_share_path(file_path) ++ ++ data_webdav_manager = \ ++ webdavmanager.get_data_webdav_manager(ip_address_or_dns_name) ++ metadata_webdav_manager = \ ++ webdavmanager.get_metadata_webdav_manager(ip_address_or_dns_name) ++ ++ # Download the preview file, if it exists. ++ preview_resource = \ ++ webdavmanager.get_resource_by_resource_key(metadata_webdav_manager, ++ '/webdav/.Sugar-Metadata/' + filename + '.preview') ++ preview_path = os.path.dirname(file_path) + '/.Sugar-Metadata/'+ filename + '.preview' ++ ++ if preview_resource is not None: ++ try: ++ preview_resource.downloadFile(preview_path, ++ show_progress=False, ++ filesize=0) ++ except (WebdavError, socket.error), e: ++ error_message = e ++ logging.warn(error_message) ++ if self._batch_mode: ++ self._handle_error_alert(error_message, metadata) ++ else: ++ self._handle_single_mode_notification(error_message, ++ _('Error')) ++ return False ++ ++ # If we manage to reach here, download the data file. ++ data_resource = \ ++ webdavmanager.get_resource_by_resource_key(data_webdav_manager, ++ '/webdav/'+ filename) + try: +- resource.downloadFile(download_file_path) ++ data_resource.downloadFile(metadata['uid'], ++ show_progress=True, ++ filesize=int(metadata['filesize'])) + return True + except (WebdavError, socket.error), e: ++ # Delete the downloaded preview file, if it exists. ++ if os.path.exists(preview_path): ++ os.unlink(preview_path) ++ + error_message = e + logging.warn(error_message) + if self._batch_mode: + self._handle_error_alert(error_message, metadata) + else: +- self.emit('volume-error', error_message, ++ self._handle_single_mode_notification(error_message, + _('Error')) + return False + +@@ -662,7 +754,7 @@ class ActionItem(gobject.GObject): + if self._batch_mode: + self._handle_error_alert(error_message, metadata) + else: +- self.emit('volume-error', error_message, _('Warning')) ++ self._handle_single_mode_notification(error_message, _('Warning')) + return False + else: + return True +@@ -674,12 +766,12 @@ class ActionItem(gobject.GObject): + model.copy(metadata, mount_point) + return True + except Exception, e: +- logging.exception('Error while copying the entry. %s', e) ++ logging.exception(e) + error_message = _('Error while copying the entry. %s') % e + if self._batch_mode: + self._handle_error_alert(error_message, metadata) + else: +- self.emit('volume-error', error_message, _('Error')) ++ self._handle_single_mode_notification(error_message, _('Error')) + return False + finally: + self._set_bundle_installation_allowed(True) +@@ -698,7 +790,7 @@ class ActionItem(gobject.GObject): + if self._batch_mode: + self._handle_error_alert(error_message, metadata) + else: +- self.emit('volume-error', error_message, _('Error')) ++ self._handle_single_mode_notification(error_message, _('Error')) + return False + finally: + self._set_bundle_installation_allowed(True) +@@ -751,6 +843,29 @@ class BaseCopyMenuItem(MenuItem, ActionItem): + def _get_info_alert_title(self): + return _('Copying') + ++ def _operate(self, metadata): ++ from jarabe.journal.journalactivity import get_mount_point ++ if model.is_mount_point_for_locally_mounted_remote_share(get_mount_point()): ++ PassphraseDialog(self._proceed_after_receiving_passphrase, metadata) ++ else: ++ self._proceed_with_copy(metadata) ++ ++ def _proceed_after_receiving_passphrase(self, metadata, passphrase): ++ if metadata['passphrase'] != passphrase: ++ error_message = _('Passphrase does not match.') ++ if self._batch_mode: ++ self._handle_error_alert(error_message, metadata) ++ else: ++ self._handle_single_mode_notification(error_message, _('Error')) ++ return False ++ else: ++ self._unhide_info_widget_for_single_mode() ++ gobject.idle_add(self._proceed_with_copy, metadata) ++ ++ def _proceed_with_copy(self, metadata): ++ return NotImplementedError ++ ++ + + class VolumeMenu(BaseCopyMenuItem): + def __init__(self, metadata_list, label, mount_point, +@@ -761,9 +876,10 @@ class VolumeMenu(BaseCopyMenuItem): + show_progress_info_alert, batch_mode) + self._mount_point = mount_point + +- def _operate(self, metadata): ++ def _proceed_with_copy(self, metadata): + if not self._file_path_valid(metadata): + return False ++ + if not self._metadata_copy_valid(metadata, self._mount_point): + return False + +@@ -780,7 +896,7 @@ class ClipboardMenu(BaseCopyMenuItem): + batch_mode) + self._temp_file_path_list = [] + +- def _operate(self, metadata): ++ def _proceed_with_copy(self, metadata): + if not self._file_path_valid(metadata): + return False + +@@ -813,9 +929,10 @@ class DocumentsMenu(BaseCopyMenuItem): + show_progress_info_alert, + batch_mode) + +- def _operate(self, metadata): ++ def _proceed_with_copy(self, metadata): + if not self._file_path_valid(metadata): + return False ++ + if not self._metadata_copy_valid(metadata, + model.get_documents_path()): + return False +@@ -824,10 +941,10 @@ class DocumentsMenu(BaseCopyMenuItem): + self._post_operate_per_metadata_per_action(metadata) + + +-class SharesMenu(BaseCopyMenuItem): ++class SchoolServerMenu(BaseCopyMenuItem): + def __init__(self, metadata_list, show_editing_alert, + show_progress_info_alert, batch_mode): +- BaseCopyMenuItem.__init__(self, metadata_list, _('Shares'), ++ BaseCopyMenuItem.__init__(self, metadata_list, _('School Server'), + show_editing_alert, + show_progress_info_alert, + batch_mode) +@@ -835,8 +952,34 @@ class SharesMenu(BaseCopyMenuItem): + def _operate(self, metadata): + if not self._file_path_valid(metadata): + return False ++ ++ # If the entry is copyable, proceed with asking the ++ # upload-passphrase. ++ PassphraseDialog(self._proceed_after_receiving_passphrase, metadata) ++ ++ def _proceed_after_receiving_passphrase(self, metadata, passphrase): ++ self._unhide_info_widget_for_single_mode() ++ gobject.idle_add(self._proceed_with_uploading, metadata, ++ passphrase) ++ ++ def _proceed_with_uploading(self, metadata, passphrase): ++ # ++ # Attach the passphrase. ++ metadata['passphrase'] = passphrase ++ ++ # Attach the filesize. ++ file_path = model.get_file(metadata['uid']) ++ metadata['filesize'] = os.stat(file_path).st_size ++ ++ # Attach the current mount-point. ++ from jarabe.journal.journalactivity import get_mount_point, \ ++ get_journal ++ metadata['mountpoint'] = get_mount_point() ++ if not self._metadata_write_valid(metadata): ++ return False ++ + if not self._metadata_copy_valid(metadata, +- '/var/www/web1/web'): ++ model.WEBDAV_MOUNT_POINT): + return False + + # This is sync-operation. Call the post-operation now. +@@ -970,8 +1113,8 @@ class CopyMenuHelper(gtk.Menu): + menu.append(documents_menu) + documents_menu.show() + +- if get_mount_point() != '/var/www/web1/web': +- documents_menu = SharesMenu(metadata_list, ++ if not model.is_mount_point_for_locally_mounted_remote_share(get_mount_point()): ++ documents_menu = SchoolServerMenu(metadata_list, + show_editing_alert, + show_progress_info_alert, + batch_mode) +diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumestoolbar.py +index c24475d..8453682 100644 +--- a/src/jarabe/journal/volumestoolbar.py ++++ b/src/jarabe/journal/volumestoolbar.py +@@ -211,7 +211,7 @@ class VolumesToolbar(gtk.Toolbar): + + def _set_up_volumes(self): + self._set_up_documents_button() +- self._set_up_shares_button() ++ self._add_remote_share_button(model.SCHOOL_SERVER_IP_ADDRESS_OR_DNS_NAME) + + volume_monitor = gio.volume_monitor_get() + self._mount_added_hid = volume_monitor.connect('mount-added', +@@ -242,18 +242,10 @@ class VolumesToolbar(gtk.Toolbar): + 'user-documents', + _('Documents')) + +- def _set_up_shares_button(self): +- shares_dir_path = '/var/www/web1/web' +- self._set_up_directory_button(shares_dir_path, +- 'emblem-neighborhood-shared', +- _('Shares')) +- +- def _add_remote_share_button(self, buddy): +- button = RemoteSharesButton(buddy) ++ def _add_remote_share_button(self, ip_address_or_dns_name): ++ button = RemoteSharesButton(ip_address_or_dns_name) + button.props.group = self._volume_buttons[0] +- label = glib.markup_escape_text(_('%s\'s share') % \ +- buddy.props.nick) +- button.set_palette(RemoteSharePalette(buddy, button)) ++ button.set_palette(RemoteSharePalette(_('Shares'), button)) + button.connect('toggled', self._button_toggled_cb) + button.show() + +@@ -262,13 +254,6 @@ class VolumesToolbar(gtk.Toolbar): + self._volume_buttons.append(button) + self.show() + +- frame = jarabe.frame.get_view() +- notif_icon = NotificationIcon() +- notif_icon.props.icon_name = 'emblem-neighborhood-shared' +- notif_icon.props.xo_color = buddy.props.color +- frame.add_notification(notif_icon, +- gtk.CORNER_BOTTOM_RIGHT) +- + def __mount_added_cb(self, volume_monitor, mount): + self._add_button(mount) + +@@ -308,7 +293,6 @@ class VolumesToolbar(gtk.Toolbar): + journal.get_list_view()._selected_entries = 0 + journal.switch_to_editing_mode(False) + journal.get_list_view().inhibit_refresh(False) +- journal.get_list_view().refresh() + + self.emit('volume-changed', button.mount_point) + +@@ -338,7 +322,7 @@ class VolumesToolbar(gtk.Toolbar): + self.hide() + + def _remove_remote_share_button(self, mount_point): +- # Here, IP_Address is the mount_point. ++ # Here, ip_address_or_dns_name is the mount_point. + for button in self.get_children(): + if type(button) == RemoteSharesButton and \ + button.mount_point == mount_point: +@@ -478,6 +462,7 @@ class DirectoryButton(BaseButton): + + def __init__(self, dir_path, icon_name): + BaseButton.__init__(self, mount_point=dir_path) ++ self._mount = dir_path + + self.props.named_icon = icon_name + +@@ -488,16 +473,18 @@ class DirectoryButton(BaseButton): + + class RemoteSharesButton(BaseButton): + +- def __init__(self, buddy): +- BaseButton.__init__(self, mount_point=buddy.props.ip_address) ++ def __init__(self, ip_address_or_dns_name): ++ BaseButton.__init__(self, mount_point=ip_address_or_dns_name) + +- self._buddy = buddy ++ self._ip_address_or_dns_name = ip_address_or_dns_name + self.props.named_icon = 'emblem-neighborhood-shared' +- self.props.xo_color = buddy.props.color +- self._buddy_ip_address = buddy.props.ip_address ++ ++ client = gconf.client_get_default() ++ color = XoColor(client.get_string('/desktop/sugar/user/color')) ++ self.props.xo_color = color + + def create_palette(self): +- palette = RemoteSharePalette(self._buddy) ++ palette = RemoteSharePalette(self._ip_address_or_dns_name) + return palette + + +diff --git a/src/jarabe/journal/webdavmanager.py b/src/jarabe/journal/webdavmanager.py +index 6cd0713..db99504 100644 +--- a/src/jarabe/journal/webdavmanager.py ++++ b/src/jarabe/journal/webdavmanager.py +@@ -1,5 +1,6 @@ + from gettext import gettext as _ + ++import logging + import os + import sys + +@@ -13,11 +14,6 @@ from webdav.WebdavClient import CollectionStorer + def get_key_from_resource(resource): + return resource.path + +-def ensure_correct_remote_webdav_hierarchy(remote_webdav_share_resources, +- remote_webdav_share_collections): +- pass +- #assert len(remote_webdav_share_collections.keys()) == 1 +- + class WebDavUrlManager(gobject.GObject): + """ + This class holds all data, relevant to a WebDavUrl. +@@ -50,6 +46,9 @@ class WebDavUrlManager(gobject.GObject): + def _get_number_of_collections(self): + return len(self._remote_webdav_share_collections) + ++ def _get_root(self): ++ return self._root ++ + def _get_resources_dict(self): + return self._remote_webdav_share_resources + +@@ -59,6 +58,10 @@ class WebDavUrlManager(gobject.GObject): + def _get_resource_by_key(self, key): + return self._remote_webdav_share_resources[key]['resource'] + ++ def _add_or_replace_resource_by_key(self, key, resource): ++ self._remote_webdav_share_resources[key] = {} ++ self._remote_webdav_share_resources[key]['resource'] = resource ++ + def _get_metadata_list(self): + metadata_list = [] + for key in self._remote_webdav_share_resources.keys(): +@@ -69,11 +72,9 @@ class WebDavUrlManager(gobject.GObject): + resource_container = self._remote_webdav_share_resources[resource_key] + return resource_container['webdav-properties'] + +- def _set_metadata_for_resource(self, key, metadata): +- self._remote_webdav_share_resources[key]['metadata'] = metadata +- + def _fetch_resources_and_collections(self): + webdavConnection = CollectionStorer(self._WebDavUrl, validateResourceNames=False) ++ self._root = webdavConnection + + authFailures = 0 + while authFailures < 2: +@@ -111,8 +112,13 @@ class WebDavUrlManager(gobject.GObject): + error_message = e + get_journal()._volume_error_cb(None, error_message,_('Error')) + +- # Simply return, in case of connection-not-available. +- return False ++ # Re-raise this error. ++ # Note that since this is not an ++ # "AuthorizationError", this will not be caught ++ # by the outer except-block. Instead, it will ++ # navigate all the way back up, and will report ++ # the error in the enclosing except block. ++ raise e + + else: + # If this indeed is an Authorization Error, +@@ -138,25 +144,54 @@ class WebDavUrlManager(gobject.GObject): + + webdav_manager = {} + ++def get_data_webdav_manager(ip_address_or_dns_name): ++ return webdav_manager[ip_address_or_dns_name]['data'] + +-def get_resource_by_ip_address_and_resource_key(ip_address, key): +- global webdav_manager + +- if ip_address in webdav_manager.keys(): +- root_webdav = webdav_manager[ip_address] +- resources_dict = root_webdav._get_resources_dict() ++def get_metadata_webdav_manager(ip_address_or_dns_name): ++ return webdav_manager[ip_address_or_dns_name]['metadata'] ++ ++ ++def get_resource_by_resource_key(root_webdav, key): ++ resources_dict = root_webdav._get_resources_dict() ++ if key in resources_dict.keys(): + resource_dict = resources_dict[key] + resource = resource_dict['resource'] +- + return resource ++ return None + + +-def get_remote_webdav_share_metadata(ip_address): +- protocol = 'dav://' ++def add_resource_by_resource_key(root_webdav, key, ++ content_file_path): ++ root = root_webdav._get_root() + +- root_webdav_url = '/webdav' ++ resource = root.addResource(key) ++ ++ # Procure the resource-lock. ++ lockToken = resource.lock('olpc') ++ ++ input_stream = open(content_file_path) + +- complete_root_url = protocol + ip_address + root_webdav_url ++ # Now, upload the data; but it's necessary to enclose this in a ++ # try-except-finally block here, since we need to close the ++ # input-stream, whatever may happen. ++ try: ++ resource.uploadFile(input_stream, lockToken) ++ root_webdav._add_or_replace_resource_by_key(key, resource) ++ except Exception, e: ++ logging.exception(e) ++ resource.delete(lockToken) ++ raise e ++ else: ++ resource.unlock(lockToken) ++ finally: ++ input_stream.close() ++ ++ ++def get_remote_webdav_share_metadata(ip_address_or_dns_name): ++ protocol = 'davs://' ++ root_webdav_url = '/webdav' ++ complete_root_url = protocol + ip_address_or_dns_name + root_webdav_url + + root_webdav = WebDavUrlManager(complete_root_url, 'test', 'olpc') + if root_webdav._fetch_resources_and_collections() is False: +@@ -165,7 +200,8 @@ def get_remote_webdav_share_metadata(ip_address): + + # Keep reference to the "WebDavUrlManager", keyed by IP-Address. + global webdav_manager +- webdav_manager[ip_address] = root_webdav ++ webdav_manager[ip_address_or_dns_name] = {} ++ webdav_manager[ip_address_or_dns_name]['data'] = root_webdav + + + # Assert that the number of collections is only one at this url +@@ -174,12 +210,14 @@ def get_remote_webdav_share_metadata(ip_address): + + root_sugar_metadata_url = root_webdav_url + '/.Sugar-Metadata' + +- complete_root_sugar_metadata_url = protocol + ip_address + root_sugar_metadata_url ++ complete_root_sugar_metadata_url = protocol + ip_address_or_dns_name + root_sugar_metadata_url + root_webdav_sugar_metadata = WebDavUrlManager(complete_root_sugar_metadata_url, 'test', 'olpc') + if root_webdav_sugar_metadata._fetch_resources_and_collections() is False: + # Return empty metadata list. + return [] + ++ webdav_manager[ip_address_or_dns_name]['metadata'] = root_webdav_sugar_metadata ++ + # assert that the number of collections is zero at this url. + assert root_webdav_sugar_metadata._get_number_of_collections() == 0 + +@@ -189,68 +227,60 @@ def get_remote_webdav_share_metadata(ip_address): + root_webdav_sugar_metadata_resources = root_webdav_sugar_metadata._get_resources_dict() + + # Prepare the metadata-download folder. +- downloaded_data_root_dir = '/tmp/' + ip_address ++ downloaded_data_root_dir = '/tmp/' + ip_address_or_dns_name + downloaded_metadata_file_dir = downloaded_data_root_dir + '/.Sugar-Metadata' + if os.path.isdir(downloaded_data_root_dir): + shutil.rmtree(downloaded_data_root_dir) + os.makedirs(downloaded_metadata_file_dir) + +- for root_webdav_resource_name in root_webdav_resources.keys(): +- """ +- root_webdav_resource_name is of the type :: ++ metadata_list = [] + +- /webdav/a.txt ++ # Note that the presence of a resource in the metadata directory, ++ # is the only assurance of the entry (and its constituents) being ++ # present in entirety. Thus, always proceed taking the metadata as ++ # the "key". ++ for root_webdav_sugar_metadata_resource_name in root_webdav_sugar_metadata_resources.keys(): + """ +- split_tokens_array = root_webdav_resource_name.split('/') ++ root_webdav_sugar_metadata_resource_name is of the type :: + +- # This will provide us with "a.txt" +- basename = split_tokens_array[len(split_tokens_array) - 1] ++ /webdav/.Sugar-Metadata/a.txt.metadata, OR ++ /webdav/.Sugar-Metadata/a.txt.preview ++ """ + +- # This will provide us with "a.txt.metadata" +- sugar_metadata_basename = basename + '.metadata' ++ # If this is a "preview" resource, continue forward, as we only ++ # want the metadata list. The "preview" resources are anyways ++ # already present in the manager DS. ++ if root_webdav_sugar_metadata_resource_name.endswith('.preview'): ++ continue + +- # Thus will provide us with "/webdav/.Sugar-Metadata/a.txt.metadata" +- sugar_metadata_url = root_sugar_metadata_url + '/' + sugar_metadata_basename ++ split_tokens_array = root_webdav_sugar_metadata_resource_name.split('/') + +- # Ensure that "sugar_metadata_url" is present as one of the +- # keys in "root_webdav_sugar_metadata_resources" +- assert sugar_metadata_url in root_webdav_sugar_metadata_resources.keys() ++ # This will provide us with "a.txt.metadata" ++ sugar_metadata_basename = split_tokens_array[len(split_tokens_array) - 1] + +- # Now download the metadata file, read its contents, and store +- # the metadata in memory. +- # It is assumed that the metadata-file is small enough to be +- # read in one call to "read". ++ # This will provide us with "a.txt" ++ basename = sugar_metadata_basename.rstrip('.metadata') + + downloaded_metadata_file_path = downloaded_metadata_file_dir + '/' + sugar_metadata_basename +- metadata_resource = root_webdav_sugar_metadata._get_resource_by_key(sugar_metadata_url) ++ metadata_resource = \ ++ root_webdav_sugar_metadata._get_resource_by_key(root_webdav_sugar_metadata_resource_name) + metadata_resource.downloadFile(downloaded_metadata_file_path) + + file_pointer = open(downloaded_metadata_file_path) + metadata = eval(file_pointer.read()) + file_pointer.close() + +- # Very critical. +- # 1. CRITICAL ONE: +- # Fill in the uid. +- # Note that the file is not physically present. ++ # Fill in the missing metadata properties. ++ # Note that the file is not physically present. + metadata['uid'] = downloaded_data_root_dir + '/' + basename +- +- # 2. CRITICAL TWO: +- # Fill in the properties, that can only be done by reading +- # in the webdav-properties. +- live_properties = root_webdav._get_live_properties(root_webdav_resource_name) +- metadata['filesize'] = live_properties.getContentLength() +- metadata['timestamp'] = live_properties.getLastModified() +- metadata['creation_time'] = live_properties.getCreationDate() ++ metadata['creation_time'] = metadata['timestamp'] + + # Now, write this to the metadata-file, so that + # webdav-properties get gelled into sugar-metadata. +- + file_pointer = open(downloaded_metadata_file_path, 'w') + file_pointer.write(simplejson.dumps(metadata)) + file_pointer.close() + +- root_webdav._set_metadata_for_resource(root_webdav_resource_name, +- metadata) ++ metadata_list.append(metadata) + +- return root_webdav._get_metadata_list() ++ return metadata_list +diff --git a/src/jarabe/view/palettes.py b/src/jarabe/view/palettes.py +index 3b26faf..b753a5a 100644 +--- a/src/jarabe/view/palettes.py ++++ b/src/jarabe/view/palettes.py +@@ -339,33 +339,6 @@ class JournalXSPalette(Palette): + + + class RemoteSharePalette(Palette): +- def __init__(self, buddy, button): +- Palette.__init__(self, label=('%s\'s share' % buddy.props.nick)) +- self._buddy = buddy ++ def __init__(self, primary_text, button): ++ Palette.__init__(self, label=primary_text) + self._button = button +- +- self.props.secondary_text = glib.markup_escape_text(buddy.props.ip_address) +- +- vbox = gtk.VBox() +- self.set_content(vbox) +- vbox.show() +- +- self.connect('popup', self.__popup_cb) +- +- menu_item = MenuItem(pgettext('Share', 'Unmount')) +- +- icon = Icon(icon_name='media-eject', icon_size=gtk.ICON_SIZE_MENU) +- menu_item.set_image(icon) +- icon.show() +- +- menu_item.connect('activate', self.__unmount_activate_cb) +- self.menu.append(menu_item) +- menu_item.show() +- +- def __unmount_activate_cb(self, menu_item): +- from jarabe.journal.journalactivity import get_journal +- singleton_volumes_toolbar = get_journal().get_volumes_toolbar() +- singleton_volumes_toolbar._remove_remote_share_button(self._buddy.props.ip_address) +- +- def __popup_cb(self, palette): +- pass +diff --git a/src/webdav/Connection.py b/src/webdav/Connection.py +index 66f7833..33719f9 100644 +--- a/src/webdav/Connection.py ++++ b/src/webdav/Connection.py +@@ -197,7 +197,9 @@ class Connection(DAV): + path = _urlEncode(path) + try: + HTTPConnection.request(self, 'PUT', path, "", header) +- self._blockCopySocket(srcfile, self, Connection.blockSize) ++ filesize = os.path.getsize(srcfile.name) ++ self._blockCopySocket(srcfile, self, ++ Connection.blockSize,filesize) + srcfile.close() + response = self.getresponse() + except (CannotSendRequest, socket.error, BadStatusLine, ResponseNotReady), exc: +@@ -215,14 +217,15 @@ class Connection(DAV): + finally: + self._lock.release() + +- def _blockCopySocket(self, source, toSocket, blockSize): ++ def _blockCopySocket(self, source, toSocket, blockSize, filesize): + transferedBytes = 0 + block = source.read(blockSize) +- #while source.readinto(block, blockSize): + while len(block): +- toSocket.send(block) + self.logger.debug("Wrote %d bytes." % len(block)) + transferedBytes += len(block) ++ toSocket.send(block) ++ from jarabe.journal.journalactivity import get_journal ++ get_journal().update_progress(transferedBytes/(filesize*1.0)) + block = source.read(blockSize) + self.logger.info("Transfered %d bytes." % transferedBytes) + +diff --git a/src/webdav/WebdavClient.py b/src/webdav/WebdavClient.py +index ab5cec3..8ce5c77 100644 +--- a/src/webdav/WebdavClient.py ++++ b/src/webdav/WebdavClient.py +@@ -336,6 +336,9 @@ class ResourceStorer(object): + else: + header["Content-length"] = 0 + ++ # We need to change the header["Content-length"] to a string. ++ header["Content-length"] = str(header["Content-length"]) ++ + try: + response = self.connection.put(self.path, content, extra_hdrs=header) + finally: +@@ -367,7 +370,8 @@ class ResourceStorer(object): + # TODO: Other interface ? return self.connection.getfile() + return response + +- def downloadFile(self, localFileName): ++ def downloadFile(self, localFileName, show_progress=False, ++ filesize=0): + """ + Copy binary data from permanent storage to a local file. + +@@ -377,7 +381,8 @@ class ResourceStorer(object): + remoteFile = self.downloadContent() + try: + socket.setdefaulttimeout(SOCKET_DEFAULT_TIMEOUT) +- _blockCopyFile(remoteFile, localFile, Connection.blockSize) ++ _blockCopyFile(remoteFile, localFile, Connection.blockSize, ++ show_progress, filesize) + except socket.error, e: + raise e + remoteFile.close() +@@ -807,7 +812,7 @@ class LockToken(object): + return self.value() + + +-def _blockCopyFile(source, dest, blockSize): ++def _blockCopyFile(source, dest, blockSize, show_progress, filesize): + """ + Copies a file in chunks of C{blockSize}. + +@@ -821,8 +826,11 @@ def _blockCopyFile(source, dest, blockSize): + transferedBytes = 0 + block = source.read(blockSize) + while len(block): +- dest.write(block) + transferedBytes += len(block); ++ dest.write(block) ++ if show_progress: ++ from jarabe.journal.journalactivity import get_journal ++ get_journal().update_progress(transferedBytes/(filesize * 1.0)) + block = source.read(blockSize) + + def _checkUrl(url): +diff --git a/src/webdav/davlib.py b/src/webdav/davlib.py +index f4dac91..a611e51 100644 +--- a/src/webdav/davlib.py ++++ b/src/webdav/davlib.py +@@ -31,7 +31,7 @@ BLOCKSIZE = 16384 + class HTTPProtocolChooser(httplib.HTTPSConnection): + def __init__(self, *args, **kw): + self.protocol = kw.pop('protocol') +- if self.protocol == "https": ++ if self.__is_secure_protocol(): + self.default_port = 443 + else: + self.default_port = 80 +@@ -39,11 +39,14 @@ class HTTPProtocolChooser(httplib.HTTPSConnection): + apply(httplib.HTTPSConnection.__init__, (self,) + args, kw) + + def connect(self): +- if self.protocol == "https": ++ if self.__is_secure_protocol(): + httplib.HTTPSConnection.connect(self) + else: + httplib.HTTPConnection.connect(self) + ++ def __is_secure_protocol(self): ++ return (self.protocol == 'https') or (self.protocol == 'davs') ++ + + class HTTPConnectionAuth(HTTPProtocolChooser): + def __init__(self, *args, **kw): +@@ -333,4 +336,4 @@ class DAV(HTTPConnectionAuth): + response = self.lock(url, owner, timeout, depth) + response.parse_lock_response() + return response.locktoken +- +\ No newline at end of file ++ +-- +1.7.4.4 + |