Example #1
0
		/// <summary>
		/// Retrieve the most recent synchronization information from the data store and set the relevant properties
		/// on the <paramref name="synchStatus"/> parameter. The return value is the same reference as the parameter.
		/// </summary>
		/// <param name="synchStatus">An <see cref="ISynchronizationStatus"/> object to populate with the most recent synchronization
		/// information from the data store.</param>
		/// <returns>
		/// Returns an <see cref="ISynchronizationStatus"/> object with updated properties based on what was retrieved
		/// from the data store.
		/// </returns>
		public static ISynchronizationStatus UpdateStatusFromDataStore(ISynchronizationStatus synchStatus)
		{
			bool processedFirstRecord = false;

			using (IDataReader dr = GetDataReaderSynchronizeSelect(synchStatus))
			{
				while (dr.Read())
				{
					if (processedFirstRecord)
					{
						// We found more than one record matching the gallery ID in the Synchronize table. Throw exception.
						throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.Synchronize_UpdateStatusFromDataStore_ExMsg1, synchStatus.GalleryId));
					}

					synchStatus.SynchId = dr["SynchId"].ToString();
					synchStatus.Status = (SynchronizationState)Enum.Parse(typeof(SynchronizationState), dr["SynchState"].ToString());
					synchStatus.TotalFileCount = (int)dr["TotalFiles"];
					synchStatus.CurrentFileIndex = (int)dr["CurrentFileIndex"];

					processedFirstRecord = true;
					break;
				}
			}

			if (!processedFirstRecord)
			{
				throw new System.Data.RowNotInTableException(string.Format(CultureInfo.CurrentCulture, Resources.Synchronize_UpdateStatusFromDataStore_ExMsg2, synchStatus.GalleryId));
			}

			return synchStatus;
		}
