Statistics
| Branch: | Revision:

root / trunk / Pithos.Client.WPF / Shell / ShellViewModel.cs @ f2d88248

History | View | Annotate | Download (26.9 kB)

1 255f5f86 Panagiotis Kanavos
#region
2 255f5f86 Panagiotis Kanavos
/* -----------------------------------------------------------------------
3 255f5f86 Panagiotis Kanavos
 * <copyright file="ShellViewModel.cs" company="GRNet">
4 255f5f86 Panagiotis Kanavos
 * 
5 255f5f86 Panagiotis Kanavos
 * Copyright 2011-2012 GRNET S.A. All rights reserved.
6 255f5f86 Panagiotis Kanavos
 *
7 255f5f86 Panagiotis Kanavos
 * Redistribution and use in source and binary forms, with or
8 255f5f86 Panagiotis Kanavos
 * without modification, are permitted provided that the following
9 255f5f86 Panagiotis Kanavos
 * conditions are met:
10 255f5f86 Panagiotis Kanavos
 *
11 255f5f86 Panagiotis Kanavos
 *   1. Redistributions of source code must retain the above
12 255f5f86 Panagiotis Kanavos
 *      copyright notice, this list of conditions and the following
13 255f5f86 Panagiotis Kanavos
 *      disclaimer.
14 255f5f86 Panagiotis Kanavos
 *
15 255f5f86 Panagiotis Kanavos
 *   2. Redistributions in binary form must reproduce the above
16 255f5f86 Panagiotis Kanavos
 *      copyright notice, this list of conditions and the following
17 255f5f86 Panagiotis Kanavos
 *      disclaimer in the documentation and/or other materials
18 255f5f86 Panagiotis Kanavos
 *      provided with the distribution.
19 255f5f86 Panagiotis Kanavos
 *
20 255f5f86 Panagiotis Kanavos
 *
21 255f5f86 Panagiotis Kanavos
 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22 255f5f86 Panagiotis Kanavos
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 255f5f86 Panagiotis Kanavos
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 255f5f86 Panagiotis Kanavos
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25 255f5f86 Panagiotis Kanavos
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 255f5f86 Panagiotis Kanavos
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 255f5f86 Panagiotis Kanavos
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 255f5f86 Panagiotis Kanavos
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 255f5f86 Panagiotis Kanavos
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 255f5f86 Panagiotis Kanavos
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 255f5f86 Panagiotis Kanavos
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 255f5f86 Panagiotis Kanavos
 * POSSIBILITY OF SUCH DAMAGE.
33 255f5f86 Panagiotis Kanavos
 *
34 255f5f86 Panagiotis Kanavos
 * The views and conclusions contained in the software and
35 255f5f86 Panagiotis Kanavos
 * documentation are those of the authors and should not be
36 255f5f86 Panagiotis Kanavos
 * interpreted as representing official policies, either expressed
37 255f5f86 Panagiotis Kanavos
 * or implied, of GRNET S.A.
38 255f5f86 Panagiotis Kanavos
 * </copyright>
39 255f5f86 Panagiotis Kanavos
 * -----------------------------------------------------------------------
40 255f5f86 Panagiotis Kanavos
 */
