diff options
author | Ajay Garg <ajay@activitycentral.com> | 2012-09-04 13:49:58 (GMT) |
---|---|---|
committer | Ajay Garg <ajay@activitycentral.com> | 2012-09-04 15:43:47 (GMT) |
commit | 7c231494096c2cbcd737ff477ebb5193a386a556 (patch) | |
tree | 42864e6da69444427c764af608ee07fe0f0369cb | |
parent | b9a295182236bff4499ecaf1d2dc21e1e553bb1c (diff) |
1-to-N Feature, via Peer-To-Peer mechanism; and Via-School-Server mechanism.
Details at :: http://wiki.sugarlabs.org/go/Features/Transfer_to_many_options
Signed-off-by: Ajay Garg <ajay@activitycentral.com>
-rw-r--r-- | rpms/sugar/0130-1-to-N-feature-via-School-Server.patch | 491 |
1 files changed, 403 insertions, 88 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 index ef570a4..81cd2f8 100644 --- a/rpms/sugar/0130-1-to-N-feature-via-School-Server.patch +++ b/rpms/sugar/0130-1-to-N-feature-via-School-Server.patch @@ -1,23 +1,84 @@ -From ef7fc9392d515430290406bc7813987b5e661451 Mon Sep 17 00:00:00 2001 +From d56362d85ecd6ce9921a67c2f4252d01c26a71af Mon Sep 17 00:00:00 2001 From: Ajay Garg <ajay@activitycentral.com> -Date: Wed, 29 Aug 2012 16:56:59 +0530 -Subject: [sugar PATCH] 1-to-N-feature-via-School-Server. +Date: Tue, 4 Sep 2012 18:16:19 +0530 +Subject: [sugar PATCH] 1-to-N-feature via Peer-to-Peer mechanism; and Via-School-Server mechanism. Organization: Sugar Labs Foundation Signed-off-by: Ajay Garg <ajay@activitycentral.com> --- - src/jarabe/journal/expandedentry.py | 24 +++- + src/jarabe/frame/activitiestray.py | 5 + + src/jarabe/frame/frame.py | 3 + + src/jarabe/intro/window.py | 12 + + src/jarabe/journal/expandedentry.py | 24 ++- src/jarabe/journal/journalactivity.py | 17 ++- src/jarabe/journal/journaltoolbox.py | 44 ++++- - src/jarabe/journal/model.py | 352 ++++++++++++++++++++++----------- - src/jarabe/journal/palettes.py | 238 +++++++++++++++++++--- - src/jarabe/journal/volumestoolbar.py | 41 ++--- - src/jarabe/journal/webdavmanager.py | 164 ++++++++++------ - src/jarabe/view/palettes.py | 31 +--- + src/jarabe/journal/model.py | 375 ++++++++++++++++++++++----------- + src/jarabe/journal/palettes.py | 283 ++++++++++++++++++++++--- + src/jarabe/journal/volumestoolbar.py | 92 ++++++--- + src/jarabe/journal/webdavmanager.py | 172 ++++++++++----- + src/jarabe/view/buddymenu.py | 45 ++++- + src/jarabe/view/palettes.py | 38 ++-- src/webdav/Connection.py | 11 +- - src/webdav/WebdavClient.py | 16 ++- + src/webdav/WebdavClient.py | 16 +- src/webdav/davlib.py | 9 +- - 11 files changed, 667 insertions(+), 280 deletions(-) + 15 files changed, 867 insertions(+), 279 deletions(-) +diff --git a/src/jarabe/frame/activitiestray.py b/src/jarabe/frame/activitiestray.py +index 55e0c31..7cbeefb 100644 +--- a/src/jarabe/frame/activitiestray.py ++++ b/src/jarabe/frame/activitiestray.py +@@ -249,6 +249,8 @@ class ActivitiesTray(HTray): + # JournalActivity is always the first activity to be added. + # Broadcast the signal-of-its-creation. + if group is None: ++ self._journal_button = button ++ self._journal_activity = home_activity + self._signal_addition_of_journal_activity() + + def _signal_addition_of_journal_activity(self): +@@ -306,6 +308,9 @@ class ActivitiesTray(HTray): + if window: + window.activate(gtk.get_current_event_time()) + ++ def _show_journal_activity(self): ++ self.__activity_clicked_cb(self._journal_button, self._journal_activity) ++ + def __remove_invite_cb(self, icon, invite): + self._invites.remove_invite(invite) + +diff --git a/src/jarabe/frame/frame.py b/src/jarabe/frame/frame.py +index cd1dc20..821e31f 100644 +--- a/src/jarabe/frame/frame.py ++++ b/src/jarabe/frame/frame.py +@@ -449,3 +449,6 @@ class Frame(object): + # Do nothing for now. Our notification UI is so simple, there's no + # point yet. + pass ++ ++ def switch_to_journal_activity(self): ++ self._activities_tray._show_journal_activity() +diff --git a/src/jarabe/intro/window.py b/src/jarabe/intro/window.py +index df19fbf..c4ae56e 100644 +--- a/src/jarabe/intro/window.py ++++ b/src/jarabe/intro/window.py +@@ -56,6 +56,18 @@ def create_profile(name, color=None): + else: + logging.error('Keypair exists, skip generation.') + ++ # Also, generate SSL key and cert pair; to be used in secure ++ # sharing of webdav entries via httpd. ++ keypath = os.path.join(env.get_profile_path(), 'ssl.key') ++ certpath = os.path.join(env.get_profile_path(), 'ssl.crt') ++ ++ cmd = 'openssl req -new -newkey rsa:1024 -days 365 -nodes ' + \ ++ '-x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" ' + \ ++ '-keyout %s -out %s' % (keypath, certpath) ++ (s, o) = commands.getstatusoutput(cmd) ++ if s != 0: ++ logging.error('Could not generate ssl key and cert: %d %s', s, o) ++ + + class _Page(hippo.CanvasBox): + __gproperties__ = { diff --git a/src/jarabe/journal/expandedentry.py b/src/jarabe/journal/expandedentry.py index 03f8cd1..1e857ba 100644 --- a/src/jarabe/journal/expandedentry.py @@ -194,7 +255,7 @@ index 6b2494e..b1c0cac 100644 return self._selected_entries diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py -index 422e947..071376b 100644 +index 422e947..c06b2ee 100644 --- a/src/jarabe/journal/model.py +++ b/src/jarabe/journal/model.py @@ -43,7 +43,7 @@ from sugar import dispatch @@ -206,15 +267,17 @@ index 422e947..071376b 100644 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', +@@ -58,6 +58,9 @@ PROPERTIES = ['activity', 'activity_id', 'buddies', 'bundle_id', MIN_PAGES_TO_CACHE = 3 MAX_PAGES_TO_CACHE = 5 +WEBDAV_MOUNT_POINT = '/tmp/' ++LOCAL_SHARES_MOUNT_POINT = '/var/www/web1/web/' ++ JOURNAL_METADATA_DIR = '.Sugar-Metadata' _datastore = None -@@ -66,6 +67,43 @@ updated = dispatch.Signal() +@@ -66,6 +69,52 @@ updated = dispatch.Signal() deleted = dispatch.Signal() @@ -222,8 +285,7 @@ index 422e947..071376b 100644 + '/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) ++SCHOOL_SERVER_IP_ADDRESS_OR_DNS_NAME = client.get_string(SCHOOL_SERVER_IP_ADDRESS_OR_DNS_NAME_PATH) or '127.0.0.1' + + +def _get_mount_point(path): @@ -236,6 +298,16 @@ index 422e947..071376b 100644 + return None + + ++def is_mount_point_for_school_server(mount_point): ++ from jarabe.journal.journalactivity import get_journal ++ from jarabe.journal.volumestoolbar import SHARE_TYPE_SCHOOL_SERVER ++ ++ mount_point_button = get_journal().get_volumes_toolbar()._get_button_for_mount_point(mount_point) ++ if mount_point_button._share_type == SHARE_TYPE_SCHOOL_SERVER: ++ return True ++ return False ++ ++ +def extract_ip_address_or_dns_name_from_locally_mounted_remote_share_path(path): + """ + Path is of type :: @@ -258,7 +330,7 @@ index 422e947..071376b 100644 class _Cache(object): __gtype_name__ = 'model_Cache' -@@ -430,8 +468,8 @@ class InplaceResultSet(BaseResultSet): +@@ -430,8 +479,8 @@ class InplaceResultSet(BaseResultSet): class RemoteShareResultSet(object): @@ -269,7 +341,7 @@ index 422e947..071376b 100644 self._file_list = [] self.ready = dispatch.Signal() -@@ -464,7 +502,11 @@ class RemoteShareResultSet(object): +@@ -464,7 +513,11 @@ class RemoteShareResultSet(object): self._sort = query.get('order_by', ['+timestamp'])[0] def setup(self): @@ -282,7 +354,7 @@ index 422e947..071376b 100644 for metadata in metadata_list_complete: add_to_list = False -@@ -553,15 +595,8 @@ def _get_file_metadata(path, stat, fetch_preview=True): +@@ -553,15 +606,8 @@ def _get_file_metadata(path, stat, fetch_preview=True): metadata based on the file properties. """ @@ -299,7 +371,7 @@ index 422e947..071376b 100644 if 'filesize' not in metadata: if stat is not None: metadata['filesize'] = stat.st_size -@@ -582,17 +617,26 @@ def _get_file_metadata(path, stat, fetch_preview=True): +@@ -582,17 +628,26 @@ def _get_file_metadata(path, stat, fetch_preview=True): 'description': path} @@ -329,7 +401,7 @@ index 422e947..071376b 100644 filename + '.preview') if not os.path.exists(metadata_path): -@@ -670,7 +714,8 @@ def find(query_, page_size): +@@ -670,7 +725,8 @@ def find(query_, page_size): Regex Matching is used, to ensure that the mount-point is an IP-Address. """ @@ -339,7 +411,7 @@ index 422e947..071376b 100644 else: """ For Documents/Shares/Mounted-Drives. -@@ -678,56 +723,13 @@ def find(query_, page_size): +@@ -678,56 +734,13 @@ def find(query_, page_size): return InplaceResultSet(query, page_size, mount_points[0]) @@ -396,7 +468,7 @@ index 422e947..071376b 100644 stat = None metadata = _get_file_metadata(object_id, stat) -@@ -812,7 +814,21 @@ def delete(object_id): +@@ -812,7 +825,21 @@ def delete(object_id): def copy(metadata, mount_point): """Copies an object to another mount point """ @@ -418,7 +490,7 @@ index 422e947..071376b 100644 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 +839,7 @@ def copy(metadata, mount_point): +@@ -823,7 +850,7 @@ def copy(metadata, mount_point): metadata['mountpoint'] = mount_point del metadata['uid'] @@ -427,7 +499,7 @@ index 422e947..071376b 100644 def write(metadata, file_path='', update_mtime=True, transfer_ownership=True): -@@ -845,27 +861,97 @@ def write(metadata, file_path='', update_mtime=True, transfer_ownership=True): +@@ -845,27 +872,105 @@ def write(metadata, file_path='', update_mtime=True, transfer_ownership=True): object_id = _get_datastore().create(dbus.Dictionary(metadata), file_path, transfer_ownership) @@ -506,6 +578,12 @@ index 422e947..071376b 100644 -def _rename_entry_on_external_device(file_path, destination_path, - metadata_dir_path): ++def make_file_fully_permissible(file_path): ++ fd = os.open(file_path, os.O_RDONLY) ++ os.fchmod(fd, stat.S_IRWXU | stat.S_IRWXG |stat.S_IRWXO) ++ os.close(fd) ++ ++ +def _rename_entry_on_external_device(file_path, destination_path): """Rename an entry with the associated metadata on an external device.""" old_file_path = file_path @@ -515,6 +593,8 @@ index 422e947..071376b 100644 + # on XOs, wih the OSError 13 ("invalid cross-device link"). So, + # using the system call "mv". + os.system('mv "%s" "%s"' % (file_path, destination_path)) ++ make_file_fully_permissible(destination_path) ++ + + # In renaming, we want to delete the metadata-, and preview- + # files of the current mount-point, and not the destination @@ -536,7 +616,7 @@ index 422e947..071376b 100644 old_fname + '.preview')] for ofile in old_files: if os.path.exists(ofile): -@@ -876,41 +962,31 @@ def _rename_entry_on_external_device(file_path, destination_path, +@@ -876,41 +981,32 @@ def _rename_entry_on_external_device(file_path, destination_path, 'for file=%s', ofile, old_fname) @@ -566,7 +646,8 @@ index 422e947..071376b 100644 - 'removable devices') + # 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: ++ if (metadata.get('mountpoint', '/') != WEBDAV_MOUNT_POINT) and \ ++ (metadata.get('mountpoint', '/') != LOCAL_SHARES_MOUNT_POINT): + metadata_copy.pop('filesize', None) - if not metadata.get('title'): @@ -599,21 +680,21 @@ index 422e947..071376b 100644 metadata_dir_path = os.path.join(metadata['mountpoint'], JOURNAL_METADATA_DIR) -@@ -939,25 +1015,61 @@ def _write_entry_on_external_device(metadata, file_path): +@@ -939,25 +1035,64 @@ 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') ++ make_file_fully_permissible(metadata_destination_path) + if preview: + preview_destination_path = os.path.join(metadata_dir_path, preview_fname) ++ make_file_fully_permissible(preview_destination_path) else: - _rename_entry_on_external_device(file_path, destination_path, - metadata_dir_path) + preview_destination_path = None -+ -+ return (metadata_destination_path, preview_destination_path) - # For "Shares" folder, we need to set the permissions of the newly - # copied file to 0777, else it will not be accessible by "httpd" @@ -622,11 +703,13 @@ index 422e947..071376b 100644 - fd = os.open(destination_path, os.O_RDONLY) - os.fchmod(fd, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) - os.close(fd) ++ return (metadata_destination_path, preview_destination_path) - 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) + +def update_only_metadata_and_preview_files_and_return_file_paths(metadata): + file_name = get_file_name(metadata['title'], metadata['mime_type']) + _write_metadata_and_preview_files_and_return_file_paths(metadata, @@ -637,7 +720,7 @@ index 422e947..071376b 100644 + transfer_ownership): + """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. @@ -673,10 +756,11 @@ index 422e947..071376b 100644 + _rename_entry_on_external_device(file_path, destination_path) + else: + shutil.copy(file_path, destination_path) ++ make_file_fully_permissible(destination_path) object_id = destination_path created.send(None, object_id=object_id) -@@ -1013,7 +1125,11 @@ def is_editable(metadata): +@@ -1013,7 +1148,11 @@ def is_editable(metadata): # called, upon an entry in the context of a singular # mount-point. from jarabe.journal.journalactivity import get_mount_point @@ -690,7 +774,7 @@ index 422e947..071376b 100644 def get_documents_path(): diff --git a/src/jarabe/journal/palettes.py b/src/jarabe/journal/palettes.py -index 66dcadc..f8a437e 100644 +index 66dcadc..91e5460 100644 --- a/src/jarabe/journal/palettes.py +++ b/src/jarabe/journal/palettes.py @@ -30,6 +30,7 @@ import gio @@ -926,13 +1010,14 @@ index 66dcadc..f8a437e 100644 return False finally: self._set_bundle_installation_allowed(True) -@@ -751,6 +842,29 @@ class BaseCopyMenuItem(MenuItem, ActionItem): +@@ -751,6 +842,30 @@ 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()): ++ if(model.is_mount_point_for_locally_mounted_remote_share(get_mount_point())) \ ++ and (model.is_mount_point_for_school_server(get_mount_point()) == True): + PassphraseDialog(self._proceed_after_receiving_passphrase, metadata) + else: + self._proceed_with_copy(metadata) @@ -956,7 +1041,7 @@ index 66dcadc..f8a437e 100644 class VolumeMenu(BaseCopyMenuItem): def __init__(self, metadata_list, label, mount_point, -@@ -761,9 +875,10 @@ class VolumeMenu(BaseCopyMenuItem): +@@ -761,9 +876,10 @@ class VolumeMenu(BaseCopyMenuItem): show_progress_info_alert, batch_mode) self._mount_point = mount_point @@ -968,7 +1053,7 @@ index 66dcadc..f8a437e 100644 if not self._metadata_copy_valid(metadata, self._mount_point): return False -@@ -780,7 +895,7 @@ class ClipboardMenu(BaseCopyMenuItem): +@@ -780,7 +896,7 @@ class ClipboardMenu(BaseCopyMenuItem): batch_mode) self._temp_file_path_list = [] @@ -977,7 +1062,7 @@ index 66dcadc..f8a437e 100644 if not self._file_path_valid(metadata): return False -@@ -813,9 +928,10 @@ class DocumentsMenu(BaseCopyMenuItem): +@@ -813,9 +929,10 @@ class DocumentsMenu(BaseCopyMenuItem): show_progress_info_alert, batch_mode) @@ -989,11 +1074,42 @@ index 66dcadc..f8a437e 100644 if not self._metadata_copy_valid(metadata, model.get_documents_path()): return False -@@ -824,10 +940,10 @@ class DocumentsMenu(BaseCopyMenuItem): +@@ -824,10 +941,41 @@ class DocumentsMenu(BaseCopyMenuItem): self._post_operate_per_metadata_per_action(metadata) -class SharesMenu(BaseCopyMenuItem): ++class LocalSharesMenu(BaseCopyMenuItem): ++ def __init__(self, metadata_list, show_editing_alert, ++ show_progress_info_alert, batch_mode): ++ BaseCopyMenuItem.__init__(self, metadata_list, _('Local Shares'), ++ show_editing_alert, ++ show_progress_info_alert, ++ batch_mode) ++ ++ def _proceed_with_copy(self, metadata): ++ if not self._file_path_valid(metadata): ++ return False ++ ++ # 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 ++ metadata['mountpoint'] = get_mount_point() ++ ++ if not self._metadata_write_valid(metadata): ++ return False ++ ++ if not self._metadata_copy_valid(metadata, ++ model.LOCAL_SHARES_MOUNT_POINT): ++ return False ++ ++ # This is sync-operation. Call the post-operation now. ++ self._post_operate_per_metadata_per_action(metadata) ++ ++ +class SchoolServerMenu(BaseCopyMenuItem): def __init__(self, metadata_list, show_editing_alert, show_progress_info_alert, batch_mode): @@ -1002,7 +1118,7 @@ index 66dcadc..f8a437e 100644 show_editing_alert, show_progress_info_alert, batch_mode) -@@ -835,13 +951,75 @@ class SharesMenu(BaseCopyMenuItem): +@@ -835,13 +983,75 @@ class SharesMenu(BaseCopyMenuItem): def _operate(self, metadata): if not self._file_path_valid(metadata): return False @@ -1079,53 +1195,110 @@ index 66dcadc..f8a437e 100644 class FriendsMenu(gtk.Menu): __gtype_name__ = 'JournalFriendsMenu' -@@ -970,8 +1148,8 @@ class CopyMenuHelper(gtk.Menu): +@@ -970,12 +1180,23 @@ 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 get_mount_point() != model.LOCAL_SHARES_MOUNT_POINT: ++ local_shares_menu = LocalSharesMenu(metadata_list, ++ show_editing_alert, ++ show_progress_info_alert, ++ batch_mode) ++ local_shares_menu.set_image(Icon(icon_name='emblem-neighborhood-shared', ++ icon_size=gtk.ICON_SIZE_MENU)) ++ local_shares_menu.connect('volume-error', self.__volume_error_cb) ++ menu.append(local_shares_menu) ++ local_shares_menu.show() ++ + 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) +- documents_menu.set_image(Icon(icon_name='emblem-neighborhood-shared', ++ documents_menu.set_image(Icon(icon_name='school-server', + icon_size=gtk.ICON_SIZE_MENU)) + documents_menu.connect('volume-error', self.__volume_error_cb) + menu.append(documents_menu) diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumestoolbar.py -index c24475d..e251300 100644 +index c24475d..b004657 100644 --- a/src/jarabe/journal/volumestoolbar.py +++ b/src/jarabe/journal/volumestoolbar.py -@@ -211,7 +211,7 @@ class VolumesToolbar(gtk.Toolbar): +@@ -37,7 +37,6 @@ from sugar.graphics.palette import Palette + from sugar.graphics.xocolor import XoColor + from sugar import env + +-from jarabe.frame.notification import NotificationIcon + from jarabe.journal import model + from jarabe.view.palettes import JournalVolumePalette, JournalXSPalette, RemoteSharePalette + import jarabe.frame +@@ -45,6 +44,9 @@ import jarabe.frame + + _JOURNAL_0_METADATA_DIR = '.olpc.store' + ++SHARE_TYPE_PEER = 1 ++SHARE_TYPE_SCHOOL_SERVER = 2 ++ + + def _get_id(document): + """Get the ID for the document in the xapian database.""" +@@ -211,7 +213,13 @@ 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) ++ self._set_up_local_shares_button() ++ ++ client = gconf.client_get_default() ++ color = XoColor(client.get_string('/desktop/sugar/user/color')) ++ self._add_remote_share_button(_('School-Server Shares'), ++ model.SCHOOL_SERVER_IP_ADDRESS_OR_DNS_NAME, ++ color, SHARE_TYPE_SCHOOL_SERVER) volume_monitor = gio.volume_monitor_get() self._mount_added_hid = volume_monitor.connect('mount-added', -@@ -242,18 +242,10 @@ class VolumesToolbar(gtk.Toolbar): +@@ -242,18 +250,28 @@ 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', ++ def _set_up_local_shares_button(self): ++ local_shares_path = model.LOCAL_SHARES_MOUNT_POINT ++ self._set_up_directory_button(local_shares_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) ++ _('Local Shares')) ++ ++ def _add_remote_share_button(self, primary_text, ++ ip_address_or_dns_name, color, ++ share_type): ++ button = RemoteSharesButton(primary_text, ip_address_or_dns_name, ++ color, share_type) ++ button._share_type = share_type 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)) ++ ++ show_unmount_option = None ++ if share_type == SHARE_TYPE_PEER: ++ show_unmount_option = True ++ else: ++ show_unmount_option = False ++ button.set_palette(RemoteSharePalette(primary_text, ++ ip_address_or_dns_name, button, ++ show_unmount_option)) button.connect('toggled', self._button_toggled_cb) button.show() -@@ -262,13 +254,6 @@ class VolumesToolbar(gtk.Toolbar): +@@ -262,12 +280,7 @@ class VolumesToolbar(gtk.Toolbar): self._volume_buttons.append(button) self.show() @@ -1135,11 +1308,18 @@ index c24475d..e251300 100644 - notif_icon.props.xo_color = buddy.props.color - frame.add_notification(notif_icon, - gtk.CORNER_BOTTOM_RIGHT) -- ++ return button + def __mount_added_cb(self, volume_monitor, mount): self._add_button(mount) +@@ -302,13 +315,13 @@ class VolumesToolbar(gtk.Toolbar): + + def _button_toggled_cb(self, button, force_toggle=False): + if button.props.active or force_toggle: ++ button.set_active(True) + from jarabe.journal.journalactivity import get_journal + journal = get_journal() -@@ -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) @@ -1147,16 +1327,47 @@ index c24475d..e251300 100644 self.emit('volume-changed', button.mount_point) -@@ -338,7 +322,7 @@ class VolumesToolbar(gtk.Toolbar): +@@ -328,6 +341,14 @@ class VolumesToolbar(gtk.Toolbar): + logging.error('Couldnt find button with mount_point %r', mount_point) + return None + ++ def _get_button_for_mount_point(self, mount_point): ++ for button in self.get_children(): ++ if button.mount_point == mount_point: ++ return button ++ logging.error('Couldnt find button with mount_point %r', mount_point) ++ return None ++ ++ + def _remove_button(self, mount): + button = self._get_button_for_mount(mount) + self._volume_buttons.remove(button) +@@ -337,16 +358,20 @@ class VolumesToolbar(gtk.Toolbar): + if len(self.get_children()) < 2: self.hide() - def _remove_remote_share_button(self, mount_point): +- 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. ++ def _remove_remote_share_button(self, ip_address_or_dns_name): for button in self.get_children(): if type(button) == RemoteSharesButton and \ - button.mount_point == mount_point: -@@ -478,6 +462,7 @@ class DirectoryButton(BaseButton): +- button.mount_point == mount_point: ++ button.mount_point == (model.WEBDAV_MOUNT_POINT + ip_address_or_dns_name): + self._volume_buttons.remove(button) + self.remove(button) ++ ++ from jarabe.journal.webdavmanager import \ ++ unmount_share_from_backend ++ unmount_share_from_backend(ip_address_or_dns_name) ++ + self.get_children()[0].props.active = True + +- if len(sel.get_children()) < 2: ++ if len(self.get_children()) < 2: + self.hide() + break; + +@@ -478,6 +503,7 @@ class DirectoryButton(BaseButton): def __init__(self, dir_path, icon_name): BaseButton.__init__(self, mount_point=dir_path) @@ -1164,33 +1375,38 @@ index c24475d..e251300 100644 self.props.named_icon = icon_name -@@ -488,16 +473,18 @@ class DirectoryButton(BaseButton): +@@ -488,16 +514,22 @@ 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): ++ def __init__(self, primary_text, ip_address_or_dns_name, color, ++ share_type): + BaseButton.__init__(self, mount_point=(model.WEBDAV_MOUNT_POINT + ip_address_or_dns_name)) ++ ++ self._primary_text = primary_text ++ self._ip_address_or_dns_name = 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.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')) ++ if share_type == SHARE_TYPE_PEER: ++ self.props.named_icon = 'emblem-neighborhood-shared' ++ elif share_type == SHARE_TYPE_SCHOOL_SERVER: ++ self.props.named_icon = 'school-server' + self.props.xo_color = color def create_palette(self): - palette = RemoteSharePalette(self._buddy) -+ palette = RemoteSharePalette(self._ip_address_or_dns_name) ++ palette = RemoteSharePalette(self._primary_text, self._ip_address_or_dns_name, ++ self, True) return palette diff --git a/src/jarabe/journal/webdavmanager.py b/src/jarabe/journal/webdavmanager.py -index 6cd0713..39c1c30 100644 +index 6cd0713..3ff9990 100644 --- a/src/jarabe/journal/webdavmanager.py +++ b/src/jarabe/journal/webdavmanager.py @@ -1,5 +1,6 @@ @@ -1272,6 +1488,10 @@ index 6cd0713..39c1c30 100644 +def get_data_webdav_manager(ip_address_or_dns_name): + return webdav_manager[ip_address_or_dns_name]['data'] ++ ++ ++def get_metadata_webdav_manager(ip_address_or_dns_name): ++ return webdav_manager[ip_address_or_dns_name]['metadata'] -def get_resource_by_ip_address_and_resource_key(ip_address, key): - global webdav_manager @@ -1279,10 +1499,6 @@ index 6cd0713..39c1c30 100644 - 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(): @@ -1304,10 +1520,10 @@ index 6cd0713..39c1c30 100644 + + # Procure the resource-lock. + lockToken = resource.lock('olpc') ++ ++ input_stream = open(content_file_path) - complete_root_url = protocol + ip_address + root_webdav_url -+ input_stream = open(content_file_path) -+ + # 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. @@ -1357,7 +1573,7 @@ index 6cd0713..39c1c30 100644 # assert that the number of collections is zero at this url. assert root_webdav_sugar_metadata._get_number_of_collections() == 0 -@@ -189,68 +229,74 @@ def get_remote_webdav_share_metadata(ip_address): +@@ -189,68 +229,82 @@ 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. @@ -1465,32 +1681,126 @@ index 6cd0713..39c1c30 100644 - root_webdav._set_metadata_for_resource(root_webdav_resource_name, - metadata) + metadata_list.append(metadata) ++ ++ return metadata_list ++ ++ ++def is_remote_webdav_loaded(ip_address_or_dns_name): ++ return ip_address_or_dns_name in webdav_manager.keys() ++ - return root_webdav._get_metadata_list() -+ return metadata_list ++def unmount_share_from_backend(ip_address_or_dns_name): ++ del webdav_manager[ip_address_or_dns_name] +diff --git a/src/jarabe/view/buddymenu.py b/src/jarabe/view/buddymenu.py +index dfbcfa3..f6af1b5 100644 +--- a/src/jarabe/view/buddymenu.py ++++ b/src/jarabe/view/buddymenu.py +@@ -27,6 +27,7 @@ from sugar.graphics.palette import Palette + from sugar.graphics.menuitem import MenuItem + from sugar.graphics.icon import Icon + ++from jarabe.frame.notification import NotificationIcon + from jarabe.model import shell + from jarabe.model import friends + from jarabe.model.session import get_session_manager +@@ -73,11 +74,17 @@ class BuddyMenu(Palette): + self.menu.append(menu_item) + menu_item.show() + +- access_buddy_remote_share_menu_item = MenuItem(_('Access Share'), 'list-add') +- access_buddy_remote_share_menu_item.connect('activate', +- self._access_share_cb) +- self.menu.append(access_buddy_remote_share_menu_item) +- access_buddy_remote_share_menu_item.show() ++ remote_share_menu_item = None ++ from jarabe.journal import webdavmanager ++ if not webdavmanager.is_remote_webdav_loaded(self._buddy.props.ip_address): ++ remote_share_menu_item = MenuItem(_('Access Share'), 'list-add') ++ remote_share_menu_item.connect('activate', self._access_share_cb) ++ else: ++ remote_share_menu_item = MenuItem(_('Unmount Share'), 'list-remove') ++ remote_share_menu_item.connect('activate', self.__unmount_cb) ++ ++ self.menu.append(remote_share_menu_item) ++ remote_share_menu_item.show() + + self._invite_menu = MenuItem('') + self._invite_menu.connect('activate', self._invite_friend_cb) +@@ -89,10 +96,36 @@ class BuddyMenu(Palette): + activity = home_model.get_active_activity() + self._update_invite_menu(activity) + ++ def __unmount_cb(self, menuitem): ++ 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 _access_share_cb(self, menuitem): + from jarabe.journal.journalactivity import get_journal + volumes_toolbar = get_journal().get_volumes_toolbar() +- volumes_toolbar._add_remote_share_button(self._buddy) ++ ++ # TRANS: Do not translate the """%s""". ++ primary_text = _('%s\'s Shares') % self._buddy.props.nick ++ button = volumes_toolbar._add_remote_share_button(primary_text, ++ self._buddy.props.ip_address, ++ self._buddy.props.color, ++ jarabe.journal.volumestoolbar.SHARE_TYPE_PEER) ++ ++ # Now, make three levels of transitions, from the ++ # Neighborhood-view. ++ ++ # 1. Switch to the home-view. ++ from jarabe.model import shell ++ from jarabe.model.shell import ShellModel ++ shell.get_model().set_zoom_level(ShellModel.ZOOM_HOME) ++ ++ # 2. Switch to the journal-activity-view. ++ top_frame = jarabe.frame.get_view() ++ top_frame.switch_to_journal_activity() ++ ++ # 3. Switch to the newly mounted-view. ++ volumes_toolbar._button_toggled_cb(button, force_toggle=True) + + def _add_my_items(self): + item = MenuItem(_('Shutdown'), 'system-shutdown') diff --git a/src/jarabe/view/palettes.py b/src/jarabe/view/palettes.py -index 3b26faf..b753a5a 100644 +index 3b26faf..f6bca48 100644 --- a/src/jarabe/view/palettes.py +++ b/src/jarabe/view/palettes.py -@@ -339,33 +339,6 @@ class JournalXSPalette(Palette): +@@ -339,33 +339,35 @@ 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): ++ def __init__(self, primary_text, ip_address_or_dns_name, button, ++ show_unmount_option): + Palette.__init__(self, label=primary_text) self._button = button -- ++ self._ip_address_or_dns_name = ip_address_or_dns_name + - self.props.secondary_text = glib.markup_escape_text(buddy.props.ip_address) -- ++ self.props.secondary_text = \ ++ glib.markup_escape_text(self._ip_address_or_dns_name) + - vbox = gtk.VBox() - self.set_content(vbox) - vbox.show() -- ++ if show_unmount_option == True: ++ vbox = gtk.VBox() ++ self.set_content(vbox) ++ vbox.show() + - self.connect('popup', self.__popup_cb) -- ++ 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 = MenuItem(pgettext('Share', 'Unmount')) - - icon = Icon(icon_name='media-eject', icon_size=gtk.ICON_SIZE_MENU) @@ -1500,14 +1810,19 @@ index 3b26faf..b753a5a 100644 - 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() ++ 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 ++ singleton_volumes_toolbar._remove_remote_share_button(self._ip_address_or_dns_name) + + 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 |