Example #2
0
		/// <summary>
		/// Persist the synchronization information to the data store.
		/// </summary>
		/// <param name="synchStatus">An <see cref="ISynchronizationStatus"/> object containing the synchronization information
		/// to persist to the data store.</param>
		/// <exception>Throws a GalleryServerPro.ErrorHandler.CustomExceptions.SynchronizationInProgressException if the data
		/// store indicates another synchronization is already in progress for this gallery.</exception>
		public static void SaveStatus(ISynchronizationStatus synchStatus)
		{
			SqlCommand cmd = GetCommandSynchronizeSave(synchStatus);

			cmd.Connection.Open();
			cmd.ExecuteNonQuery();
			int returnValue = Convert.ToInt32(cmd.Parameters["@ReturnValue"].Value, CultureInfo.InvariantCulture);
			cmd.Connection.Close();

			if (returnValue == 250000)
			{
				throw new GalleryServerPro.ErrorHandler.CustomExceptions.SynchronizationInProgressException();
			}
		}
        /// <summary>
        /// Persist the synchronization information to the data store.
        /// </summary>
        /// <param name="synchStatus">An <see cref="ISynchronizationStatus"/> object containing the synchronization information
        /// to persist to the data store.</param>
        /// <exception cref="GalleryServerPro.ErrorHandler.CustomExceptions.SynchronizationInProgressException">Thrown when the data
        /// store indicates another synchronization is already in progress for this gallery.</exception>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="synchStatus" /> is null.</exception>
        public override void Synchronize_SaveStatus(ISynchronizationStatus synchStatus)
        {
            if (synchStatus == null)
                throw new ArgumentNullException("synchStatus");

            using (GspContext ctx = new GspContext())
            {
                SynchronizeDto sDto = ctx.Synchronizes.Find(synchStatus.GalleryId);

                if (sDto != null)
                {
                    if ((sDto.SynchId != synchStatus.SynchId) && ((sDto.SynchState == (int)SynchronizationState.SynchronizingFiles) || (sDto.SynchState == (int)SynchronizationState.PersistingToDataStore)))
                    {
                        throw new ErrorHandler.CustomExceptions.SynchronizationInProgressException();
                    }
                    else
                    {
                        sDto.SynchId = synchStatus.SynchId;
                        sDto.SynchState = (int)synchStatus.Status;
                        sDto.TotalFiles = synchStatus.TotalFileCount;
                        sDto.CurrentFileIndex = synchStatus.CurrentFileIndex;
                    }
                }
                else
                {
                    sDto = new SynchronizeDto
                                    {
                                        SynchId = synchStatus.SynchId,
                                        FKGalleryId = synchStatus.GalleryId,
                                        SynchState = (int)synchStatus.Status,
                                        TotalFiles = synchStatus.TotalFileCount,
                                        CurrentFileIndex = synchStatus.CurrentFileIndex
                                    };

                    ctx.Synchronizes.Add(sDto);
                }

                ctx.SaveChanges();
            }
        }
        private void Initialize(string synchId, IAlbum album, string userName)
        {
            if (album == null)
                throw new ArgumentNullException("album");

            if (String.IsNullOrEmpty(userName))
                throw new ArgumentNullException("userName");

            if (!album.IsWritable)
            {
                throw new WebException(String.Format(CultureInfo.CurrentCulture, "The album is not writable (ID {0}, Title='{1}')", album.Id, album.Title));
            }

            UserName = userName;

            // Tell the status instance we are starting a new synchronization. It will throw
            // SynchronizationInProgressException if another is in progress.
            _synchStatus = SynchronizationStatus.Start(synchId, album.GalleryId);

            _synchStatus.Update(SynchronizationState.NotSet, CountFiles(album.FullPhysicalPathOnDisk), null, null, null, null, true);
        }
 private static int CalculatePercentComplete(ISynchronizationStatus synchStatus)
 {
     if (synchStatus.Status == SynchronizationState.SynchronizingFiles)
         return (int)(((double)synchStatus.CurrentFileIndex / (double)synchStatus.TotalFileCount) * 100);
     else
         return 100;
 }
		/// <summary>
		/// Retrieve the most recent synchronization information from the data store and set the relevant properties
		/// on the <paramref name="synchStatus"/> parameter. The return value is the same reference as the parameter.
		/// </summary>
		/// <param name="synchStatus">An <see cref="ISynchronizationStatus"/> object to populate with the most recent synchronization
		/// information from the data store.</param>
		/// <returns>
		/// Returns an <see cref="ISynchronizationStatus"/> object with updated properties based on what was retrieved
		/// from the data store.
		/// </returns>
		public override ISynchronizationStatus Synchronize_UpdateStatusFromDataStore(ISynchronizationStatus synchStatus)
		{
			bool updatedStatus = false;

			SQLiteConnection cn = GetDBConnectionForGallery();
			try
			{
				using (SQLiteCommand cmd = cn.CreateCommand())
				{
					const string sql = @"
SELECT SynchId, FKGalleryId, SynchState, TotalFiles, CurrentFileIndex
FROM [gs_Synchronize]
WHERE FKGalleryId = @GalleryId;";
					cmd.CommandText = sql;
					cmd.Parameters.AddWithValue("@GalleryId", ConfigManager.GetGalleryServerProConfigSection().Core.GalleryId);
					if (cn.State == ConnectionState.Closed)
						cn.Open();

					using (IDataReader dr = cmd.ExecuteReader())
					{
						while (dr.Read())
						{
							synchStatus.SynchId = dr["SynchId"].ToString();
							synchStatus.Status = (SynchronizationState)Enum.Parse(typeof(SynchronizationState), dr["SynchState"].ToString());
							synchStatus.TotalFileCount = Convert.ToInt32(dr["TotalFiles"]);
							synchStatus.CurrentFileIndex = Convert.ToInt32(dr["CurrentFileIndex"]);

							updatedStatus = true;
							break;
						}
					}
				}
			}
			finally
			{
				if (!IsTransactionInProgress())
					cn.Dispose();
			}

			if (!updatedStatus)
			{
				throw new System.Data.RowNotInTableException(string.Format(CultureInfo.CurrentCulture, Resources.Synchronize_UpdateStatusFromDataStore_ExMsg2, synchStatus.GalleryId));
			}

			return synchStatus;
		}
 private static string GetFriendlyStatusText(ISynchronizationStatus status)
 {
     switch (status.Status)
     {
         case SynchronizationState.AnotherSynchronizationInProgress:
             return Resources.GalleryServerPro.Task_Synch_Progress_Status_SynchInProgressException_Hdr;
         case SynchronizationState.Complete:
             return String.Concat(status.Status, GetProgressCount(status));
         case SynchronizationState.Error:
             return String.Concat(status.Status, GetProgressCount(status));
         case SynchronizationState.PersistingToDataStore:
             return String.Concat(Resources.GalleryServerPro.Task_Synch_Progress_Status_PersistingToDataStore_Hdr, GetProgressCount(status));
         case SynchronizationState.SynchronizingFiles:
             return String.Concat(Resources.GalleryServerPro.Task_Synch_Progress_Status_SynchInProgress_Hdr, GetProgressCount(status));
         case SynchronizationState.Aborted:
             return String.Concat(Resources.GalleryServerPro.Task_Synch_Progress_Status_Aborted_Hdr, GetProgressCount(status));
         default: throw new System.ComponentModel.InvalidEnumArgumentException("The GetFriendlyStatusText() method in synchronize.aspx encountered a SynchronizationState enum value it was not designed for. This method must be updated.");
     }
 }
