e6dff26354b813169df6186ee96a903c4d7e5ec3
[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 readonly 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,bool forSelectiveActivation)\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             //IsEnabled = account.SelectiveSyncEnabled;\r
98             TaskEx.Run(()=>LoadRootNode(forSelectiveActivation));\r
99         }\r
100 \r
101         private void LoadRootNode(bool forSelectiveActivation)\r
102         {            \r
103             //TODO: Check this\r
104             var client = new CloudFilesClient(AccountName,_apiKey){AuthenticationUrl=Account.ServerUrl,UsePithos=true};\r
105             client.Authenticate();\r
106             \r
107             //NEED to get the local folders here as well,\r
108             // and combine them with the cloud folders\r
109 \r
110 \r
111             var dirs = from container in client.ListContainers(AccountName)  \r
112                        where container.Name.ToUnescapedString() != "trash"\r
113                        select new DirectoryRecord\r
114                                   {\r
115                                       DisplayName = container.Name.ToUnescapedString(),\r
116                                       Uri=client.StorageUrl.Combine(String.Format(@"{0}/{1}",Account.AccountName, container.Name)),\r
117                                       Directories = (from dir in client.ListObjects(AccountName, container.Name)                                                                                                          \r
118                                                      select dir).ToTree()\r
119                                   };\r
120             var ownFolders = dirs.ToList();\r
121 \r
122 \r
123 \r
124             var accountNodes=from account in client.ListSharingAccounts()\r
125                              select new DirectoryRecord\r
126                              {\r
127                                 DisplayName=account.name,\r
128                                 Uri=client.StorageUrl.Combine("../"+ account.name),\r
129                                 Directories=(from container in client.ListContainers(account.name)\r
130                                             select new DirectoryRecord\r
131                                                         {\r
132                                                             DisplayName=container.Name.ToUnescapedString(),\r
133                                                             Uri = client.StorageUrl.Combine("../" + account.name + "/" + container.Name),\r
134                                                             Directories=(from folder in client.ListObjects(account.name,container.Name)                                                                        \r
135                                                                         select folder).ToTree()\r
136                                                         }).ToList()\r
137                              };                                                          \r
138 \r
139             var othersNode = new DirectoryRecord\r
140                                  {\r
141                                      DisplayName = "Shared with me",\r
142                                      Directories=accountNodes.ToList()\r
143                                  };\r
144 \r
145             \r
146             var rootItem = new DirectoryRecord\r
147                                {\r
148                                    DisplayName = AccountName ,\r
149                                    Directories = ownFolders.ToList()\r
150                                };\r
151 \r
152             // Add Local Folders\r
153             //var localFolders = SelectiveExtensions.LocalFolders(AccountName, Account.RootPath,client.StorageUrl.AbsoluteUri);\r
154             //AppendToTree(localFolders, rootItem);\r
155 \r
156             \r
157             //For each local folder that doesn't exist in the server nodes \r
158             //find the best matching parent and add the folder below it.\r
159 \r
160             Execute.OnUIThread(() =>\r
161                                    {\r
162                                        RootNodes.Add(rootItem);\r
163                                        RootNodes.Add(othersNode);\r
164                                    });\r
165 \r
166             SetInitialSelections(Account,forSelectiveActivation);\r
167             \r
168             IsBusy = false;\r
169         }\r
170 \r
171         private static void AppendToTree(IEnumerable<DirectoryRecord> localFolders, DirectoryRecord rootItem)\r
172         {\r
173             foreach (var folder in localFolders)\r
174             {\r
175                 var items = from root in rootItem\r
176                             from child in root\r
177                             select child;\r
178                 //If this folder is not included in the server folders\r
179                 if (items.Any(dir => dir.Uri == folder.Uri)) \r
180                     continue;\r
181 \r
182                 //we need to add it\r
183                 //Find the best parent\r
184 \r
185                 //One way to do this, is to break the the Uri to its parts\r
186                 //and try to find a parent using progressively fewer parts\r
187                 var parts = folder.Uri.AbsoluteUri.Split('/');\r
188                 for (var i = parts.Length - 1; i > 2; i--)\r
189                 {\r
190                     var parentUrl = String.Join("/", parts.Splice(0, i));\r
191                     var parentUri = new Uri(parentUrl);\r
192 \r
193                     var parent = items.FirstOrDefault(dir => dir.Uri == parentUri);\r
194                     if (parent != null)\r
195                     {\r
196                         parent.Directories.Add(folder);\r
197                         break;\r
198                     }\r
199                 }\r
200             }\r
201         }\r
202 \r
203         public bool IsBusy\r
204         {\r
205             get {\r
206                 return _isBusy;\r
207             }\r
208             set {\r
209                 _isBusy = value;\r
210                 NotifyOfPropertyChange(()=>IsBusy);\r
211             }\r
212         }\r
213 \r
214         private void SetInitialSelections(AccountSettings account,bool forSelectiveActivation)\r
215         {\r
216             var selections = account.SelectiveFolders;\r
217 \r
218 \r
219                 \r
220             //Initially, all nodes are checked\r
221             //We need to *uncheck* the nodes that are not selected\r
222 \r
223             var allNodes = (from DirectoryRecord rootRecord in RootNodes\r
224                            from DirectoryRecord record in rootRecord\r
225                            select record).ToList();\r
226             //WARNING: Using IsChecked marks the item as REMOVED\r
227             allNodes.Apply(record => record.IsExplicitlyChecked = false);\r
228 \r
229             if (selections.Count == 0)\r
230             {\r
231             //    allNodes.Apply(record => record.IsChecked = false);\r
232                 return;\r
233             } \r
234             \r
235             var selects = (from DirectoryRecord rootRecord in RootNodes\r
236                           from DirectoryRecord record in rootRecord\r
237                           where record.Uri !=null &&  selections.Contains(record.Uri.ToString())\r
238                           select record).ToList();\r
239             //var shouldBeChecked = allNodes.Except(selects).ToList();\r
240             \r
241             //WARNING: Using IsChecked marks the item as ADDED\r
242             selects.Apply(record => record.IsExplicitlyChecked = !forSelectiveActivation);\r
243 \r
244             //If any of the root nodes has at least one selected child, set it to gray\r
245             SetRootNodeSelections(forSelectiveActivation);\r
246 \r
247             //shouldBeChecked.Apply(record => record.IsChecked = true);\r
248 \r
249 \r
250 \r
251         }\r
252 \r
253         private void SetRootNodeSelections(bool forSelectiveActivation)\r
254         {\r
255             var selectedRoots = from DirectoryRecord rootRecord in RootNodes\r
256                                 from DirectoryRecord record in rootRecord\r
257                                 where record.IsChecked == true\r
258                                 select rootRecord;\r
259             selectedRoots.Apply(record => record.IsExplicitlyChecked = !forSelectiveActivation);\r
260         }\r
261 \r
262         protected string AccountName { get; set; }\r
263 \r
264         public void SaveChanges()\r
265         {\r
266             var uris = (from DirectoryRecord root in RootNodes\r
267                         from DirectoryRecord record in root\r
268                         where record.IsChecked == true && record.Uri != null\r
269                         select record.Uri).ToArray();            \r
270 \r
271             SaveSettings(uris);\r
272             \r
273             //RootNodes is an ObservableCollection, it can't be enumerated iterativelly\r
274 \r
275             var added = (from DirectoryRecord root in RootNodes\r
276                          from DirectoryRecord record in root\r
277                          where record.Added && record.Uri != null\r
278                          select record.Uri).ToArray();\r
279             var removed = (from DirectoryRecord root in RootNodes\r
280                            from DirectoryRecord record in root\r
281                           where record.Removed && record.Uri != null\r
282                          select record.Uri).ToArray();\r
283             //TODO: Include Uris for the containers as well\r
284             _events.Publish(new SelectiveSynchChanges{Enabled=true, Account=Account,Uris=uris,Added=added,Removed=removed});\r
285             \r
286 \r
287             \r
288 \r
289             TryClose(true);\r
290         }\r
291 \r
292 /*\r
293         private bool _isEnabled;\r
294         public bool IsEnabled\r
295         {\r
296             get { return _isEnabled; }\r
297             set\r
298             {\r
299                 _isEnabled = value;\r
300                 NotifyOfPropertyChange(()=>IsEnabled);\r
301             }\r
302         }\r
303 */\r
304 \r
305         private void SaveSettings(IEnumerable<Uri> uris)\r
306         {\r
307             var selections = uris.Select(uri => uri.ToString()).ToArray();\r
308             //Account.SelectiveSyncEnabled = IsEnabled;\r
309             Account.SelectiveFolders.Clear();\r
310             Account.SelectiveFolders.AddRange(selections);\r
311             Settings.Default.Save();            \r
312         }        \r
313 \r
314         public void RejectChanges()\r
315         {\r
316             TryClose(false);\r
317         }\r
318     }\r
319 }\r