41 255f5f86 Panagiotis Kanavos
#endregion
42 255f5f86 Panagiotis Kanavos
using System.Collections.Concurrent;
43 c53aa229 Panagiotis Kanavos
using System.Diagnostics;
44 c28a075a Panagiotis Kanavos
using System.Diagnostics.Contracts;
45 c53aa229 Panagiotis Kanavos
using System.IO;
46 c28a075a Panagiotis Kanavos
using System.Net;
47 d17258c2 Panagiotis Kanavos
using System.Reflection;
48 c53aa229 Panagiotis Kanavos
using System.Runtime.InteropServices;
49 c53aa229 Panagiotis Kanavos
using System.ServiceModel;
50 c53aa229 Panagiotis Kanavos
using System.Threading.Tasks;
51 c53aa229 Panagiotis Kanavos
using System.Windows;
52 fb9d6e00 Panagiotis Kanavos
using System.Windows.Controls.Primitives;
53 9bae55d1 Panagiotis Kanavos
using Caliburn.Micro;
54 c53aa229 Panagiotis Kanavos
using Hardcodet.Wpf.TaskbarNotification;
55 c53aa229 Panagiotis Kanavos
using Pithos.Client.WPF.Configuration;
56 42800be8 Panagiotis Kanavos
using Pithos.Client.WPF.FileProperties;
57 65282d58 Panagiotis Kanavos
using Pithos.Client.WPF.Preferences;
58 1e26eceb Panagiotis Kanavos
using Pithos.Client.WPF.Properties;
59 d3a13891 Panagiotis Kanavos
using Pithos.Client.WPF.SelectiveSynch;
60 42800be8 Panagiotis Kanavos
using Pithos.Client.WPF.Services;
61 5cb9d74f Panagiotis Kanavos
using Pithos.Client.WPF.Shell;
62 c53aa229 Panagiotis Kanavos
using Pithos.Core;
63 29a6b387 Panagiotis Kanavos
using Pithos.Core.Agents;
64 c53aa229 Panagiotis Kanavos
using Pithos.Interfaces;
65 c53aa229 Panagiotis Kanavos
using System;
66 c53aa229 Panagiotis Kanavos
using System.Collections.Generic;
67 c53aa229 Panagiotis Kanavos
using System.Linq;
68 0bd56b7c Panagiotis Kanavos
using Pithos.Network;
69 c53aa229 Panagiotis Kanavos
using StatusService = Pithos.Client.WPF.Services.StatusService;
70 9bae55d1 Panagiotis Kanavos
71 9bae55d1 Panagiotis Kanavos
namespace Pithos.Client.WPF {
72 4ec636f6 Panagiotis Kanavos
	using System.ComponentModel.Composition;
73 9bae55d1 Panagiotis Kanavos
74 4ec636f6 Panagiotis Kanavos
	
75 cf761c0d Panagiotis Kanavos
	///<summary>
76 cf761c0d Panagiotis Kanavos
	/// The "shell" of the Pithos application displays the taskbar  icon, menu and notifications.
77 cf761c0d Panagiotis Kanavos
	/// The shell also hosts the status service called by shell extensions to retrieve file info
78 cf761c0d Panagiotis Kanavos
	///</summary>
79 cf761c0d Panagiotis Kanavos
	///<remarks>
80 cf761c0d Panagiotis Kanavos
	/// It is a strange "shell" as its main visible element is an icon instead of a window
81 cf761c0d Panagiotis Kanavos
	/// The shell subscribes to the following events:
82 cf761c0d Panagiotis Kanavos
	/// * Notification:  Raised by components that want to notify the user. Usually displayed in a balloon
83 cf761c0d Panagiotis Kanavos
	/// * SelectiveSynchChanges: Notifies that the user made changes to the selective synch folders for an account. Raised by the Selective Synch dialog. Located here because the monitors are here
84 cf761c0d Panagiotis Kanavos
	/// * ShowFilePropertiesEvent: Raised when a shell command requests the display of the file/container properties dialog
85 cf761c0d Panagiotis Kanavos
	///</remarks>		
86 cf761c0d Panagiotis Kanavos
	//TODO: CODE SMELL Why does the shell handle the SelectiveSynchChanges?
87 4ec636f6 Panagiotis Kanavos
	[Export(typeof(IShell))]
88 4ec636f6 Panagiotis Kanavos
	public class ShellViewModel : Screen, IStatusNotification, IShell,
89 4ec636f6 Panagiotis Kanavos
		IHandle<Notification>, IHandle<SelectiveSynchChanges>, IHandle<ShowFilePropertiesEvent>
90 4ec636f6 Panagiotis Kanavos
	{
91 cf761c0d Panagiotis Kanavos
		//The Status Checker provides the current synch state
92 cf761c0d Panagiotis Kanavos
		//TODO: Could we remove the status checker and use events in its place?
93 4f6d51d4 Panagiotis Kanavos
		private readonly IStatusChecker _statusChecker;
94 4f6d51d4 Panagiotis Kanavos
		private readonly IEventAggregator _events;
95 c53aa229 Panagiotis Kanavos
96 4ec636f6 Panagiotis Kanavos
		public PithosSettings Settings { get; private set; }
97 c53aa229 Panagiotis Kanavos
98 4ec636f6 Panagiotis Kanavos
99 255f5f86 Panagiotis Kanavos
		private readonly ConcurrentDictionary<string, PithosMonitor> _monitors = new ConcurrentDictionary<string, PithosMonitor>();
100 cf761c0d Panagiotis Kanavos
		///<summary>
101 cf761c0d Panagiotis Kanavos
		/// Dictionary of account monitors, keyed by account
102 cf761c0d Panagiotis Kanavos
		///</summary>
103 cf761c0d Panagiotis Kanavos
		///<remarks>
104 cf761c0d Panagiotis Kanavos
		/// One monitor class is created for each account. The Shell needs access to the monitors to execute start/stop/pause commands,
105 cf761c0d Panagiotis Kanavos
		/// retrieve account and boject info		
106 cf761c0d Panagiotis Kanavos
		///</remarks>
107 cf761c0d Panagiotis Kanavos
		// TODO: Does the Shell REALLY need access to the monitors? Could we achieve the same results with a better design?
108 cf761c0d Panagiotis Kanavos
		// TODO: The monitors should be internal to Pithos.Core, even though exposing them makes coding of the Object and Container windows easier
109 255f5f86 Panagiotis Kanavos
		public ConcurrentDictionary<string, PithosMonitor> Monitors
110 4ec636f6 Panagiotis Kanavos
		{
111 4ec636f6 Panagiotis Kanavos
			get { return _monitors; }
112 4ec636f6 Panagiotis Kanavos
		}
113 c53aa229 Panagiotis Kanavos
114 cf761c0d Panagiotis Kanavos
115 255f5f86 Panagiotis Kanavos
		///<summary>
116 255f5f86 Panagiotis Kanavos
		/// The status service is used by Shell extensions to retrieve file status information
117 255f5f86 Panagiotis Kanavos
		///</summary>
118 255f5f86 Panagiotis Kanavos
		//TODO: CODE SMELL! This is the shell! While hosting in the shell makes executing start/stop commands easier, it is still a smell
119 255f5f86 Panagiotis Kanavos
		private ServiceHost _statusService;
120 c53aa229 Panagiotis Kanavos
121 cf761c0d Panagiotis Kanavos
		//Logging in the Pithos client is provided by log4net
122 4ec636f6 Panagiotis Kanavos
		private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Pithos");
123 c53aa229 Panagiotis Kanavos
124 255f5f86 Panagiotis Kanavos
		//Lazily initialized File Version info. This is done once and lazily to avoid blocking the UI
125 255f5f86 Panagiotis Kanavos
		private Lazy<FileVersionInfo> _fileVersion;
126 422c9598 Panagiotis Kanavos
127 cf761c0d Panagiotis Kanavos
		///<summary>
128 cf761c0d Panagiotis Kanavos
		/// The Shell depends on MEF to provide implementations for windowManager, events, the status checker service and the settings
129 cf761c0d Panagiotis Kanavos
		///</summary>
130 cf761c0d Panagiotis Kanavos
		///<remarks>
131 cf761c0d Panagiotis Kanavos
		/// The PithosSettings class encapsulates the app's settings to abstract their storage mechanism (App settings, a database or registry)
132 cf761c0d Panagiotis Kanavos
		///</remarks>
133 4ec636f6 Panagiotis Kanavos
		[ImportingConstructor]		
134 4ec636f6 Panagiotis Kanavos
		public ShellViewModel(IWindowManager windowManager, IEventAggregator events, IStatusChecker statusChecker, PithosSettings settings)
135 4ec636f6 Panagiotis Kanavos
		{
136 4ec636f6 Panagiotis Kanavos
			try
137 4ec636f6 Panagiotis Kanavos
			{
138 d3a13891 Panagiotis Kanavos
139 4ec636f6 Panagiotis Kanavos
				_windowManager = windowManager;
140 cf761c0d Panagiotis Kanavos
				//CHECK: Caliburn doesn't need explicit command construction
141 4ec636f6 Panagiotis Kanavos
				//OpenPithosFolderCommand = new PithosCommand(OpenPithosFolder);
142 4ec636f6 Panagiotis Kanavos
				_statusChecker = statusChecker;
143 cf761c0d Panagiotis Kanavos
				//The event subst
144 4ec636f6 Panagiotis Kanavos
				_events = events;
145 4ec636f6 Panagiotis Kanavos
				_events.Subscribe(this);
146 c53aa229 Panagiotis Kanavos
147 4ec636f6 Panagiotis Kanavos
				Settings = settings;
148 c53aa229 Panagiotis Kanavos
149 255f5f86 Panagiotis Kanavos
				Proxy.SetFromSettings(settings);
150 34bdb91d Panagiotis Kanavos
151 4ec636f6 Panagiotis Kanavos
				StatusMessage = "In Synch";
152 7e26c075 Panagiotis Kanavos
153 255f5f86 Panagiotis Kanavos
				_fileVersion=  new Lazy<FileVersionInfo>(() =>
154 255f5f86 Panagiotis Kanavos
				{
155 255f5f86 Panagiotis Kanavos
					Assembly assembly = Assembly.GetExecutingAssembly();
156 255f5f86 Panagiotis Kanavos
					var fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location);
157 255f5f86 Panagiotis Kanavos
					return fileVersion;
158 255f5f86 Panagiotis Kanavos
				});
159 4ec636f6 Panagiotis Kanavos
				_accounts.CollectionChanged += (sender, e) =>
160 4ec636f6 Panagiotis Kanavos
												   {
161 4ec636f6 Panagiotis Kanavos
													   NotifyOfPropertyChange(() => OpenFolderCaption);
162 4ec636f6 Panagiotis Kanavos
													   NotifyOfPropertyChange(() => HasAccounts);
163 4ec636f6 Panagiotis Kanavos
												   };
164 6aa29f4f Panagiotis Kanavos
165 4ec636f6 Panagiotis Kanavos
			}
166 4ec636f6 Panagiotis Kanavos
			catch (Exception exc)
167 4ec636f6 Panagiotis Kanavos
			{
168 4ec636f6 Panagiotis Kanavos
				Log.Error("Error while starting the ShellViewModel",exc);
169 4ec636f6 Panagiotis Kanavos
				throw;
170 4ec636f6 Panagiotis Kanavos
			}
171 4ec636f6 Panagiotis Kanavos
		}
172 c53aa229 Panagiotis Kanavos
173 6aa29f4f Panagiotis Kanavos
174 4ec636f6 Panagiotis Kanavos
		protected override void OnActivate()
175 4ec636f6 Panagiotis Kanavos
		{
176 4ec636f6 Panagiotis Kanavos
			base.OnActivate();
177 42800be8 Panagiotis Kanavos
178 255f5f86 Panagiotis Kanavos
			
179 34bdb91d Panagiotis Kanavos
180 4ec636f6 Panagiotis Kanavos
			StartMonitoring();                    
181 4ec636f6 Panagiotis Kanavos
		}
182 7b0a5fec Panagiotis Kanavos
183 42800be8 Panagiotis Kanavos
184 34bdb91d Panagiotis Kanavos
185 4f6d51d4 Panagiotis Kanavos
		private async void StartMonitoring()
186 4ec636f6 Panagiotis Kanavos
		{
187 4ec636f6 Panagiotis Kanavos
			try
188 4ec636f6 Panagiotis Kanavos
			{
189 4ec636f6 Panagiotis Kanavos
				var accounts = Settings.Accounts.Select(MonitorAccount);
190 4ec636f6 Panagiotis Kanavos
				await TaskEx.WhenAll(accounts);
191 4ec636f6 Panagiotis Kanavos
				_statusService = StatusService.Start();
192 692ec33b Panagiotis Kanavos
193 692ec33b Panagiotis Kanavos
/*
194 4ec636f6 Panagiotis Kanavos
				foreach (var account in Settings.Accounts)
195 4ec636f6 Panagiotis Kanavos
				{
196 4ec636f6 Panagiotis Kanavos
					await MonitorAccount(account);
197 4ec636f6 Panagiotis Kanavos
				}
198 692ec33b Panagiotis Kanavos
*/
199 4ec636f6 Panagiotis Kanavos
				
200 4ec636f6 Panagiotis Kanavos
			}
201 4ec636f6 Panagiotis Kanavos
			catch (AggregateException exc)
202 4ec636f6 Panagiotis Kanavos
			{
203 4ec636f6 Panagiotis Kanavos
				exc.Handle(e =>
204 4ec636f6 Panagiotis Kanavos
				{
205 4ec636f6 Panagiotis Kanavos
					Log.Error("Error while starting monitoring", e);
206 4ec636f6 Panagiotis Kanavos
					return true;
207 4ec636f6 Panagiotis Kanavos
				});
208 4ec636f6 Panagiotis Kanavos
				throw;
209 4ec636f6 Panagiotis Kanavos
			}
210 4ec636f6 Panagiotis Kanavos
		}
211 4ec636f6 Panagiotis Kanavos
212 4ec636f6 Panagiotis Kanavos
		protected override void OnDeactivate(bool close)
213 4ec636f6 Panagiotis Kanavos
		{
214 4ec636f6 Panagiotis Kanavos
			base.OnDeactivate(close);
215 4ec636f6 Panagiotis Kanavos
			if (close)
216 4ec636f6 Panagiotis Kanavos
			{
217 4ec636f6 Panagiotis Kanavos
				StatusService.Stop(_statusService);
218 4ec636f6 Panagiotis Kanavos
				_statusService = null;
219 4ec636f6 Panagiotis Kanavos
			}
220 4ec636f6 Panagiotis Kanavos
		}
221 4ec636f6 Panagiotis Kanavos
222 4ec636f6 Panagiotis Kanavos
		public Task MonitorAccount(AccountSettings account)
223 4ec636f6 Panagiotis Kanavos
		{
224 4ec636f6 Panagiotis Kanavos
			return Task.Factory.StartNew(() =>
225 4ec636f6 Panagiotis Kanavos
			{                                                
226 4f6d51d4 Panagiotis Kanavos
				PithosMonitor monitor;
227 4ec636f6 Panagiotis Kanavos
				var accountName = account.AccountName;
228 4ec636f6 Panagiotis Kanavos
229 4ec636f6 Panagiotis Kanavos
				if (_monitors.TryGetValue(accountName, out monitor))
230 4ec636f6 Panagiotis Kanavos
				{
231 4ec636f6 Panagiotis Kanavos
					//If the account is active
232 4ec636f6 Panagiotis Kanavos
					if (account.IsActive)
233 4ec636f6 Panagiotis Kanavos
						//Start the monitor. It's OK to start an already started monitor,
234 4ec636f6 Panagiotis Kanavos
						//it will just ignore the call                        
235 4ec636f6 Panagiotis Kanavos
						StartMonitor(monitor).Wait();                        
236 4ec636f6 Panagiotis Kanavos
					else
237 4ec636f6 Panagiotis Kanavos
					{
238 4ec636f6 Panagiotis Kanavos
						//If the account is inactive
239 4ec636f6 Panagiotis Kanavos
						//Stop and remove the monitor
240 4ec636f6 Panagiotis Kanavos
						RemoveMonitor(accountName);
241 4ec636f6 Panagiotis Kanavos
					}
242 4ec636f6 Panagiotis Kanavos
					return;
243 4ec636f6 Panagiotis Kanavos
				}
244 4ec636f6 Panagiotis Kanavos
245 255f5f86 Panagiotis Kanavos
				
246 4ec636f6 Panagiotis Kanavos
				//Create a new monitor/ Can't use MEF here, it would return a single instance for all monitors
247 4ec636f6 Panagiotis Kanavos
				monitor = new PithosMonitor
248 4ec636f6 Panagiotis Kanavos
							  {
249 4ec636f6 Panagiotis Kanavos
								  UserName = accountName,
250 4ec636f6 Panagiotis Kanavos
								  ApiKey = account.ApiKey,                                  
251 4ec636f6 Panagiotis Kanavos
								  StatusNotification = this,
252 4ec636f6 Panagiotis Kanavos
								  RootPath = account.RootPath
253 4ec636f6 Panagiotis Kanavos
							  };
254 4ec636f6 Panagiotis Kanavos
				//PithosMonitor uses MEF so we need to resolve it
255 4ec636f6 Panagiotis Kanavos
				IoC.BuildUp(monitor);
256 4ec636f6 Panagiotis Kanavos
257 255f5f86 Panagiotis Kanavos
				monitor.AuthenticationUrl = account.ServerUrl;
258 4ec636f6 Panagiotis Kanavos
259 4ec636f6 Panagiotis Kanavos
				_monitors[accountName] = monitor;
260 4ec636f6 Panagiotis Kanavos
261 4ec636f6 Panagiotis Kanavos
				if (account.IsActive)
262 4ec636f6 Panagiotis Kanavos
				{
263 4ec636f6 Panagiotis Kanavos
					//Don't start a monitor if it doesn't have an account and ApiKey
264 4ec636f6 Panagiotis Kanavos
					if (String.IsNullOrWhiteSpace(monitor.UserName) ||
265 4ec636f6 Panagiotis Kanavos
						String.IsNullOrWhiteSpace(monitor.ApiKey))
266 4ec636f6 Panagiotis Kanavos
						return;
267 4ec636f6 Panagiotis Kanavos
					StartMonitor(monitor);
268 4ec636f6 Panagiotis Kanavos
				}
269 4ec636f6 Panagiotis Kanavos
			});
270 4ec636f6 Panagiotis Kanavos
		}
271 4ec636f6 Panagiotis Kanavos
272 4ec636f6 Panagiotis Kanavos
273 4ec636f6 Panagiotis Kanavos
		protected override void OnViewLoaded(object view)
274 4ec636f6 Panagiotis Kanavos
		{
275 4ec636f6 Panagiotis Kanavos
			UpdateStatus();
276 4ec636f6 Panagiotis Kanavos
			var window = (Window)view;            
277 4ec636f6 Panagiotis Kanavos
			TaskEx.Delay(1000).ContinueWith(t => Execute.OnUIThread(window.Hide));
278 4ec636f6 Panagiotis Kanavos
			base.OnViewLoaded(view);
279 4ec636f6 Panagiotis Kanavos
		}
280 4ec636f6 Panagiotis Kanavos
281 4ec636f6 Panagiotis Kanavos
282 4ec636f6 Panagiotis Kanavos
		#region Status Properties
283 4ec636f6 Panagiotis Kanavos
284 4ec636f6 Panagiotis Kanavos
		private string _statusMessage;
285 4ec636f6 Panagiotis Kanavos
		public string StatusMessage
286 4ec636f6 Panagiotis Kanavos
		{
287 4ec636f6 Panagiotis Kanavos
			get { return _statusMessage; }
288 4ec636f6 Panagiotis Kanavos
			set
289 4ec636f6 Panagiotis Kanavos
			{
290 4ec636f6 Panagiotis Kanavos
				_statusMessage = value;
291 4ec636f6 Panagiotis Kanavos
				NotifyOfPropertyChange(() => StatusMessage);
292 4ec636f6 Panagiotis Kanavos
			}
293 4ec636f6 Panagiotis Kanavos
		}
294 4ec636f6 Panagiotis Kanavos
295 4ec636f6 Panagiotis Kanavos
		private readonly ObservableConcurrentCollection<AccountInfo> _accounts = new ObservableConcurrentCollection<AccountInfo>();
296 4ec636f6 Panagiotis Kanavos
		public ObservableConcurrentCollection<AccountInfo> Accounts
297 4ec636f6 Panagiotis Kanavos
		{
298 4ec636f6 Panagiotis Kanavos
			get { return _accounts; }
299 4ec636f6 Panagiotis Kanavos
		}
300 4ec636f6 Panagiotis Kanavos
301 4ec636f6 Panagiotis Kanavos
		public bool HasAccounts
302 4ec636f6 Panagiotis Kanavos
		{
303 4ec636f6 Panagiotis Kanavos
			get { return _accounts.Count > 0; }
304 4ec636f6 Panagiotis Kanavos
		}
305 4ec636f6 Panagiotis Kanavos
306 4ec636f6 Panagiotis Kanavos
307 4ec636f6 Panagiotis Kanavos
		public string OpenFolderCaption
308 4ec636f6 Panagiotis Kanavos
		{
309 4ec636f6 Panagiotis Kanavos
			get
310 4ec636f6 Panagiotis Kanavos
			{
311 4ec636f6 Panagiotis Kanavos
				return (_accounts.Count == 0)
312 4ec636f6 Panagiotis Kanavos
						? "No Accounts Defined"
313 4ec636f6 Panagiotis Kanavos
						: "Open Pithos Folder";
314 4ec636f6 Panagiotis Kanavos
			}
315 4ec636f6 Panagiotis Kanavos
		}
316 4ec636f6 Panagiotis Kanavos
317 4ec636f6 Panagiotis Kanavos
		private string _pauseSyncCaption="Pause Synching";
318 4ec636f6 Panagiotis Kanavos
		public string PauseSyncCaption
319 4ec636f6 Panagiotis Kanavos
		{
320 4ec636f6 Panagiotis Kanavos
			get { return _pauseSyncCaption; }
321 4ec636f6 Panagiotis Kanavos
			set
322 4ec636f6 Panagiotis Kanavos
			{
323 4ec636f6 Panagiotis Kanavos
				_pauseSyncCaption = value;
324 4ec636f6 Panagiotis Kanavos
				NotifyOfPropertyChange(() => PauseSyncCaption);
325 4ec636f6 Panagiotis Kanavos
			}
326 4ec636f6 Panagiotis Kanavos
		}
327 4ec636f6 Panagiotis Kanavos
328 4ec636f6 Panagiotis Kanavos
		private readonly ObservableConcurrentCollection<FileEntry> _recentFiles = new ObservableConcurrentCollection<FileEntry>();
329 4ec636f6 Panagiotis Kanavos
		public ObservableConcurrentCollection<FileEntry> RecentFiles
330 4ec636f6 Panagiotis Kanavos
		{
331 4ec636f6 Panagiotis Kanavos
			get { return _recentFiles; }
332 4ec636f6 Panagiotis Kanavos
		}
333 4ec636f6 Panagiotis Kanavos
334 4ec636f6 Panagiotis Kanavos
335 4ec636f6 Panagiotis Kanavos
		private string _statusIcon="../Images/Pithos.ico";
336 4ec636f6 Panagiotis Kanavos
		public string StatusIcon
337 4ec636f6 Panagiotis Kanavos
		{
338 4ec636f6 Panagiotis Kanavos
			get { return _statusIcon; }
339 4ec636f6 Panagiotis Kanavos
			set
340 4ec636f6 Panagiotis Kanavos
			{
341 255f5f86 Panagiotis Kanavos
				//TODO: Ensure all status icons use the Pithos logo
342 4f6d51d4 Panagiotis Kanavos
				_statusIcon = value;
343 4ec636f6 Panagiotis Kanavos
				NotifyOfPropertyChange(() => StatusIcon);
344 4ec636f6 Panagiotis Kanavos
			}
345 4ec636f6 Panagiotis Kanavos
		}
346 4ec636f6 Panagiotis Kanavos
347 4ec636f6 Panagiotis Kanavos
		#endregion
348 4ec636f6 Panagiotis Kanavos
349 4ec636f6 Panagiotis Kanavos
		#region Commands
350 4ec636f6 Panagiotis Kanavos
351 f2d88248 Panagiotis Kanavos
        public void ShowPreferences()
352 f2d88248 Panagiotis Kanavos
        {
353 f2d88248 Panagiotis Kanavos
            ShowPreferences(null);
354 f2d88248 Panagiotis Kanavos
        }
355 f2d88248 Panagiotis Kanavos
356 f2d88248 Panagiotis Kanavos
		public void ShowPreferences(string currentTab)
357 4ec636f6 Panagiotis Kanavos
		{
358 1e26eceb Panagiotis Kanavos
			//Settings.Reload();
359 f2d88248 Panagiotis Kanavos
		    var preferences = new PreferencesViewModel(_windowManager, _events, this, Settings,currentTab);
360 f2d88248 Panagiotis Kanavos
		    _windowManager.ShowDialog(preferences);
361 4ec636f6 Panagiotis Kanavos
			
362 4ec636f6 Panagiotis Kanavos
		}
363 4ec636f6 Panagiotis Kanavos
364 4ec636f6 Panagiotis Kanavos
		public void AboutPithos()
365 4ec636f6 Panagiotis Kanavos
		{
366 4ec636f6 Panagiotis Kanavos
			var about = new AboutViewModel();
367 4ec636f6 Panagiotis Kanavos
			_windowManager.ShowWindow(about);
368 4ec636f6 Panagiotis Kanavos
		}
369 4ec636f6 Panagiotis Kanavos
370 4ec636f6 Panagiotis Kanavos
		public void SendFeedback()
371 4ec636f6 Panagiotis Kanavos
		{
372 4ec636f6 Panagiotis Kanavos
			var feedBack =  IoC.Get<FeedbackViewModel>();
373 4ec636f6 Panagiotis Kanavos
			_windowManager.ShowWindow(feedBack);
374 4ec636f6 Panagiotis Kanavos
		}
375 4ec636f6 Panagiotis Kanavos
376 4ec636f6 Panagiotis Kanavos
		//public PithosCommand OpenPithosFolderCommand { get; private set; }
377 4ec636f6 Panagiotis Kanavos
378 4ec636f6 Panagiotis Kanavos
		public void OpenPithosFolder()
379 4ec636f6 Panagiotis Kanavos
		{
380 4ec636f6 Panagiotis Kanavos
			var account = Settings.Accounts.FirstOrDefault(acc => acc.IsActive);
381 4ec636f6 Panagiotis Kanavos
			if (account == null)
382 4ec636f6 Panagiotis Kanavos
				return;
383 4ec636f6 Panagiotis Kanavos
			Process.Start(account.RootPath);
384 4ec636f6 Panagiotis Kanavos
		}
385 4ec636f6 Panagiotis Kanavos
386 4ec636f6 Panagiotis Kanavos
		public void OpenPithosFolder(AccountInfo account)
387 4ec636f6 Panagiotis Kanavos
		{
388 4ec636f6 Panagiotis Kanavos
			Process.Start(account.AccountPath);
389 4ec636f6 Panagiotis Kanavos
		}
390 4ec636f6 Panagiotis Kanavos
391 4ec636f6 Panagiotis Kanavos
		
392 f734ab5b Panagiotis Kanavos
/*
393 4ec636f6 Panagiotis Kanavos
		public void GoToSite()
394 4ec636f6 Panagiotis Kanavos
		{            
395 4ec636f6 Panagiotis Kanavos
			var site = Properties.Settings.Default.PithosSite;
396 4ec636f6 Panagiotis Kanavos
			Process.Start(site);            
397 4ec636f6 Panagiotis Kanavos
		}
398 f734ab5b Panagiotis Kanavos
*/
399 6aa29f4f Panagiotis Kanavos
400 4ec636f6 Panagiotis Kanavos
		public void GoToSite(AccountInfo account)
401 4ec636f6 Panagiotis Kanavos
		{
402 4ec636f6 Panagiotis Kanavos
			/*var site = String.Format("{0}/ui/?token={1}&user={2}",
403 4ec636f6 Panagiotis Kanavos
				account.SiteUri,account.Token,
404 4ec636f6 Panagiotis Kanavos
				account.UserName);*/
405 4ec636f6 Panagiotis Kanavos
			Process.Start(account.SiteUri);
406 4ec636f6 Panagiotis Kanavos
		}
407 4ec636f6 Panagiotis Kanavos
408 4ec636f6 Panagiotis Kanavos
		public void ShowFileProperties()
409 4ec636f6 Panagiotis Kanavos
		{
410 4ec636f6 Panagiotis Kanavos
			var account = Settings.Accounts.First(acc => acc.IsActive);            
411 4ec636f6 Panagiotis Kanavos
			var dir = new DirectoryInfo(account.RootPath + @"\pithos");
412 4ec636f6 Panagiotis Kanavos
			var files=dir.GetFiles();
413 4ec636f6 Panagiotis Kanavos
			var r=new Random();
414 4ec636f6 Panagiotis Kanavos
			var idx=r.Next(0, files.Length);
415 4ec636f6 Panagiotis Kanavos
			ShowFileProperties(files[idx].FullName);            
416 4ec636f6 Panagiotis Kanavos
		}
417 4ec636f6 Panagiotis Kanavos
418 4ec636f6 Panagiotis Kanavos
		public void ShowFileProperties(string filePath)
419 4ec636f6 Panagiotis Kanavos
		{
420 4ec636f6 Panagiotis Kanavos
			if (String.IsNullOrWhiteSpace(filePath))
421 4ec636f6 Panagiotis Kanavos
				throw new ArgumentNullException("filePath");
422 4f6d51d4 Panagiotis Kanavos
			if (!File.Exists(filePath) && !Directory.Exists(filePath))
423 4ec636f6 Panagiotis Kanavos
				throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
424 4ec636f6 Panagiotis Kanavos
			Contract.EndContractBlock();
425 4ec636f6 Panagiotis Kanavos
426 4ec636f6 Panagiotis Kanavos
			var pair=(from monitor in  Monitors
427 4ec636f6 Panagiotis Kanavos
							   where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
428 4ec636f6 Panagiotis Kanavos
								   select monitor).FirstOrDefault();
429 255f5f86 Panagiotis Kanavos
			var accountMonitor = pair.Value;
430 4ec636f6 Panagiotis Kanavos
431 4ec636f6 Panagiotis Kanavos
			if (accountMonitor == null)
432 4ec636f6 Panagiotis Kanavos
				return;
433 4ec636f6 Panagiotis Kanavos
434 4ec636f6 Panagiotis Kanavos
			var infoTask=Task.Factory.StartNew(()=>accountMonitor.GetObjectInfo(filePath));
435 4ec636f6 Panagiotis Kanavos
436 4ec636f6 Panagiotis Kanavos
			
437 4ec636f6 Panagiotis Kanavos
438 4ec636f6 Panagiotis Kanavos
			var fileProperties = new FilePropertiesViewModel(this, infoTask,filePath);
439 4ec636f6 Panagiotis Kanavos
			_windowManager.ShowWindow(fileProperties);
440 4ec636f6 Panagiotis Kanavos
		} 
441 4ec636f6 Panagiotis Kanavos
		
442 4ec636f6 Panagiotis Kanavos
		public void ShowContainerProperties()
443 4ec636f6 Panagiotis Kanavos
		{
444 4ec636f6 Panagiotis Kanavos
			var account = Settings.Accounts.First(acc => acc.IsActive);            
445 4ec636f6 Panagiotis Kanavos
			var dir = new DirectoryInfo(account.RootPath);
446 4ec636f6 Panagiotis Kanavos
			var fullName = (from folder in dir.EnumerateDirectories()
447 4ec636f6 Panagiotis Kanavos
							where (folder.Attributes & FileAttributes.Hidden) == 0
448 4ec636f6 Panagiotis Kanavos
							select folder.FullName).First();
449 4ec636f6 Panagiotis Kanavos
			ShowContainerProperties(fullName);            
450 4ec636f6 Panagiotis Kanavos
		}
451 4ec636f6 Panagiotis Kanavos
452 4ec636f6 Panagiotis Kanavos
		public void ShowContainerProperties(string filePath)
453 4ec636f6 Panagiotis Kanavos
		{
454 4ec636f6 Panagiotis Kanavos
			if (String.IsNullOrWhiteSpace(filePath))
455 4ec636f6 Panagiotis Kanavos
				throw new ArgumentNullException("filePath");
456 4ec636f6 Panagiotis Kanavos
			if (!Directory.Exists(filePath))
457 4ec636f6 Panagiotis Kanavos
				throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
458 4ec636f6 Panagiotis Kanavos
			Contract.EndContractBlock();
459 4ec636f6 Panagiotis Kanavos
460 4ec636f6 Panagiotis Kanavos
			var pair=(from monitor in  Monitors
461 4ec636f6 Panagiotis Kanavos
							   where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
462 4ec636f6 Panagiotis Kanavos
								   select monitor).FirstOrDefault();
463 255f5f86 Panagiotis Kanavos
			var accountMonitor = pair.Value;            
464 4ec636f6 Panagiotis Kanavos
			var info = accountMonitor.GetContainerInfo(filePath);
465 4ec636f6 Panagiotis Kanavos
466 4ec636f6 Panagiotis Kanavos
			
467 4ec636f6 Panagiotis Kanavos
468 4ec636f6 Panagiotis Kanavos
			var containerProperties = new ContainerPropertiesViewModel(this, info,filePath);
469 4ec636f6 Panagiotis Kanavos
			_windowManager.ShowWindow(containerProperties);
470 4ec636f6 Panagiotis Kanavos
		}
471 4ec636f6 Panagiotis Kanavos
472 255f5f86 Panagiotis Kanavos
		public void SynchNow()
473 255f5f86 Panagiotis Kanavos
		{
474 aa7ac00e Panagiotis Kanavos
			var agent = IoC.Get<PollAgent>();
475 255f5f86 Panagiotis Kanavos
			agent.SynchNow();
476 255f5f86 Panagiotis Kanavos
		}
477 133f83c2 Panagiotis Kanavos
478 4ec636f6 Panagiotis Kanavos
		public ObjectInfo RefreshObjectInfo(ObjectInfo currentInfo)
479 4ec636f6 Panagiotis Kanavos
		{
480 4ec636f6 Panagiotis Kanavos
			if (currentInfo==null)
481 4ec636f6 Panagiotis Kanavos
				throw new ArgumentNullException("currentInfo");
482 4ec636f6 Panagiotis Kanavos
			Contract.EndContractBlock();
483 4ec636f6 Panagiotis Kanavos
484 4ec636f6 Panagiotis Kanavos
			var monitor = Monitors[currentInfo.Account];
485 4ec636f6 Panagiotis Kanavos
			var newInfo=monitor.CloudClient.GetObjectInfo(currentInfo.Account, currentInfo.Container, currentInfo.Name);
486 4ec636f6 Panagiotis Kanavos
			return newInfo;
487 4ec636f6 Panagiotis Kanavos
		}
488 4ec636f6 Panagiotis Kanavos
489 4ec636f6 Panagiotis Kanavos
		public ContainerInfo RefreshContainerInfo(ContainerInfo container)
490 4ec636f6 Panagiotis Kanavos
		{
491 4ec636f6 Panagiotis Kanavos
			if (container == null)
492 4ec636f6 Panagiotis Kanavos
				throw new ArgumentNullException("container");
493 4ec636f6 Panagiotis Kanavos
			Contract.EndContractBlock();
494 4ec636f6 Panagiotis Kanavos
495 4ec636f6 Panagiotis Kanavos
			var monitor = Monitors[container.Account];
496 4ec636f6 Panagiotis Kanavos
			var newInfo = monitor.CloudClient.GetContainerInfo(container.Account, container.Name);
497 4ec636f6 Panagiotis Kanavos
			return newInfo;
498 4ec636f6 Panagiotis Kanavos
		}
499 4ec636f6 Panagiotis Kanavos
500 4ec636f6 Panagiotis Kanavos
501 4ec636f6 Panagiotis Kanavos
		public void ToggleSynching()
502 4ec636f6 Panagiotis Kanavos
		{
503 4ec636f6 Panagiotis Kanavos
			bool isPaused=false;
504 4ec636f6 Panagiotis Kanavos
			foreach (var pair in Monitors)
505 4ec636f6 Panagiotis Kanavos
			{
506 4ec636f6 Panagiotis Kanavos
				var monitor = pair.Value;
507 4ec636f6 Panagiotis Kanavos
				monitor.Pause = !monitor.Pause;
508 4ec636f6 Panagiotis Kanavos
				isPaused = monitor.Pause;
509 4ec636f6 Panagiotis Kanavos
			}
510 4ec636f6 Panagiotis Kanavos
511 4ec636f6 Panagiotis Kanavos
			PauseSyncCaption = isPaused ? "Resume syncing" : "Pause syncing";
512 4ec636f6 Panagiotis Kanavos
			var iconKey = isPaused? "TraySyncPaused" : "TrayInSynch";
513 4ec636f6 Panagiotis Kanavos
			StatusIcon = String.Format(@"../Images/{0}.ico", iconKey);
514 4ec636f6 Panagiotis Kanavos
		}
515 4ec636f6 Panagiotis Kanavos
516 4ec636f6 Panagiotis Kanavos
		public void ExitPithos()
517 4ec636f6 Panagiotis Kanavos
		{
518 4ec636f6 Panagiotis Kanavos
			foreach (var pair in Monitors)
519 4ec636f6 Panagiotis Kanavos
			{
520 4ec636f6 Panagiotis Kanavos
				var monitor = pair.Value;
521 4ec636f6 Panagiotis Kanavos
				monitor.Stop();
522 4ec636f6 Panagiotis Kanavos
			}
523 4ec636f6 Panagiotis Kanavos
524 4ec636f6 Panagiotis Kanavos
			((Window)GetView()).Close();
525 4ec636f6 Panagiotis Kanavos
		}
526 4ec636f6 Panagiotis Kanavos
		#endregion
527 4ec636f6 Panagiotis Kanavos
528 4ec636f6 Panagiotis Kanavos
529 4f6d51d4 Panagiotis Kanavos
		private readonly Dictionary<PithosStatus, StatusInfo> _iconNames = new List<StatusInfo>
530 4ec636f6 Panagiotis Kanavos
			{
531 4ec636f6 Panagiotis Kanavos
				new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"),
532 4ec636f6 Panagiotis Kanavos
				new StatusInfo(PithosStatus.Syncing, "Syncing Files", "TraySynching"),
533 4ec636f6 Panagiotis Kanavos
				new StatusInfo(PithosStatus.SyncPaused, "Sync Paused", "TraySyncPaused")
534 4ec636f6 Panagiotis Kanavos
			}.ToDictionary(s => s.Status);
535 4ec636f6 Panagiotis Kanavos
536 4ec636f6 Panagiotis Kanavos
		readonly IWindowManager _windowManager;
537 255f5f86 Panagiotis Kanavos
		
538 c53aa229 Panagiotis Kanavos
539 cf761c0d Panagiotis Kanavos
		///<summary>
540 cf761c0d Panagiotis Kanavos
		/// Updates the visual status indicators of the application depending on status changes, e.g. icon, stat		
541 cf761c0d Panagiotis Kanavos
		///</summary>
542 4ec636f6 Panagiotis Kanavos
		public void UpdateStatus()
543 4ec636f6 Panagiotis Kanavos
		{
544 4ec636f6 Panagiotis Kanavos
			var pithosStatus = _statusChecker.GetPithosStatus();
545 4ec636f6 Panagiotis Kanavos
546 4f6d51d4 Panagiotis Kanavos
			if (_iconNames.ContainsKey(pithosStatus))
547 4ec636f6 Panagiotis Kanavos
			{
548 4f6d51d4 Panagiotis Kanavos
				var info = _iconNames[pithosStatus];
549 4ec636f6 Panagiotis Kanavos
				StatusIcon = String.Format(@"../Images/{0}.ico", info.IconName);
550 4ec636f6 Panagiotis Kanavos
551 4ec636f6 Panagiotis Kanavos
552 4ec636f6 Panagiotis Kanavos
553 422c9598 Panagiotis Kanavos
				StatusMessage = String.Format("Pithos {0}\r\n{1}", _fileVersion.Value.FileVersion,info.StatusText);
554 4ec636f6 Panagiotis Kanavos
			}
555 4ec636f6 Panagiotis Kanavos
			
556 852d2cf2 Panagiotis Kanavos
			//_events.Publish(new Notification { Title = "Start", Message = "Start Monitoring", Level = TraceLevel.Info});
557 4ec636f6 Panagiotis Kanavos
		}
558 4ec636f6 Panagiotis Kanavos
559 4ec636f6 Panagiotis Kanavos
560 4ec636f6 Panagiotis Kanavos
	   
561 4ec636f6 Panagiotis Kanavos
		private Task StartMonitor(PithosMonitor monitor,int retries=0)
562 4ec636f6 Panagiotis Kanavos
		{
563 4ec636f6 Panagiotis Kanavos
			return Task.Factory.StartNew(() =>
564 4ec636f6 Panagiotis Kanavos
			{
565 4ec636f6 Panagiotis Kanavos
				using (log4net.ThreadContext.Stacks["Monitor"].Push("Start"))
566 4ec636f6 Panagiotis Kanavos
				{
567 4ec636f6 Panagiotis Kanavos
					try
568 4ec636f6 Panagiotis Kanavos
					{
569 4ec636f6 Panagiotis Kanavos
						Log.InfoFormat("Start Monitoring {0}", monitor.UserName);
570 4ec636f6 Panagiotis Kanavos
571 4ec636f6 Panagiotis Kanavos
						monitor.Start();
572 4ec636f6 Panagiotis Kanavos
					}
573 4ec636f6 Panagiotis Kanavos
					catch (WebException exc)
574 4ec636f6 Panagiotis Kanavos
					{
575 4ec636f6 Panagiotis Kanavos
						if (AbandonRetry(monitor, retries))
576 4ec636f6 Panagiotis Kanavos
							return;
577 4ec636f6 Panagiotis Kanavos
578 255f5f86 Panagiotis Kanavos
						HttpStatusCode statusCode =HttpStatusCode.OK;
579 255f5f86 Panagiotis Kanavos
						var response = exc.Response as HttpWebResponse;
580 255f5f86 Panagiotis Kanavos
						if(response!=null)
581 255f5f86 Panagiotis Kanavos
							statusCode = response.StatusCode;
582 255f5f86 Panagiotis Kanavos
583 255f5f86 Panagiotis Kanavos
						switch (statusCode)
584 255f5f86 Panagiotis Kanavos
						{
585 255f5f86 Panagiotis Kanavos
							case HttpStatusCode.Unauthorized:
586 255f5f86 Panagiotis Kanavos
								var message = String.Format("API Key Expired for {0}. Starting Renewal",
587 255f5f86 Panagiotis Kanavos
															monitor.UserName);
588 255f5f86 Panagiotis Kanavos
								Log.Error(message, exc);
589 1e26eceb Panagiotis Kanavos
						        var account = Settings.Accounts.Find(acc => acc.AccountName == monitor.UserName);                                
590 1e26eceb Panagiotis Kanavos
						        account.IsExpired = true;
591 1e26eceb Panagiotis Kanavos
                                Notify(new ExpirationNotification(account));
592 1e26eceb Panagiotis Kanavos
								//TryAuthorize(monitor.UserName, retries).Wait();
593 255f5f86 Panagiotis Kanavos
								break;
594 255f5f86 Panagiotis Kanavos
							case HttpStatusCode.ProxyAuthenticationRequired:
595 255f5f86 Panagiotis Kanavos
								TryAuthenticateProxy(monitor,retries);
596 255f5f86 Panagiotis Kanavos
								break;
597 255f5f86 Panagiotis Kanavos
							default:
598 255f5f86 Panagiotis Kanavos
								TryLater(monitor, exc, retries);
599 255f5f86 Panagiotis Kanavos
								break;
600 255f5f86 Panagiotis Kanavos
						}
601 4ec636f6 Panagiotis Kanavos
					}
602 4ec636f6 Panagiotis Kanavos
					catch (Exception exc)
603 4ec636f6 Panagiotis Kanavos
					{
604 4ec636f6 Panagiotis Kanavos
						if (AbandonRetry(monitor, retries)) 
605 4ec636f6 Panagiotis Kanavos
							return;
606 4ec636f6 Panagiotis Kanavos
607 4ec636f6 Panagiotis Kanavos
						TryLater(monitor,exc,retries);
608 4ec636f6 Panagiotis Kanavos
					}
609 4ec636f6 Panagiotis Kanavos
				}
610 4ec636f6 Panagiotis Kanavos
			});
611 4ec636f6 Panagiotis Kanavos
		}
612 4ec636f6 Panagiotis Kanavos
613 255f5f86 Panagiotis Kanavos
		private void TryAuthenticateProxy(PithosMonitor monitor,int retries)
614 255f5f86 Panagiotis Kanavos
		{
615 255f5f86 Panagiotis Kanavos
			Execute.OnUIThread(() =>
616 255f5f86 Panagiotis Kanavos
								   {                                       
617 255f5f86 Panagiotis Kanavos
									   var proxyAccount = IoC.Get<ProxyAccountViewModel>();
618 255f5f86 Panagiotis Kanavos
										proxyAccount.Settings = this.Settings;
619 255f5f86 Panagiotis Kanavos
									   if (true != _windowManager.ShowDialog(proxyAccount)) 
620 255f5f86 Panagiotis Kanavos
										   return;
621 255f5f86 Panagiotis Kanavos
									   StartMonitor(monitor, retries);
622 255f5f86 Panagiotis Kanavos
									   NotifyOfPropertyChange(() => Accounts);
623 255f5f86 Panagiotis Kanavos
								   });
624 255f5f86 Panagiotis Kanavos
		}
625 65282d58 Panagiotis Kanavos
626 255f5f86 Panagiotis Kanavos
		private bool AbandonRetry(PithosMonitor monitor, int retries)
627 4ec636f6 Panagiotis Kanavos
		{
628 4ec636f6 Panagiotis Kanavos
			if (retries > 1)
629 4ec636f6 Panagiotis Kanavos
			{
630 4ec636f6 Panagiotis Kanavos
				var message = String.Format("Monitoring of account {0} has failed too many times. Will not retry",
631 4ec636f6 Panagiotis Kanavos
											monitor.UserName);
632 4ec636f6 Panagiotis Kanavos
				_events.Publish(new Notification
633 4ec636f6 Panagiotis Kanavos
									{Title = "Account monitoring failed", Message = message, Level = TraceLevel.Error});
634 4ec636f6 Panagiotis Kanavos
				return true;
635 4ec636f6 Panagiotis Kanavos
			}
636 4ec636f6 Panagiotis Kanavos
			return false;
637 4ec636f6 Panagiotis Kanavos
		}
638 4ec636f6 Panagiotis Kanavos
639 4ec636f6 Panagiotis Kanavos
640 2e3aee00 Panagiotis Kanavos
		public async Task TryAuthorize(string userName, int retries)
641 4ec636f6 Panagiotis Kanavos
		{
642 4ec636f6 Panagiotis Kanavos
			_events.Publish(new Notification { Title = "Authorization failed", Message = "Your API Key has probably expired. You will be directed to a page where you can renew it", Level = TraceLevel.Error });
643 4ec636f6 Panagiotis Kanavos
644 4ec636f6 Panagiotis Kanavos
			try
645 4ec636f6 Panagiotis Kanavos
			{
646 4ec636f6 Panagiotis Kanavos
647 4ec636f6 Panagiotis Kanavos
				var credentials = await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl);
648 4ec636f6 Panagiotis Kanavos
649 4f6d51d4 Panagiotis Kanavos
				var account = Settings.Accounts.First(act => act.AccountName == credentials.UserName);
650 62d5b25f Panagiotis Kanavos
                //The server may return credentials for a different account
651 62d5b25f Panagiotis Kanavos
			    var monitor = _monitors[account.AccountName];
652 4ec636f6 Panagiotis Kanavos
				account.ApiKey = credentials.Password;
653 62d5b25f Panagiotis Kanavos
                monitor.ApiKey = credentials.Password;
654 1e26eceb Panagiotis Kanavos
			    account.IsExpired = false;
655 4ec636f6 Panagiotis Kanavos
				Settings.Save();
656 1e26eceb Panagiotis Kanavos
				TaskEx.Delay(10000).ContinueWith(_=>
657 1e26eceb Panagiotis Kanavos
                            StartMonitor(monitor, retries + 1));
658 4ec636f6 Panagiotis Kanavos
				NotifyOfPropertyChange(()=>Accounts);
659 4ec636f6 Panagiotis Kanavos
			}
660 4ec636f6 Panagiotis Kanavos
			catch (AggregateException exc)
661 4ec636f6 Panagiotis Kanavos
			{
662 62d5b25f Panagiotis Kanavos
				string message = String.Format("API Key retrieval for {0} failed", userName);
663 4ec636f6 Panagiotis Kanavos
				Log.Error(message, exc.InnerException);
664 4ec636f6 Panagiotis Kanavos
				_events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
665 4ec636f6 Panagiotis Kanavos
			}
666 4ec636f6 Panagiotis Kanavos
			catch (Exception exc)
667 4ec636f6 Panagiotis Kanavos
			{
668 62d5b25f Panagiotis Kanavos
				string message = String.Format("API Key retrieval for {0} failed", userName);
669 4ec636f6 Panagiotis Kanavos
				Log.Error(message, exc);
670 4ec636f6 Panagiotis Kanavos
				_events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
671 4ec636f6 Panagiotis Kanavos
			}
672 4ec636f6 Panagiotis Kanavos
673 4ec636f6 Panagiotis Kanavos
		}