Example #8
0
		/// <summary>
		/// Retrieve the most recent synchronization information from the data store and set the relevant properties
		/// on the <paramref name="synchStatus"/> parameter. The return value is the same reference as the parameter.
		/// </summary>
		/// <param name="synchStatus">An <see cref="ISynchronizationStatus"/> object to populate with the most recent synchronization
		/// information from the data store.</param>
		/// <returns>
		/// Returns an <see cref="ISynchronizationStatus"/> object with updated properties based on what was retrieved
		/// from the data store.
		/// </returns>
		public override ISynchronizationStatus Synchronize_UpdateStatusFromDataStore(ISynchronizationStatus synchStatus)
		{
			return Synchronize.UpdateStatusFromDataStore(synchStatus);
		}
        /// <summary>
        /// Persist the synchronization information to the data store.
        /// </summary>
        /// <param name="synchStatus">An <see cref="ISynchronizationStatus"/> object containing the synchronization information
        /// to persist to the data store.</param>
        /// <exception cref="GalleryServerPro.ErrorHandler.CustomExceptions.SynchronizationInProgressException">Thrown when the data
        /// store indicates another synchronization is already in progress for this gallery.</exception>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="synchStatus" /> is null.</exception>
        public override void Synchronize_SaveStatus(ISynchronizationStatus synchStatus)
        {
            if (synchStatus == null)
                throw new ArgumentNullException("synchStatus");

            Synchronize.SaveStatus(synchStatus);
        }
Example #10
0
		private static SqlCommand GetCommandSynchronizeSelect(ISynchronizationStatus synchStatus)
		{
			SqlCommand cmd = new SqlCommand(Util.GetSqlName("gs_SynchronizeSelect"), SqlDataProvider.GetDbConnection());
			cmd.CommandType = CommandType.StoredProcedure;
			cmd.Parameters.Add(new SqlParameter("@GalleryId", SqlDbType.Int));

			cmd.Parameters["@GalleryId"].Value = synchStatus.GalleryId;

			cmd.Connection.Open();

			return cmd;
		}
Example #11
0
		private static IDataReader GetDataReaderSynchronizeSelect(ISynchronizationStatus synchStatus)
		{
			IDataReader dr = GetCommandSynchronizeSelect(synchStatus).ExecuteReader(CommandBehavior.CloseConnection);
			return dr;
		}
Example #12
0
		private static SqlCommand GetCommandSynchronizeSave(ISynchronizationStatus synchStatus)
		{
			SqlCommand cmd = new SqlCommand(Util.GetSqlName("gs_SynchronizeSave"), SqlDataProvider.GetDbConnection());
			cmd.CommandType = CommandType.StoredProcedure;
			cmd.Parameters.Add(new SqlParameter("@SynchId", SqlDbType.NChar, 50));
			cmd.Parameters.Add(new SqlParameter("@GalleryId", SqlDbType.Int));
			cmd.Parameters.Add(new SqlParameter("@SynchState", SqlDbType.Int));
			cmd.Parameters.Add(new SqlParameter("@TotalFiles", SqlDbType.Int));
			cmd.Parameters.Add(new SqlParameter("@CurrentFileIndex", SqlDbType.Int));
			cmd.Parameters.Add(new SqlParameter("@ReturnValue", SqlDbType.Int));

			cmd.Parameters["@SynchId"].Value = synchStatus.SynchId;
			cmd.Parameters["@GalleryId"].Value = synchStatus.GalleryId;
			cmd.Parameters["@SynchState"].Value = synchStatus.Status;
			cmd.Parameters["@TotalFiles"].Value = synchStatus.TotalFileCount;
			cmd.Parameters["@CurrentFileIndex"].Value = synchStatus.CurrentFileIndex;
			cmd.Parameters["@ReturnValue"].Direction = ParameterDirection.ReturnValue;

			return cmd;
		}
