2 /* -----------------------------------------------------------------------
\r
3 * <copyright file="SelectiveSynchViewModel.cs" company="GRNet">
\r
5 * Copyright 2011-2012 GRNET S.A. All rights reserved.
\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
11 * 1. Redistributions of source code must retain the above
\r
12 * copyright notice, this list of conditions and the following
\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
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
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
39 * -----------------------------------------------------------------------
\r
43 using System.Collections.Generic;
\r
44 using System.Collections.ObjectModel;
\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
51 using Pithos.Interfaces;
\r
52 using Pithos.Network;
\r
54 namespace Pithos.Client.WPF.SelectiveSynch
\r
56 class SelectiveSynchViewModel:Screen
\r
58 private readonly IEventAggregator _events ;
\r
61 public AccountSettings Account { get; set; }
\r
63 private readonly ObservableCollection<DirectoryRecord> _rootNodes=new ObservableCollection<DirectoryRecord>();
\r
64 public ObservableCollection<DirectoryRecord> RootNodes
\r
66 get { return _rootNodes; }
\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
74 public ObservableCollection<ObjectInfo> Checks
\r
76 get { return _checks; }
\r
79 public void GetChecks()
\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
89 public SelectiveSynchViewModel(/*PithosMonitor monitor,*/ IEventAggregator events, AccountSettings account, string apiKey,bool forSelectiveActivation)
\r
92 AccountName = account.AccountName;
\r
93 DisplayName = String.Format("Selective folder synchronization for {0}",account.AccountName);
\r
94 //_monitor = monitor;
\r
97 //IsEnabled = account.SelectiveSyncEnabled;
\r
98 TaskEx.Run(()=>LoadRootNode(forSelectiveActivation));
\r
101 private void LoadRootNode(bool forSelectiveActivation)
\r
104 var client = new CloudFilesClient(AccountName,_apiKey){AuthenticationUrl=Account.ServerUrl,UsePithos=true};
\r
105 client.Authenticate();
\r
107 //NEED to get the local folders here as well,
\r
108 // and combine them with the cloud folders
\r
111 var dirs = from container in client.ListContainers(AccountName)
\r
112 where container.Name.ToUnescapedString() != "trash"
\r
113 select new DirectoryRecord
\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
120 var ownFolders = dirs.ToList();
\r
124 var accountNodes=from account in client.ListSharingAccounts()
\r
125 select new DirectoryRecord
\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
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
139 var othersNode = new DirectoryRecord
\r
141 DisplayName = "Shared with me",
\r
142 Directories=accountNodes.ToList()
\r
146 var rootItem = new DirectoryRecord
\r
148 DisplayName = AccountName ,
\r
149 Directories = ownFolders.ToList()
\r
152 // Add Local Folders
\r
153 //var localFolders = SelectiveExtensions.LocalFolders(AccountName, Account.RootPath,client.StorageUrl.AbsoluteUri);
\r
154 //AppendToTree(localFolders, rootItem);
\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
160 Execute.OnUIThread(() =>
\r
162 RootNodes.Add(rootItem);
\r
163 RootNodes.Add(othersNode);
\r
166 SetInitialSelections(Account,forSelectiveActivation);
\r
171 private static void AppendToTree(IEnumerable<DirectoryRecord> localFolders, DirectoryRecord rootItem)
\r
173 foreach (var folder in localFolders)
\r
175 var items = from root in rootItem
\r
178 //If this folder is not included in the server folders
\r
179 if (items.Any(dir => dir.Uri == folder.Uri))
\r
182 //we need to add it
\r
183 //Find the best parent
\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
190 var parentUrl = String.Join("/", parts.Splice(0, i));
\r
191 var parentUri = new Uri(parentUrl);
\r
193 var parent = items.FirstOrDefault(dir => dir.Uri == parentUri);
\r
194 if (parent != null)
\r
196 parent.Directories.Add(folder);
\r
210 NotifyOfPropertyChange(()=>IsBusy);
\r
214 private void SetInitialSelections(AccountSettings account,bool forSelectiveActivation)
\r
216 var selections = account.SelectiveFolders;
\r
220 //Initially, all nodes are checked
\r
221 //We need to *uncheck* the nodes that are not selected
\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
229 if (selections.Count == 0)
\r
231 // allNodes.Apply(record => record.IsChecked = false);
\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
241 //WARNING: Using IsChecked marks the item as ADDED
\r
242 selects.Apply(record => record.IsExplicitlyChecked = !forSelectiveActivation);
\r
244 //If any of the root nodes has at least one selected child, set it to gray
\r
245 SetRootNodeSelections(forSelectiveActivation);
\r
247 //shouldBeChecked.Apply(record => record.IsChecked = true);
\r
253 private void SetRootNodeSelections(bool forSelectiveActivation)
\r
255 var selectedRoots = from DirectoryRecord rootRecord in RootNodes
\r
256 from DirectoryRecord record in rootRecord
\r
257 where record.IsChecked == true
\r
259 selectedRoots.Apply(record => record.IsExplicitlyChecked = !forSelectiveActivation);
\r
262 protected string AccountName { get; set; }
\r
264 public void SaveChanges()
\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
271 SaveSettings(uris);
\r
273 //RootNodes is an ObservableCollection, it can't be enumerated iterativelly
\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
293 private bool _isEnabled;
\r
294 public bool IsEnabled
\r
296 get { return _isEnabled; }
\r
299 _isEnabled = value;
\r
300 NotifyOfPropertyChange(()=>IsEnabled);
\r
305 private void SaveSettings(IEnumerable<Uri> uris)
\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
314 public void RejectChanges()
\r