674 4ec636f6 Panagiotis Kanavos
675 4ec636f6 Panagiotis Kanavos
		private static bool IsUnauthorized(WebException exc)
676 4ec636f6 Panagiotis Kanavos
		{
677 4ec636f6 Panagiotis Kanavos
			if (exc==null)
678 4ec636f6 Panagiotis Kanavos
				throw new ArgumentNullException("exc");
679 4ec636f6 Panagiotis Kanavos
			Contract.EndContractBlock();
680 4ec636f6 Panagiotis Kanavos
681 4ec636f6 Panagiotis Kanavos
			var response = exc.Response as HttpWebResponse;
682 4ec636f6 Panagiotis Kanavos
			if (response == null)
683 4ec636f6 Panagiotis Kanavos
				return false;
684 4ec636f6 Panagiotis Kanavos
			return (response.StatusCode == HttpStatusCode.Unauthorized);
685 4ec636f6 Panagiotis Kanavos
		}
686 4ec636f6 Panagiotis Kanavos
687 4ec636f6 Panagiotis Kanavos
		private void TryLater(PithosMonitor monitor, Exception exc,int retries)
688 4ec636f6 Panagiotis Kanavos
		{
689 4ec636f6 Panagiotis Kanavos
			var message = String.Format("An exception occured. Can't start monitoring\nWill retry in 10 seconds");
690 4ec636f6 Panagiotis Kanavos
			Task.Factory.StartNewDelayed(10000, () => StartMonitor(monitor,retries+1));
691 4ec636f6 Panagiotis Kanavos
			_events.Publish(new Notification
692 4ec636f6 Panagiotis Kanavos
								{Title = "Error", Message = message, Level = TraceLevel.Error});
693 4ec636f6 Panagiotis Kanavos
			Log.Error(message, exc);
694 4ec636f6 Panagiotis Kanavos
		}