Example #13
0
        private static IDataReader GetDataReaderSynchronizeSelect(ISynchronizationStatus synchStatus)
        {
            IDataReader dr = GetCommandSynchronizeSelect(synchStatus).ExecuteReader(CommandBehavior.CloseConnection);

            return(dr);
        }
        private static string GetProgressCount(ISynchronizationStatus status)
        {
            var curFileIndex = (status.Status == SynchronizationState.Complete ? status.TotalFileCount : status.CurrentFileIndex);

            return String.Format(CultureInfo.CurrentCulture, Resources.GalleryServerPro.Task_Synch_Progress_Status, curFileIndex, status.TotalFileCount);
        }
Example #15
0
		/// <summary>
		/// Persist the synchronization information to the data store.
		/// </summary>
		/// <param name="synchStatus">An <see cref="ISynchronizationStatus" /> object containing the synchronization information
		/// to persist to the data store.</param>
		/// <exception>Throws a GalleryServerPro.ErrorHandler.CustomExceptions.SynchronizationInProgressException if the data 
		/// store indicates another synchronization is already in progress for this gallery.</exception>
		public abstract void Synchronize_SaveStatus(ISynchronizationStatus synchStatus);
Example #16
0
		public static void Synchronize(int albumId, string synchId, bool isRecursive, bool overwriteThumb, bool overwriteOpt, bool regenerateMetadata)
		{
			// Refresh the synch status static variable. Each time we access the Instance property of the singleton, it gets its
			// properties refreshed with the latest values from the data store.
			_synchStatus = SynchronizationStatus.Instance;

			SynchronizationManager synchMgr = new SynchronizationManager();

			synchMgr.IsRecursive = isRecursive;
			synchMgr.OverwriteThumbnail = overwriteThumb;
			synchMgr.OverwriteOptimized = overwriteOpt;
			synchMgr.RegenerateMetadata = regenerateMetadata;

			try
			{
				synchMgr.Synchronize(synchId, Factory.LoadAlbumInstance(albumId, true), System.Web.HttpContext.Current.User.Identity.Name);
			}
			catch (Exception ex)
			{
				GalleryServerPro.ErrorHandler.AppErrorHandler.RecordErrorInfo(ex);
				throw;
			}
		}
Example #17
0
		/// <summary>
		/// Retrieve the most recent synchronization information from the data store and set the relevant properties
		/// on the <paramref name="synchStatus"/> parameter. The return value is the same reference as the parameter.
		/// </summary>
		/// <param name="synchStatus">An <see cref="ISynchronizationStatus"/> object to populate with the most recent synchronization
		/// information from the data store.</param>
		/// <returns>
		/// Returns an <see cref="ISynchronizationStatus"/> object with updated properties based on what was retrieved
		/// from the data store.
		/// </returns>
		public abstract ISynchronizationStatus Synchronize_UpdateStatusFromDataStore(ISynchronizationStatus synchStatus);
Example #18
0
		public void Synchronize(int albumId, string synchId, bool isRecursive, bool overwriteThumb, bool overwriteOpt, bool regenerateMetadata)
		{
			// Refresh the synch status static variable. Each time we access the Instance property of the singleton, it gets its
			// properties refreshed with the latest values from the data store.

			#region Check user authorization

			bool isUserAuthenticated = Util.IsAuthenticated;
			if (!isUserAuthenticated)
				return;

			if (!Util.IsUserAuthorized(SecurityActions.Synchronize, RoleController.GetGalleryServerRolesForUser(), albumId, false))
				return;

			#endregion
			
			lock (_synchStatus)
			{
				_synchStatus = SynchronizationStatus.Instance;
			}

			SynchronizationManager synchMgr = new SynchronizationManager();

			synchMgr.IsRecursive = isRecursive;
			synchMgr.OverwriteThumbnail = overwriteThumb;
			synchMgr.OverwriteOptimized = overwriteOpt;
			synchMgr.RegenerateMetadata = regenerateMetadata;

			try
			{
				synchMgr.Synchronize(synchId, Factory.LoadAlbumInstance(albumId, true), Util.UserName);
			}
			catch (Exception ex)
			{
				AppErrorController.LogError(ex);
				throw;
			}
		}
