#region /* ----------------------------------------------------------------------- * * * Copyright 2011-2012 GRNET S.A. All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and * documentation are those of the authors and should not be * interpreted as representing official policies, either expressed * or implied, of GRNET S.A. * * ----------------------------------------------------------------------- */ #endregion using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using Caliburn.Micro; using Pithos.Client.WPF.Properties; using Pithos.Client.WPF.Utils; using Pithos.Core; using Pithos.Interfaces; using Pithos.Network; namespace Pithos.Client.WPF.SelectiveSynch { class SelectiveSynchViewModel:Screen { private readonly IEventAggregator _events ; public AccountSettings Account { get; set; } private readonly ObservableCollection _rootNodes=new ObservableCollection(); public ObservableCollection RootNodes { get { return _rootNodes; } } private ObservableCollection _checks; //private readonly PithosMonitor _monitor; private bool _isBusy=true; private readonly string _apiKey; public ObservableCollection Checks { get { return _checks; } } public void GetChecks() { var root = RootNodes[0]; _checks = new ObservableCollection( from DirectoryRecord record in root where record.IsChecked==true select record.ObjectInfo); NotifyOfPropertyChange(() => Checks); } public SelectiveSynchViewModel(/*PithosMonitor monitor,*/ IEventAggregator events, AccountSettings account, string apiKey) { Account = account; AccountName = account.AccountName; DisplayName = String.Format("Selective folder synchronization for {0}",account.AccountName); //_monitor = monitor; _events = events; _apiKey = apiKey; //IsEnabled = account.SelectiveSyncEnabled; TaskEx.Run(LoadRootNode); } private void LoadRootNode() { //TODO: Check this var client = new CloudFilesClient(AccountName,_apiKey){AuthenticationUrl=Account.ServerUrl,UsePithos=true}; client.Authenticate(); //NEED to get the local folders here as well, // and combine them with the cloud folders var dirs = from container in client.ListContainers(AccountName) where container.Name != "trash" select new DirectoryRecord { DisplayName = container.Name, Uri=new Uri(client.StorageUrl,String.Format(@"{0}/{1}",Account.AccountName, container.Name)), Directories = (from dir in client.ListObjects(AccountName, container.Name) select dir).ToTree() }; var ownFolders = dirs.ToList(); var accountNodes=from account in client.ListSharingAccounts() select new DirectoryRecord { DisplayName=account.name, Uri=new Uri(client.StorageUrl,"../"+ account.name), Directories=(from container in client.ListContainers(account.name) select new DirectoryRecord { DisplayName=container.Name, Uri = new Uri(client.StorageUrl, "../" + account.name + "/" + container.Name), Directories=(from folder in client.ListObjects(account.name,container.Name) select folder).ToTree() }).ToList() }; var othersNode = new DirectoryRecord { DisplayName = "Shared with me", Directories=accountNodes.ToList() }; var rootItem = new DirectoryRecord { DisplayName = AccountName , Directories = ownFolders.ToList() }; // Add Local Folders //var localFolders = SelectiveExtensions.LocalFolders(AccountName, Account.RootPath,client.StorageUrl.AbsoluteUri); //AppendToTree(localFolders, rootItem); //For each local folder that doesn't exist in the server nodes //find the best matching parent and add the folder below it. Execute.OnUIThread(() => { RootNodes.Add(rootItem); RootNodes.Add(othersNode); }); SetInitialSelections(Account); IsBusy = false; } private static void AppendToTree(IEnumerable localFolders, DirectoryRecord rootItem) { foreach (var folder in localFolders) { var items = from root in rootItem from child in root select child; //If this folder is not included in the server folders if (items.Any(dir => dir.Uri == folder.Uri)) continue; //we need to add it //Find the best parent //One way to do this, is to break the the Uri to its parts //and try to find a parent using progressively fewer parts var parts = folder.Uri.AbsoluteUri.Split('/'); for (var i = parts.Length - 1; i > 2; i--) { var parentUrl = String.Join("/", parts.Splice(0, i)); var parentUri = new Uri(parentUrl); var parent = items.FirstOrDefault(dir => dir.Uri == parentUri); if (parent != null) { parent.Directories.Add(folder); break; } } } } public bool IsBusy { get { return _isBusy; } set { _isBusy = value; NotifyOfPropertyChange(()=>IsBusy); } } private void SetInitialSelections(AccountSettings account) { var selections = account.SelectiveFolders; //Initially, all nodes are checked //We need to *uncheck* the nodes that are not selected var allNodes = (from DirectoryRecord rootRecord in RootNodes from DirectoryRecord record in rootRecord select record).ToList(); //WARNING: Using IsChecked marks the item as REMOVED allNodes.Apply(record => record.IsExplicitlyChecked = false); if (selections.Count == 0) { // allNodes.Apply(record => record.IsChecked = false); return; } var selects = (from DirectoryRecord rootRecord in RootNodes from DirectoryRecord record in rootRecord where record.Uri !=null && selections.Contains(record.Uri.ToString()) select record).ToList(); //var shouldBeChecked = allNodes.Except(selects).ToList(); //WARNING: Using IsChecked marks the item as ADDED selects.Apply(record=>record.IsExplicitlyChecked=true); //shouldBeChecked.Apply(record => record.IsChecked = true); } protected string AccountName { get; set; } public void SaveChanges() { var uris = (from DirectoryRecord root in RootNodes from DirectoryRecord record in root where record.IsChecked == true && record.Uri != null select record.Uri).ToArray(); SaveSettings(uris); //RootNodes is an ObservableCollection, it can't be enumerated iterativelly var added = (from DirectoryRecord root in RootNodes from DirectoryRecord record in root where record.Added && record.Uri != null select record.Uri).ToArray(); var removed = (from DirectoryRecord root in RootNodes from DirectoryRecord record in root where record.Removed && record.Uri != null select record.Uri).ToArray(); //TODO: Include Uris for the containers as well _events.Publish(new SelectiveSynchChanges{Enabled=true, Account=Account,Uris=uris,Added=added,Removed=removed}); TryClose(true); } /* private bool _isEnabled; public bool IsEnabled { get { return _isEnabled; } set { _isEnabled = value; NotifyOfPropertyChange(()=>IsEnabled); } } */ private void SaveSettings(IEnumerable uris) { var selections = uris.Select(uri => uri.ToString()).ToArray(); //Account.SelectiveSyncEnabled = IsEnabled; Account.SelectiveFolders.Clear(); Account.SelectiveFolders.AddRange(selections); Settings.Default.Save(); } public void RejectChanges() { TryClose(false); } } }