695 4ec636f6 Panagiotis Kanavos
696 4ec636f6 Panagiotis Kanavos
697 4ec636f6 Panagiotis Kanavos
		public void NotifyChange(string status, TraceLevel level=TraceLevel.Info)
698 4ec636f6 Panagiotis Kanavos
		{
699 4f6d51d4 Panagiotis Kanavos
			StatusMessage = status;
700 4ec636f6 Panagiotis Kanavos
			
701 4ec636f6 Panagiotis Kanavos
			_events.Publish(new Notification { Title = "Pithos", Message = status, Level = level });
702 4ec636f6 Panagiotis Kanavos
		}
703 4ec636f6 Panagiotis Kanavos
704 4ec636f6 Panagiotis Kanavos
		public void NotifyChangedFile(string filePath)
705 4ec636f6 Panagiotis Kanavos
		{
706 4ec636f6 Panagiotis Kanavos
			var entry = new FileEntry {FullPath=filePath};
707 4f6d51d4 Panagiotis Kanavos
			IProducerConsumerCollection<FileEntry> files=RecentFiles;
708 4ec636f6 Panagiotis Kanavos
			FileEntry popped;
709 4ec636f6 Panagiotis Kanavos
			while (files.Count > 5)
710 4ec636f6 Panagiotis Kanavos
				files.TryTake(out popped);
711 4ec636f6 Panagiotis Kanavos
			files.TryAdd(entry);
712 4ec636f6 Panagiotis Kanavos
		}