Example #19
0
		/// <summary>
		/// Persist the synchronization information to the data store.
		/// </summary>
		/// <param name="synchStatus">An <see cref="ISynchronizationStatus"/> object containing the synchronization information
		/// to persist to the data store.</param>
		/// <exception>Throws a GalleryServerPro.ErrorHandler.CustomExceptions.SynchronizationInProgressException if the data
		/// store indicates another synchronization is already in progress for this gallery.</exception>
		public override void Synchronize_SaveStatus(ISynchronizationStatus synchStatus)
		{
			Synchronize.SaveStatus(synchStatus);
		}
		/// <summary>
		/// Persist the synchronization information to the data store.
		/// </summary>
		/// <param name="synchStatus">An <see cref="ISynchronizationStatus"/> object containing the synchronization information
		/// to persist to the data store.</param>
		/// <exception>Throws a GalleryServerPro.ErrorHandler.CustomExceptions.SynchronizationInProgressException if the data
		/// store indicates another synchronization is already in progress for this gallery.</exception>
		public override void Synchronize_SaveStatus(ISynchronizationStatus synchStatus)
		{
			SQLiteTransaction tran = null;
			SQLiteConnection cn = GetDBConnectionForGallery();
			try
			{
				using (SQLiteCommand cmd = cn.CreateCommand())
				{
					if (cn.State == ConnectionState.Closed)
						cn.Open();

					if (!IsTransactionInProgress())
						tran = cn.BeginTransaction();

					string sql = "SELECT EXISTS(SELECT * FROM [gs_Synchronize] WHERE FKGalleryId = @GalleryId AND SynchId <> @SynchId AND (SynchState = 1 OR SynchState = 2))";
					cmd.CommandText = sql;
					cmd.Parameters.AddWithValue("@GalleryId", ConfigManager.GetGalleryServerProConfigSection().Core.GalleryId);
					cmd.Parameters.AddWithValue("@SynchId", synchStatus.SynchId);
					if (Convert.ToBoolean(cmd.ExecuteScalar()))
					{
						throw new GalleryServerPro.ErrorHandler.CustomExceptions.SynchronizationInProgressException();
					}

					sql = "SELECT EXISTS(SELECT * FROM [gs_Synchronize] WHERE FKGalleryId = @GalleryId)";
					cmd.Parameters.Clear();
					cmd.CommandText = sql;
					cmd.Parameters.AddWithValue("@GalleryId", ConfigManager.GetGalleryServerProConfigSection().Core.GalleryId);
					if (Convert.ToBoolean(cmd.ExecuteScalar()))
					{
						sql = @"
UPDATE [gs_Synchronize]
SET SynchId = @SynchId,
	FKGalleryId = @GalleryId,
	SynchState = @SynchState,
	TotalFiles = @TotalFiles,
	CurrentFileIndex = @CurrentFileIndex
WHERE FKGalleryId = @GalleryId";
						cmd.Parameters.Clear();
						cmd.CommandText = sql;
						cmd.Parameters.AddWithValue("@SynchId", synchStatus.SynchId);
						cmd.Parameters.AddWithValue("@GalleryId", ConfigManager.GetGalleryServerProConfigSection().Core.GalleryId);
						cmd.Parameters.AddWithValue("@SynchState", (int)synchStatus.Status);
						cmd.Parameters.AddWithValue("@TotalFiles", synchStatus.TotalFileCount);
						cmd.Parameters.AddWithValue("@CurrentFileIndex", synchStatus.CurrentFileIndex);
						cmd.Parameters.AddWithValue("@GalleryId", ConfigManager.GetGalleryServerProConfigSection().Core.GalleryId);
						cmd.ExecuteNonQuery();
					}
					else
					{
						sql = "INSERT INTO [gs_Synchronize] (SynchId, FKGalleryId, SynchState, TotalFiles, CurrentFileIndex)"
							+ " VALUES (@SynchId, @GalleryId, @SynchState, @TotalFiles, @CurrentFileIndex)";
						cmd.CommandText = sql;
						cmd.Parameters.Clear();
						cmd.Parameters.AddWithValue("@SynchId", synchStatus.SynchId);
						cmd.Parameters.AddWithValue("@GalleryId", ConfigManager.GetGalleryServerProConfigSection().Core.GalleryId);
						cmd.Parameters.AddWithValue("@SynchState", (int)synchStatus.Status);
						cmd.Parameters.AddWithValue("@TotalFiles", synchStatus.TotalFileCount);
						cmd.Parameters.AddWithValue("@CurrentFileIndex", synchStatus.CurrentFileIndex);
						cmd.ExecuteNonQuery();
					}

					// Commit the transaction if it's the one we created in this method.
					if (tran != null)
						tran.Commit();
				}
			}
			catch
			{
				if (tran != null)
					tran.Rollback();
				throw;
			}
			finally
			{
				if (tran != null)
					tran.Dispose();

				if (!IsTransactionInProgress())
					cn.Dispose();
			}
		}
        private void Initialize(string synchId, IAlbum album, string userName)
        {
            if (album == null)
                throw new ArgumentNullException("album");

            if (String.IsNullOrEmpty(userName))
                throw new ArgumentNullException("userName");

            this._userName = userName;

            #region Set up the _synchStatus instance

            // Tell the status instance we are starting a new synchronization. It will throw
            // SynchronizationInProgressException if another is in progress.
            this._synchStatus = SynchronizationStatus.Start(synchId, album.GalleryId);

            this._synchStatus.Update(SynchronizationState.NotSet, CountFiles(album.FullPhysicalPathOnDisk), null, null, null, null, true);

            #endregion

            #region Populate the _albumsFromDataStore and _mediaObjectsFromDataStore dictionary objects and set each to IsSynchronized = false

            this._albumsFromDataStore = new Dictionary<String, IAlbum>();
            this._mediaObjectsFromDataStore = new Dictionary<String, IGalleryObject>(this._synchStatus.TotalFileCount);

            // Fill _albums and _mediaObjects with the albums and media objects for this album as currently stored
            // in the data store. We'll be comparing these objects with those we find on the hard drive. Act recursively
            // if IsRecursive = true. Set IsSynchronized = false for each object. (We'll be setting it back to true
            // as we synchronize each object.)
            album.IsSynchronized = false;
            album.RegenerateThumbnailOnSave = this.OverwriteThumbnail;

            if (!album.IsWritable)
            {
                throw new WebException(String.Format(CultureInfo.CurrentCulture, "The album is not writeable (ID {0}, Title='{1}')", album.Id, album.Title));
            }

            this._albumsFromDataStore.Add(album.FullPhysicalPathOnDisk, album);

            foreach (IGalleryObject mediaObject in album.GetChildGalleryObjects(GalleryObjectType.MediaObject))
            {
                if (!mediaObject.IsWritable)
                {
                    throw new WebException(String.Format(CultureInfo.CurrentCulture, "The media object is not writeable (ID {0}, Title='{1}')", mediaObject.Id, mediaObject.Title));
                }

                mediaObject.IsSynchronized = false;
                mediaObject.RegenerateThumbnailOnSave = this.OverwriteThumbnail;
                mediaObject.RegenerateOptimizedOnSave = this.OverwriteOptimized;
                mediaObject.ExtractMetadataOnSave = this.RegenerateMetadata;

                if (!String.IsNullOrEmpty(mediaObject.Hashkey))
                {
                    this._mediaObjectsFromDataStore.Add(mediaObject.Hashkey, mediaObject);
                }
            }

            if (this._isRecursive)
            {
                AddChildAlbumsAndGalleryObjectsAndSetToUnsynchronized(this._albumsFromDataStore, this._mediaObjectsFromDataStore, album);
            }

            #endregion

            // Clear the list of hash keys so we're starting with a fresh load from the data store.
            MediaObjectHashKeys.Clear();
        }
        private static string CalculateSyncRate(ISynchronizationStatus synchStatus)
        {
            if (synchStatus.CurrentFileIndex == 0)
                return String.Empty;

            var elapsedTime = DateTime.UtcNow.Subtract(synchStatus.BeginTimestampUtc).TotalSeconds;

            return String.Format(CultureInfo.CurrentCulture, "{0:N1} {1}", (synchStatus.CurrentFileIndex / elapsedTime), Resources.GalleryServerPro.Task_Synch_Progress_SynchRate_Units);
        }