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