713 4ec636f6 Panagiotis Kanavos
714 4ec636f6 Panagiotis Kanavos
		public void NotifyAccount(AccountInfo account)
715 4ec636f6 Panagiotis Kanavos
		{
716 4ec636f6 Panagiotis Kanavos
			if (account== null)
717 4ec636f6 Panagiotis Kanavos
				return;
718 4ec636f6 Panagiotis Kanavos
			//TODO: What happens to an existing account whose Token has changed?
719 4ec636f6 Panagiotis Kanavos
			account.SiteUri= String.Format("{0}/ui/?token={1}&user={2}",
720 eae84ae8 Panagiotis Kanavos
				account.SiteUri, Uri.EscapeDataString(account.Token),
721 255f5f86 Panagiotis Kanavos
				Uri.EscapeDataString(account.UserName));
722 4ec636f6 Panagiotis Kanavos
723 4ec636f6 Panagiotis Kanavos
			if (Accounts.All(item => item.UserName != account.UserName))
724 4ec636f6 Panagiotis Kanavos
				Accounts.TryAdd(account);
725 4ec636f6 Panagiotis Kanavos
726 4ec636f6 Panagiotis Kanavos
		}
727 4ec636f6 Panagiotis Kanavos
728 255f5f86 Panagiotis Kanavos
		public void NotifyConflicts(IEnumerable<FileSystemInfo> conflictFiles, string message)
