Selective Sync now shows both server AND local folders
[pithos-ms-client] / trunk / Pithos.Client.WPF / SelectiveSynch / SelectiveSynchViewModel.cs
1 #region\r
2 /* -----------------------------------------------------------------------\r
3  * <copyright file="SelectiveSynchViewModel.cs" company="GRNet">\r
4  * \r
5  * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
6  *\r
7  * Redistribution and use in source and binary forms, with or\r
8  * without modification, are permitted provided that the following\r
9  * conditions are met:\r
10  *\r
11  *   1. Redistributions of source code must retain the above\r
12  *      copyright notice, this list of conditions and the following\r
13  *      disclaimer.\r
14  *\r
15  *   2. Redistributions in binary form must reproduce the above\r
16  *      copyright notice, this list of conditions and the following\r
17  *      disclaimer in the documentation and/or other materials\r
18  *      provided with the distribution.\r
19  *\r
20  *\r
21  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
22  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
24  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
25  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
28  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
32  * POSSIBILITY OF SUCH DAMAGE.\r
33  *\r
34  * The views and conclusions contained in the software and\r
35  * documentation are those of the authors and should not be\r
36  * interpreted as representing official policies, either expressed\r
37  * or implied, of GRNET S.A.\r
38  * </copyright>\r
39  * -----------------------------------------------------------------------\r
40  */\r
41 #endregion\r
42 using System;\r
43 using System.Collections.Generic;\r
44 using System.Collections.ObjectModel;\r
45 using System.Linq;\r
46 using System.Threading.Tasks;\r
47 using Caliburn.Micro;\r
48 using Pithos.Client.WPF.Properties;\r
49 using Pithos.Client.WPF.Utils;\r
50 using Pithos.Core;\r
51 using Pithos.Interfaces;\r
52 using Pithos.Network;\r
53 \r
54 namespace Pithos.Client.WPF.SelectiveSynch\r
55 {\r
56     class SelectiveSynchViewModel:Screen\r
57     {        \r
58         private readonly IEventAggregator _events ;\r
59 \r
60 \r
61         public AccountSettings Account { get; set; }\r
62 \r
63         private readonly ObservableCollection<DirectoryRecord> _rootNodes=new ObservableCollection<DirectoryRecord>();\r
64         public ObservableCollection<DirectoryRecord> RootNodes\r
65         {\r
66             get { return _rootNodes; }\r
67         }\r
68 \r
69         private ObservableCollection<ObjectInfo> _checks;\r
70         //private readonly PithosMonitor _monitor;\r
71         private bool _isBusy=true;\r
72         private string _apiKey;\r
73 \r
74         public ObservableCollection<ObjectInfo> Checks\r
75         {\r
76             get { return _checks; }\r
77         }\r
78 \r
79         public void GetChecks()\r
80         {\r
81             var root = RootNodes[0];            \r
82             _checks = new ObservableCollection<ObjectInfo>(\r
83                 from DirectoryRecord record in root\r
84                 where record.IsChecked==true\r
85                 select record.ObjectInfo);\r
86             NotifyOfPropertyChange(() => Checks);\r
87         }\r
88 \r
89         public SelectiveSynchViewModel(/*PithosMonitor monitor,*/ IEventAggregator events, AccountSettings account, string apiKey)\r
90         {\r
91             Account = account;\r
92             AccountName = account.AccountName;\r
93             DisplayName = String.Format("Selective folder synchronization for {0}",account.AccountName);\r
94             //_monitor = monitor;\r
95             _events = events;\r
96             _apiKey = apiKey;\r
97             TaskEx.Run(LoadRootNode);\r
98         }\r
99 \r
100         private void LoadRootNode()\r
101         {            \r
102             //TODO: Check this\r
103             var client = new CloudFilesClient(AccountName,_apiKey){AuthenticationUrl=Account.ServerUrl,UsePithos=true};\r
104             client.Authenticate();\r
105             \r
106             //NEED to get the local folders here as well,\r
107             // and combine them with the cloud folders\r
108 \r
109 \r
110             var dirs = from container in client.ListContainers(AccountName)  \r
111                        where container.Name != "trash"\r
112                        select new DirectoryRecord\r
113                                   {\r
114                                       DisplayName = container.Name,\r
115                                       Uri=new Uri(client.StorageUrl,String.Format(@"{0}/{1}",Account.AccountName, container.Name)),\r
116                                       Directories = (from dir in client.ListObjects(AccountName, container.Name)                                                                                                          \r
117                                                      select dir).ToTree()\r
118                                   };\r
119             var ownFolders = dirs.ToList();\r
120 \r
121 \r
122 \r
123             var accountNodes=from account in client.ListSharingAccounts()\r
124                              select new DirectoryRecord\r
125                              {\r
126                                 DisplayName=account.name,\r
127                                 Uri=new Uri(client.StorageUrl,"../"+ account.name),\r
128                                 Directories=(from container in client.ListContainers(account.name)\r
129                                             select new DirectoryRecord\r
130                                                         {\r
131                                                             DisplayName=container.Name,\r
132                                                             Uri = new Uri(client.StorageUrl, "../" + account.name + "/" + container.Name),\r
133                                                             Directories=(from folder in client.ListObjects(account.name,container.Name)                                                                        \r
134                                                                         select folder).ToTree()\r
135                                                         }).ToList()\r
136                              };                                                          \r
137 \r
138             var othersNode = new DirectoryRecord\r
139                                  {\r
140                                      DisplayName = "Shared to me",\r
141                                      Directories=accountNodes.ToList()\r
142                                  };\r
143 \r
144             \r
145             var rootItem = new DirectoryRecord\r
146                                {\r
147                                    DisplayName = AccountName ,\r
148                                    Directories = ownFolders.ToList()\r
149                                };\r
150 \r
151             var localFolders = SelectiveExtensions.LocalFolders(AccountName, Account.RootPath,client.StorageUrl.AbsoluteUri);\r
152 \r
153             foreach (var folder in localFolders)\r
154             {\r
155                 //If this folder is not included in the server folders\r
156                 if (!rootItem.Directories.Any(dir => dir.Uri == folder.Uri))\r
157                 {\r
158                     //we need to add it\r
159                     //Find the best parent\r
160 \r
161                     //One way to do this, is to break the the Uri to its parts\r
162                     //and try to find a parent using progressively fewer parts\r
163                     var parts = folder.Uri.AbsoluteUri.Split('/');\r
164                     for (var i = parts.Length - 1; i > 0; i--)\r
165                     {\r
166                         var parentUrl = String.Join("/",parts.Splice(0, i));\r
167                         var parentUri = new Uri(parentUrl);\r
168 \r
169                         var parent = rootItem.Directories.FirstOrDefault(dir => dir.Uri == parentUri);\r
170                         if (parent != null)\r
171                         {\r
172                             parent.Directories.Add(folder);\r
173                             break;\r
174                         }\r
175                     }\r
176                 }\r
177             }\r
178             \r
179             //For each local folder that doesn't exist in the server nodes \r
180             //find the best matching parent and add the folder below it.\r
181 \r
182             Execute.OnUIThread(() =>\r
183                                    {\r
184                                        RootNodes.Add(rootItem);\r
185                                        RootNodes.Add(othersNode);\r
186                                    });\r
187 \r
188             SetInitialSelections(Account);\r
189             \r
190             IsBusy = false;\r
191         }\r
192 \r
193         public bool IsBusy\r
194         {\r
195             get {\r
196                 return _isBusy;\r
197             }\r
198             set {\r
199                 _isBusy = value;\r
200                 NotifyOfPropertyChange(()=>IsBusy);\r
201             }\r
202         }\r
203 \r
204         private void SetInitialSelections(AccountSettings account)\r
205         {\r
206             var selections = account.SelectiveFolders;\r
207 \r
208 \r
209                 \r
210             //Initially, all nodes are checked\r
211             //We need to *uncheck* the nodes that are not selected\r
212 \r
213             var allNodes = (from DirectoryRecord rootRecord in RootNodes\r
214                            from DirectoryRecord record in rootRecord\r
215                            select record).ToList();\r
216             //WARNING: Using IsChecked marks the item as REMOVED\r
217             allNodes.Apply(record => record.IsExplicitlyChecked = false);\r
218 \r
219             if (selections.Count == 0)\r
220             {\r
221             //    allNodes.Apply(record => record.IsChecked = false);\r
222                 return;\r
223             } \r
224             \r
225             var selects = (from DirectoryRecord rootRecord in RootNodes\r
226                           from DirectoryRecord record in rootRecord\r
227                           where record.Uri !=null &&  selections.Contains(record.Uri.ToString())\r
228                           select record).ToList();\r
229             //var shouldBeChecked = allNodes.Except(selects).ToList();\r
230             \r
231             //WARNING: Using IsChecked marks the item as ADDED\r
232             selects.Apply(record=>record.IsExplicitlyChecked=true);\r
233 \r
234             //shouldBeChecked.Apply(record => record.IsChecked = true);\r
235             \r
236             \r
237 \r
238         }\r
239 \r
240         protected string AccountName { get; set; }\r
241 \r
242         public void SaveChanges()\r
243         {\r
244             var uris = (from DirectoryRecord root in RootNodes\r
245                         from DirectoryRecord record in root\r
246                         where record.IsChecked == true && record.Uri != null\r
247                         select record.Uri).ToArray();            \r
248 \r
249             SaveSettings(uris);\r
250             \r
251             //RootNodes is an ObservableCollection, it can't be enumerated iterativelly\r
252 \r
253             var added = (from DirectoryRecord root in RootNodes\r
254                          from DirectoryRecord record in root\r
255                          where record.Added && record.Uri != null\r
256                          select record.Uri).ToArray();\r
257             var removed = (from DirectoryRecord root in RootNodes\r
258                            from DirectoryRecord record in root\r
259                           where record.Removed && record.Uri != null\r
260                          select record.Uri).ToArray();\r
261             //TODO: Include Uris for the containers as well\r
262             _events.Publish(new SelectiveSynchChanges{Account=Account,Uris=uris,Added=added,Removed=removed});\r
263             \r
264 \r
265             \r
266 \r
267             TryClose(true);\r
268         }\r
269 \r
270         \r
271         private void SaveSettings(IEnumerable<Uri> uris)\r
272         {\r
273             var selections = uris.Select(uri => uri.ToString()).ToArray();\r
274 \r
275             Account.SelectiveFolders.Clear();\r
276             Account.SelectiveFolders.AddRange(selections);\r
277             Settings.Default.Save();            \r
278         }        \r
279 \r
280         public void RejectChanges()\r
281         {\r
282             TryClose(false);\r
283         }\r
284     }\r
285 }\r