Merge branch 'master' of https://code.grnet.gr/git/pithos-ms-client
[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)\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);\r
99         }\r
100 \r
101         private void LoadRootNode()\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 != "trash"\r
113                        select new DirectoryRecord\r
114                                   {\r
115                                       DisplayName = container.Name,\r
116                                       Uri=new Uri(client.StorageUrl,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=new Uri(client.StorageUrl,"../"+ account.name),\r
129                                 Directories=(from container in client.ListContainers(account.name)\r
130                                             select new DirectoryRecord\r
131                                                         {\r
132                                                             DisplayName=container.Name,\r
133                                                             Uri = new Uri(client.StorageUrl, "../" + 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 <<<<<<< HEAD
152 \r
153             // Add Local Folders\r
154             ///var localFolders = SelectiveExtensions.LocalFolders(AccountName, Account.RootPath,client.StorageUrl.AbsoluteUri);\r
155 \r
156             ///AppendToTree(localFolders, rootItem);\r
157 =======
158 >>>>>>> 8b6aa653d6c8a8cc3c40a93fca96459df726135a
159             \r
160             //For each local folder that doesn't exist in the server nodes \r
161             //find the best matching parent and add the folder below it.\r
162 \r
163             Execute.OnUIThread(() =>\r
164                                    {\r
165                                        RootNodes.Add(rootItem);\r
166                                        RootNodes.Add(othersNode);\r
167                                    });\r
168 \r
169             SetInitialSelections(Account);\r
170             \r
171             IsBusy = false;\r
172         }\r
173 \r
174         private static void AppendToTree(IEnumerable<DirectoryRecord> localFolders, DirectoryRecord rootItem)\r
175         {\r
176             foreach (var folder in localFolders)\r
177             {\r
178                 var items = from root in rootItem\r
179                             from child in root\r
180                             select child;\r
181                 //If this folder is not included in the server folders\r
182                 if (items.Any(dir => dir.Uri == folder.Uri)) \r
183                     continue;\r
184 \r
185                 //we need to add it\r
186                 //Find the best parent\r
187 \r
188                 //One way to do this, is to break the the Uri to its parts\r
189                 //and try to find a parent using progressively fewer parts\r
190                 var parts = folder.Uri.AbsoluteUri.Split('/');\r
191                 for (var i = parts.Length - 1; i > 2; i--)\r
192                 {\r
193                     var parentUrl = String.Join("/", parts.Splice(0, i));\r
194                     var parentUri = new Uri(parentUrl);\r
195 \r
196                     var parent = items.FirstOrDefault(dir => dir.Uri == parentUri);\r
197                     if (parent != null)\r
198                     {\r
199                         parent.Directories.Add(folder);\r
200                         break;\r
201                     }\r
202                 }\r
203             }\r
204         }\r
205 \r
206         public bool IsBusy\r
207         {\r
208             get {\r
209                 return _isBusy;\r
210             }\r
211             set {\r
212                 _isBusy = value;\r
213                 NotifyOfPropertyChange(()=>IsBusy);\r
214             }\r
215         }\r
216 \r
217         private void SetInitialSelections(AccountSettings account)\r
218         {\r
219             var selections = account.SelectiveFolders;\r
220 \r
221 \r
222                 \r
223             //Initially, all nodes are checked\r
224             //We need to *uncheck* the nodes that are not selected\r
225 \r
226             var allNodes = (from DirectoryRecord rootRecord in RootNodes\r
227                            from DirectoryRecord record in rootRecord\r
228                            select record).ToList();\r
229             //WARNING: Using IsChecked marks the item as REMOVED\r
230             allNodes.Apply(record => record.IsExplicitlyChecked = false);\r
231 \r
232             if (selections.Count == 0)\r
233             {\r
234             //    allNodes.Apply(record => record.IsChecked = false);\r
235                 return;\r
236             } \r
237             \r
238             var selects = (from DirectoryRecord rootRecord in RootNodes\r
239                           from DirectoryRecord record in rootRecord\r
240                           where record.Uri !=null &&  selections.Contains(record.Uri.ToString())\r
241                           select record).ToList();\r
242             //var shouldBeChecked = allNodes.Except(selects).ToList();\r
243             \r
244             //WARNING: Using IsChecked marks the item as ADDED\r
245             selects.Apply(record=>record.IsExplicitlyChecked=true);\r
246 \r
247             //shouldBeChecked.Apply(record => record.IsChecked = true);\r
248             \r
249             \r
250 \r
251         }\r
252 \r
253         protected string AccountName { get; set; }\r
254 \r
255         public void SaveChanges()\r
256         {\r
257             var uris = (from DirectoryRecord root in RootNodes\r
258                         from DirectoryRecord record in root\r
259                         where record.IsChecked == true && record.Uri != null\r
260                         select record.Uri).ToArray();            \r
261 \r
262             SaveSettings(uris);\r
263             \r
264             //RootNodes is an ObservableCollection, it can't be enumerated iterativelly\r
265 \r
266             var added = (from DirectoryRecord root in RootNodes\r
267                          from DirectoryRecord record in root\r
268                          where record.Added && record.Uri != null\r
269                          select record.Uri).ToArray();\r
270             var removed = (from DirectoryRecord root in RootNodes\r
271                            from DirectoryRecord record in root\r
272                           where record.Removed && record.Uri != null\r
273                          select record.Uri).ToArray();\r
274             //TODO: Include Uris for the containers as well\r
275             _events.Publish(new SelectiveSynchChanges{Enabled=IsEnabled, Account=Account,Uris=uris,Added=added,Removed=removed});\r
276             \r
277 \r
278             \r
279 \r
280             TryClose(true);\r
281         }\r
282 \r
283         private bool _isEnabled;\r
284         public bool IsEnabled\r
285         {\r
286             get { return _isEnabled; }\r
287             set\r
288             {\r
289                 _isEnabled = value;\r
290                 NotifyOfPropertyChange(()=>IsEnabled);\r
291             }\r
292         }\r
293 \r
294         private void SaveSettings(IEnumerable<Uri> uris)\r
295         {\r
296             var selections = uris.Select(uri => uri.ToString()).ToArray();\r
297             Account.SelectiveSyncEnabled = IsEnabled;\r
298             Account.SelectiveFolders.Clear();\r
299             Account.SelectiveFolders.AddRange(selections);\r
300             Settings.Default.Save();            \r
301         }        \r
302 \r
303         public void RejectChanges()\r
304         {\r
305             TryClose(false);\r
306         }\r
307     }\r
308 }\r