729 255f5f86 Panagiotis Kanavos
		{
730 255f5f86 Panagiotis Kanavos
			if (conflictFiles == null)
731 255f5f86 Panagiotis Kanavos
				return;
732 255f5f86 Panagiotis Kanavos
			if (!conflictFiles.Any())
733 255f5f86 Panagiotis Kanavos
				return;
734 e5b65606 Panagiotis Kanavos
735 255f5f86 Panagiotis Kanavos
			UpdateStatus();
736 255f5f86 Panagiotis Kanavos
			//TODO: Create a more specific message. For now, just show a warning
737 255f5f86 Panagiotis Kanavos
			NotifyForFiles(conflictFiles,message,TraceLevel.Warning);
738 9c6d3193 Panagiotis Kanavos
739 255f5f86 Panagiotis Kanavos
		}
740 9c6d3193 Panagiotis Kanavos
741 255f5f86 Panagiotis Kanavos
		public void NotifyForFiles(IEnumerable<FileSystemInfo> files, string message,TraceLevel level=TraceLevel.Info)
742 255f5f86 Panagiotis Kanavos
		{
743 255f5f86 Panagiotis Kanavos
			if (files == null)
744 255f5f86 Panagiotis Kanavos
				return;
745 255f5f86 Panagiotis Kanavos
			if (!files.Any())
746 255f5f86 Panagiotis Kanavos
				return;
747 9c6d3193 Panagiotis Kanavos
748 255f5f86 Panagiotis Kanavos
			StatusMessage = message;
749 9c6d3193 Panagiotis Kanavos
750 255f5f86 Panagiotis Kanavos
			_events.Publish(new Notification { Title = "Pithos", Message = message, Level = level});
751 255f5f86 Panagiotis Kanavos
		}
752 9c6d3193 Panagiotis Kanavos
753 255f5f86 Panagiotis Kanavos
		public void Notify(Notification notification)
754 255f5f86 Panagiotis Kanavos
		{
755 255f5f86 Panagiotis Kanavos
			_events.Publish(notification);
756 255f5f86 Panagiotis Kanavos
		}
757 e5b65606 Panagiotis Kanavos
758 4ec636f6 Panagiotis Kanavos
759 255f5f86 Panagiotis Kanavos
		public void RemoveMonitor(string accountName)
760 4ec636f6 Panagiotis Kanavos
		{
761 4ec636f6 Panagiotis Kanavos
			if (String.IsNullOrWhiteSpace(accountName))
762 4ec636f6 Panagiotis Kanavos
				return;
763 4ec636f6 Panagiotis Kanavos
764 4ec636f6 Panagiotis Kanavos
			var accountInfo=_accounts.FirstOrDefault(account => account.UserName == accountName);
765 4ec636f6 Panagiotis Kanavos
			_accounts.TryRemove(accountInfo);
766 4ec636f6 Panagiotis Kanavos
767 4ec636f6 Panagiotis Kanavos
			PithosMonitor monitor;
768 4ec636f6 Panagiotis Kanavos
			if (Monitors.TryRemove(accountName, out monitor))
769 4ec636f6 Panagiotis Kanavos
			{
770 4ec636f6 Panagiotis Kanavos
				monitor.Stop();
771 4ec636f6 Panagiotis Kanavos
			}
772 4ec636f6 Panagiotis Kanavos
		}
773 4ec636f6 Panagiotis Kanavos
774 4ec636f6 Panagiotis Kanavos
		public void RefreshOverlays()
775 4ec636f6 Panagiotis Kanavos
		{
776 4ec636f6 Panagiotis Kanavos
			foreach (var pair in Monitors)
777 4ec636f6 Panagiotis Kanavos
			{
778 4ec636f6 Panagiotis Kanavos
				var monitor = pair.Value;
779 4ec636f6 Panagiotis Kanavos
780 4ec636f6 Panagiotis Kanavos
				var path = monitor.RootPath;
781 4ec636f6 Panagiotis Kanavos
782 4ec636f6 Panagiotis Kanavos
				if (String.IsNullOrWhiteSpace(path))
783 4ec636f6 Panagiotis Kanavos
					continue;
784 4ec636f6 Panagiotis Kanavos
785 4ec636f6 Panagiotis Kanavos
				if (!Directory.Exists(path) && !File.Exists(path))
786 4ec636f6 Panagiotis Kanavos
					continue;
787 4ec636f6 Panagiotis Kanavos
788 4ec636f6 Panagiotis Kanavos
				IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);
789 4ec636f6 Panagiotis Kanavos
790 4ec636f6 Panagiotis Kanavos
				try
791 4ec636f6 Panagiotis Kanavos
				{
792 4ec636f6 Panagiotis Kanavos
					NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,
793 4ec636f6 Panagiotis Kanavos
												 HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,
794 4ec636f6 Panagiotis Kanavos
												 pathPointer, IntPtr.Zero);
795 4ec636f6 Panagiotis Kanavos
				}
796 4ec636f6 Panagiotis Kanavos
				finally
797 4ec636f6 Panagiotis Kanavos
				{
798 4ec636f6 Panagiotis Kanavos
					Marshal.FreeHGlobal(pathPointer);
799 4ec636f6 Panagiotis Kanavos
				}
800 4ec636f6 Panagiotis Kanavos
			}
801 4ec636f6 Panagiotis Kanavos
		}
802 4ec636f6 Panagiotis Kanavos
803 4ec636f6 Panagiotis Kanavos
		#region Event Handlers
804 4ec636f6 Panagiotis Kanavos
		
805 4ec636f6 Panagiotis Kanavos
		public void Handle(SelectiveSynchChanges message)
806 4ec636f6 Panagiotis Kanavos
		{
807 4ec636f6 Panagiotis Kanavos
			var accountName = message.Account.AccountName;
808 4ec636f6 Panagiotis Kanavos
			PithosMonitor monitor;
809 4ec636f6 Panagiotis Kanavos
			if (_monitors.TryGetValue(accountName, out monitor))
810 4ec636f6 Panagiotis Kanavos
			{
811 759bd3c4 Panagiotis Kanavos
				monitor.SetSelectivePaths(message.Uris,message.Added,message.Removed);
812 4ec636f6 Panagiotis Kanavos
813 4ec636f6 Panagiotis Kanavos
			}
814 4ec636f6 Panagiotis Kanavos
			
815 4ec636f6 Panagiotis Kanavos
		}
816 4ec636f6 Panagiotis Kanavos
817 4ec636f6 Panagiotis Kanavos
818 255f5f86 Panagiotis Kanavos
		private bool _pollStarted = false;
819 eae84ae8 Panagiotis Kanavos
820 255f5f86 Panagiotis Kanavos
		//SMELL: Doing so much work for notifications in the shell is wrong
821 255f5f86 Panagiotis Kanavos
		//The notifications should be moved to their own view/viewmodel pair
822 255f5f86 Panagiotis Kanavos
		//and different templates should be used for different message types
823 255f5f86 Panagiotis Kanavos
		//This will also allow the addition of extra functionality, eg. actions
824 255f5f86 Panagiotis Kanavos
		//
825 4ec636f6 Panagiotis Kanavos
		public void Handle(Notification notification)
826 4ec636f6 Panagiotis Kanavos
		{
827 255f5f86 Panagiotis Kanavos
			UpdateStatus();
828 e5b65606 Panagiotis Kanavos
829 4ec636f6 Panagiotis Kanavos
			if (!Settings.ShowDesktopNotifications)
830 4ec636f6 Panagiotis Kanavos
				return;
831 eae84ae8 Panagiotis Kanavos
832 255f5f86 Panagiotis Kanavos
			if (notification is PollNotification)
833 255f5f86 Panagiotis Kanavos
			{
834 255f5f86 Panagiotis Kanavos
				_pollStarted = true;
835 255f5f86 Panagiotis Kanavos
				return;
836 255f5f86 Panagiotis Kanavos
			}
837 255f5f86 Panagiotis Kanavos
			if (notification is CloudNotification)
838 255f5f86 Panagiotis Kanavos
			{
839 255f5f86 Panagiotis Kanavos
				if (!_pollStarted) 
840 255f5f86 Panagiotis Kanavos
					return;
841 255f5f86 Panagiotis Kanavos
				_pollStarted= false;
842 255f5f86 Panagiotis Kanavos
				notification.Title = "Pithos";
843 255f5f86 Panagiotis Kanavos
				notification.Message = "Start Synchronisation";
844 255f5f86 Panagiotis Kanavos
			}
845 255f5f86 Panagiotis Kanavos
846 255f5f86 Panagiotis Kanavos
			if (String.IsNullOrWhiteSpace(notification.Message) && String.IsNullOrWhiteSpace(notification.Title))
847 255f5f86 Panagiotis Kanavos
				return;
848 2dc6f765 Panagiotis Kanavos
849 4f6d51d4 Panagiotis Kanavos
			BalloonIcon icon;
850 4ec636f6 Panagiotis Kanavos
			switch (notification.Level)
851 4ec636f6 Panagiotis Kanavos
			{
852 4ec636f6 Panagiotis Kanavos
				case TraceLevel.Error:
853 4ec636f6 Panagiotis Kanavos
					icon = BalloonIcon.Error;
854 4ec636f6 Panagiotis Kanavos
					break;
855 4ec636f6 Panagiotis Kanavos
				case TraceLevel.Info:
856 4ec636f6 Panagiotis Kanavos
				case TraceLevel.Verbose:
857 4ec636f6 Panagiotis Kanavos
					icon = BalloonIcon.Info;
858 4ec636f6 Panagiotis Kanavos
					break;
859 4ec636f6 Panagiotis Kanavos
				case TraceLevel.Warning:
860 4ec636f6 Panagiotis Kanavos
					icon = BalloonIcon.Warning;
861 4ec636f6 Panagiotis Kanavos
					break;
862 4ec636f6 Panagiotis Kanavos
				default:
863 4ec636f6 Panagiotis Kanavos
					icon = BalloonIcon.None;
864 4ec636f6 Panagiotis Kanavos
					break;
865 4ec636f6 Panagiotis Kanavos
			}
866 255f5f86 Panagiotis Kanavos
			
867 4ec636f6 Panagiotis Kanavos
			if (Settings.ShowDesktopNotifications)
868 4ec636f6 Panagiotis Kanavos
			{
869 f2d88248 Panagiotis Kanavos
				var tv = (ShellView) GetView();
870 f2d88248 Panagiotis Kanavos
			    System.Action clickAction = null;
871 f2d88248 Panagiotis Kanavos
                if (notification is ExpirationNotification)
872 f2d88248 Panagiotis Kanavos
                {
873 f2d88248 Panagiotis Kanavos
                    clickAction = ()=>ShowPreferences("AccountTab");
874 f2d88248 Panagiotis Kanavos
                }
875 f2d88248 Panagiotis Kanavos
				var balloon=new PithosBalloon{Title=notification.Title,Message=notification.Message,Icon=icon,ClickAction=clickAction};
876 255f5f86 Panagiotis Kanavos
				tv.TaskbarView.ShowCustomBalloon(balloon,PopupAnimation.Fade,4000);
877 fb9d6e00 Panagiotis Kanavos
//				tv.TaskbarView.ShowBalloonTip(notification.Title, notification.Message, icon);
878 4ec636f6 Panagiotis Kanavos
			}
879 4ec636f6 Panagiotis Kanavos
		}
880 4ec636f6 Panagiotis Kanavos
		#endregion
881 4ec636f6 Panagiotis Kanavos
882 4ec636f6 Panagiotis Kanavos
		public void Handle(ShowFilePropertiesEvent message)
883 4ec636f6 Panagiotis Kanavos
		{
884 4ec636f6 Panagiotis Kanavos
			if (message == null)
885 4ec636f6 Panagiotis Kanavos
				throw new ArgumentNullException("message");
886 4ec636f6 Panagiotis Kanavos
			if (String.IsNullOrWhiteSpace(message.FileName) )
887 4ec636f6 Panagiotis Kanavos
				throw new ArgumentException("message");
888 4ec636f6 Panagiotis Kanavos
			Contract.EndContractBlock();
889 4ec636f6 Panagiotis Kanavos
890 4ec636f6 Panagiotis Kanavos
			var fileName = message.FileName;
891 255f5f86 Panagiotis Kanavos
			//TODO: Display file properties for non-container folders
892 4ec636f6 Panagiotis Kanavos
			if (File.Exists(fileName))
893 255f5f86 Panagiotis Kanavos
				//Retrieve the full name with exact casing. Pithos names are case sensitive				
894 255f5f86 Panagiotis Kanavos
				ShowFileProperties(FileInfoExtensions.GetProperFilePathCapitalization(fileName));
895 4ec636f6 Panagiotis Kanavos
			else if (Directory.Exists(fileName))
896 255f5f86 Panagiotis Kanavos
				//Retrieve the full name with exact casing. Pithos names are case sensitive
897 4f6d51d4 Panagiotis Kanavos
			{
898 255f5f86 Panagiotis Kanavos
				var path = FileInfoExtensions.GetProperDirectoryCapitalization(fileName);
899 255f5f86 Panagiotis Kanavos
				if (IsContainer(path))
900 255f5f86 Panagiotis Kanavos
					ShowContainerProperties(path);
901 255f5f86 Panagiotis Kanavos
				else
902 255f5f86 Panagiotis Kanavos
					ShowFileProperties(path);
903 4f6d51d4 Panagiotis Kanavos
			}
904 4ec636f6 Panagiotis Kanavos
		}
905 4f6d51d4 Panagiotis Kanavos
906 255f5f86 Panagiotis Kanavos
		private bool IsContainer(string path)
907 255f5f86 Panagiotis Kanavos
		{
908 255f5f86 Panagiotis Kanavos
			var matchingFolders = from account in _accounts
909 255f5f86 Panagiotis Kanavos
								  from rootFolder in Directory.GetDirectories(account.AccountPath)
910 255f5f86 Panagiotis Kanavos
								  where rootFolder.Equals(path, StringComparison.InvariantCultureIgnoreCase)
911 255f5f86 Panagiotis Kanavos
								  select rootFolder;
912 255f5f86 Panagiotis Kanavos
			return matchingFolders.Any();
913 255f5f86 Panagiotis Kanavos
		}
914 255f5f86 Panagiotis Kanavos
915 255f5f86 Panagiotis Kanavos
		public FileStatus GetFileStatus(string localFileName)
916 255f5f86 Panagiotis Kanavos
		{
917 255f5f86 Panagiotis Kanavos
			if (String.IsNullOrWhiteSpace(localFileName))
918 255f5f86 Panagiotis Kanavos
				throw new ArgumentNullException("localFileName");
919 255f5f86 Panagiotis Kanavos
			Contract.EndContractBlock();
920 255f5f86 Panagiotis Kanavos
			
921 255f5f86 Panagiotis Kanavos
			var statusKeeper = IoC.Get<IStatusKeeper>();
922 255f5f86 Panagiotis Kanavos
			var status=statusKeeper.GetFileStatus(localFileName);
923 255f5f86 Panagiotis Kanavos
			return status;
924 255f5f86 Panagiotis Kanavos
		}
925 4ec636f6 Panagiotis Kanavos
	}
926 9bae55d1 Panagiotis Kanavos
}