2010-10-16 22:48:50 +00:00
|
|
|
|
// SparkleShare, an instant update workflow to Git.
|
|
|
|
|
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
|
|
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2010-11-21 12:33:24 +00:00
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2010-10-16 22:48:50 +00:00
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
2010-12-22 16:27:18 +00:00
|
|
|
|
|
2010-10-16 22:48:50 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.IO;
|
2010-11-21 17:10:48 +00:00
|
|
|
|
using System.Net;
|
2010-12-22 16:27:18 +00:00
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
using System.Text;
|
2011-05-09 23:30:20 +00:00
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Xml;
|
2010-12-22 16:27:18 +00:00
|
|
|
|
|
2011-03-16 23:21:13 +00:00
|
|
|
|
using Mono.Unix;
|
|
|
|
|
using SparkleLib;
|
|
|
|
|
|
2010-10-16 22:48:50 +00:00
|
|
|
|
namespace SparkleShare {
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public abstract class SparkleController {
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public List <SparkleRepo> Repositories;
|
|
|
|
|
public string FolderSize;
|
|
|
|
|
public bool FirstRun;
|
|
|
|
|
public readonly string SparklePath;
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public event OnQuitWhileSyncingEventHandler OnQuitWhileSyncing;
|
|
|
|
|
public delegate void OnQuitWhileSyncingEventHandler ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public event FolderFetchedEventHandler FolderFetched;
|
|
|
|
|
public delegate void FolderFetchedEventHandler ();
|
|
|
|
|
|
|
|
|
|
public event FolderFetchErrorEventHandler FolderFetchError;
|
|
|
|
|
public delegate void FolderFetchErrorEventHandler ();
|
|
|
|
|
|
|
|
|
|
public event FolderListChangedEventHandler FolderListChanged;
|
|
|
|
|
public delegate void FolderListChangedEventHandler ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public event FolderSizeChangedEventHandler FolderSizeChanged;
|
|
|
|
|
public delegate void FolderSizeChangedEventHandler (string folder_size);
|
|
|
|
|
|
|
|
|
|
public event AvatarFetchedEventHandler AvatarFetched;
|
|
|
|
|
public delegate void AvatarFetchedEventHandler ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public event OnIdleEventHandler OnIdle;
|
|
|
|
|
public delegate void OnIdleEventHandler ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public event OnSyncingEventHandler OnSyncing;
|
|
|
|
|
public delegate void OnSyncingEventHandler ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public event OnErrorEventHandler OnError;
|
|
|
|
|
public delegate void OnErrorEventHandler ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public event OnInvitationEventHandler OnInvitation;
|
|
|
|
|
public delegate void OnInvitationEventHandler (string server, string folder, string token);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public event ConflictNotificationRaisedEventHandler ConflictNotificationRaised;
|
|
|
|
|
public delegate void ConflictNotificationRaisedEventHandler ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public event NotificationRaisedEventHandler NotificationRaised;
|
|
|
|
|
public delegate void NotificationRaisedEventHandler (string user_name, string user_email,
|
|
|
|
|
string message, string repository_path);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-03-24 10:22:10 +00:00
|
|
|
|
public event NewVersionAvailableEventHandler NewVersionAvailable;
|
|
|
|
|
public delegate void NewVersionAvailableEventHandler (string new_version);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-03-24 10:22:10 +00:00
|
|
|
|
public event VersionUpToDateEventHandler VersionUpToDate;
|
|
|
|
|
public delegate void VersionUpToDateEventHandler ();
|
|
|
|
|
|
|
|
|
|
|
2011-03-16 23:21:13 +00:00
|
|
|
|
// Short alias for the translations
|
|
|
|
|
public static string _ (string s)
|
|
|
|
|
{
|
|
|
|
|
return Catalog.GetString (s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public SparkleController ()
|
2011-05-09 23:32:09 +00:00
|
|
|
|
{
|
2011-05-09 23:30:20 +00:00
|
|
|
|
SparklePath = SparklePaths.SparklePath;
|
|
|
|
|
|
2011-03-20 15:10:42 +00:00
|
|
|
|
// Remove temporary file
|
|
|
|
|
if (Directory.Exists (SparklePaths.SparkleTmpPath))
|
|
|
|
|
Directory.Delete (SparklePaths.SparkleTmpPath, true);
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
InstallLauncher ();
|
|
|
|
|
EnableSystemAutostart ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Create the SparkleShare folder and add it to the bookmarks
|
|
|
|
|
if (CreateSparkleShareFolder ())
|
|
|
|
|
AddToBookmarks ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
FolderSize = GetFolderSize ();
|
2010-11-21 19:23:43 +00:00
|
|
|
|
|
2011-05-09 23:30:20 +00:00
|
|
|
|
string global_config_file_path = Path.Combine (SparklePaths.SparkleConfigPath, "config.xml");
|
|
|
|
|
string old_global_config_file_path = Path.Combine (SparklePaths.SparkleConfigPath, "config");
|
2010-11-21 19:23:43 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Show the introduction screen if SparkleShare isn't configured
|
|
|
|
|
if (!File.Exists (global_config_file_path)) {
|
2011-05-09 23:30:20 +00:00
|
|
|
|
if (File.Exists (old_global_config_file_path)) {
|
|
|
|
|
MigrateConfig ();
|
|
|
|
|
FirstRun = false;
|
|
|
|
|
} else {
|
|
|
|
|
FirstRun = true;
|
|
|
|
|
}
|
2011-05-02 15:46:31 +00:00
|
|
|
|
} else {
|
|
|
|
|
FirstRun = false;
|
|
|
|
|
AddKey ();
|
|
|
|
|
}
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Watch the SparkleShare folder
|
|
|
|
|
FileSystemWatcher watcher = new FileSystemWatcher (SparklePaths.SparklePath) {
|
|
|
|
|
IncludeSubdirectories = false,
|
|
|
|
|
EnableRaisingEvents = true,
|
|
|
|
|
Filter = "*"
|
|
|
|
|
};
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Remove the repository when a delete event occurs
|
|
|
|
|
watcher.Deleted += delegate (object o, FileSystemEventArgs args) {
|
|
|
|
|
RemoveRepository (args.FullPath);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-03-25 10:24:46 +00:00
|
|
|
|
if (FolderListChanged != null)
|
|
|
|
|
FolderListChanged ();
|
|
|
|
|
|
|
|
|
|
FolderSize = GetFolderSize ();
|
|
|
|
|
|
|
|
|
|
if (FolderSizeChanged != null)
|
|
|
|
|
FolderSizeChanged (FolderSize);
|
2011-05-02 15:46:31 +00:00
|
|
|
|
};
|
2011-03-25 10:24:46 +00:00
|
|
|
|
|
2011-05-08 23:44:29 +00:00
|
|
|
|
// TODO: Only support removing because 1. removing causes crashes and 2. Backend will be determined in
|
|
|
|
|
// the Intro and added to a table with the repo type, so we wont' know what type will be added
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Add the repository when a create event occurs
|
|
|
|
|
watcher.Created += delegate (object o, FileSystemEventArgs args) {
|
2011-03-25 10:24:46 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Handle invitations when the user saves an
|
|
|
|
|
// invitation into the SparkleShare folder
|
|
|
|
|
if (args.Name.EndsWith (".sparkle") && !FirstRun) {
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
XmlDocument xml_doc = new XmlDocument ();
|
|
|
|
|
xml_doc.Load (args.Name);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
string server = xml_doc.GetElementsByTagName ("server") [0].InnerText;
|
|
|
|
|
string folder = xml_doc.GetElementsByTagName ("folder") [0].InnerText;
|
|
|
|
|
string token = xml_doc.GetElementsByTagName ("token") [0].InnerText;
|
|
|
|
|
|
|
|
|
|
// FIXME: this is broken :\
|
|
|
|
|
if (OnInvitation != null)
|
|
|
|
|
OnInvitation (server, folder, token);
|
|
|
|
|
} else if (SparkleRepo.IsRepo (args.FullPath)) {
|
|
|
|
|
AddRepository (args.FullPath);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-03-25 10:24:46 +00:00
|
|
|
|
if (FolderListChanged != null)
|
|
|
|
|
FolderListChanged ();
|
|
|
|
|
|
|
|
|
|
FolderSize = GetFolderSize ();
|
|
|
|
|
|
|
|
|
|
if (FolderSizeChanged != null)
|
|
|
|
|
FolderSizeChanged (FolderSize);
|
2011-05-02 15:46:31 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
2011-03-25 10:24:46 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
CreateConfigurationFolders ();
|
|
|
|
|
new Thread (new ThreadStart (PopulateRepositories)).Start ();
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-09 23:30:20 +00:00
|
|
|
|
|
|
|
|
|
// TODO: remove this later
|
|
|
|
|
private void MigrateConfig () {
|
|
|
|
|
string global_config_file_path = Path.Combine (SparklePaths.SparkleConfigPath, "config.xml");
|
|
|
|
|
string old_global_config_file_path = Path.Combine (SparklePaths.SparkleConfigPath, "config");
|
|
|
|
|
|
|
|
|
|
StreamReader reader = new StreamReader (old_global_config_file_path);
|
|
|
|
|
string global_config_file = reader.ReadToEnd ();
|
|
|
|
|
reader.Close ();
|
|
|
|
|
|
|
|
|
|
Regex regex = new Regex (@"name.+= (.+)");
|
|
|
|
|
Match match = regex.Match (global_config_file);
|
|
|
|
|
|
|
|
|
|
string user_name = match.Groups [1].Value;
|
|
|
|
|
|
|
|
|
|
regex = new Regex (@"email.+= (.+)");
|
|
|
|
|
match = regex.Match (global_config_file);
|
|
|
|
|
|
|
|
|
|
string user_email = match.Groups [1].Value;
|
|
|
|
|
|
|
|
|
|
WriteUserInfo (user_name, user_email);
|
|
|
|
|
File.Delete (old_global_config_file_path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Uploads the user's public key to the server
|
|
|
|
|
public bool AcceptInvitation (string server, string folder, string token)
|
|
|
|
|
{
|
|
|
|
|
// The location of the user's public key for SparkleShare
|
|
|
|
|
string public_key_file_path = SparkleHelpers.CombineMore (SparklePaths.HomePath, ".ssh",
|
|
|
|
|
"sparkleshare." + UserEmail + ".key.pub");
|
|
|
|
|
|
|
|
|
|
if (!File.Exists (public_key_file_path))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
StreamReader reader = new StreamReader (public_key_file_path);
|
|
|
|
|
string public_key = reader.ReadToEnd ();
|
|
|
|
|
reader.Close ();
|
|
|
|
|
|
|
|
|
|
string url = "https://" + server + "/?folder=" + folder +
|
|
|
|
|
"&token=" + token + "&pubkey=" + public_key;
|
|
|
|
|
|
|
|
|
|
SparkleHelpers.DebugInfo ("WebRequest", url);
|
|
|
|
|
|
|
|
|
|
HttpWebRequest request = (HttpWebRequest) WebRequest.Create (url);
|
|
|
|
|
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
|
|
|
|
|
|
|
|
|
|
if (response.StatusCode == HttpStatusCode.OK) {
|
|
|
|
|
response.Close ();
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
response.Close ();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public List <string> Folders
|
|
|
|
|
{
|
|
|
|
|
get {
|
|
|
|
|
List <string> folders = new List <string> ();
|
|
|
|
|
|
|
|
|
|
foreach (SparkleRepo repo in Repositories)
|
|
|
|
|
folders.Add (repo.LocalPath);
|
|
|
|
|
|
|
|
|
|
return folders;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public List <SparkleCommit> GetLog (string name)
|
|
|
|
|
{
|
|
|
|
|
string path = Path.Combine (SparklePaths.SparklePath, name);
|
|
|
|
|
int log_size = 30;
|
|
|
|
|
|
|
|
|
|
foreach (SparkleRepo repo in Repositories) {
|
|
|
|
|
if (repo.LocalPath.Equals (path))
|
|
|
|
|
return repo.GetCommits (log_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public abstract string EventLogHTML { get; }
|
|
|
|
|
public abstract string DayEntryHTML { get; }
|
|
|
|
|
public abstract string EventEntryHTML { get; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public string GetHTMLLog (string name)
|
|
|
|
|
{
|
|
|
|
|
List <SparkleCommit> commits = GetLog (name);
|
|
|
|
|
List <ActivityDay> activity_days = new List <ActivityDay> ();
|
|
|
|
|
|
|
|
|
|
if (commits.Count == 0)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
foreach (SparkleCommit commit in commits) {
|
|
|
|
|
GetAvatar (commit.UserEmail, 36);
|
|
|
|
|
|
|
|
|
|
bool commit_inserted = false;
|
|
|
|
|
foreach (ActivityDay stored_activity_day in activity_days) {
|
|
|
|
|
if (stored_activity_day.DateTime.Year == commit.DateTime.Year &&
|
|
|
|
|
stored_activity_day.DateTime.Month == commit.DateTime.Month &&
|
|
|
|
|
stored_activity_day.DateTime.Day == commit.DateTime.Day) {
|
|
|
|
|
|
|
|
|
|
stored_activity_day.Add (commit);
|
|
|
|
|
commit_inserted = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!commit_inserted) {
|
|
|
|
|
ActivityDay activity_day = new ActivityDay (commit.DateTime);
|
|
|
|
|
activity_day.Add (commit);
|
|
|
|
|
activity_days.Add (activity_day);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string event_log_html = EventLogHTML;
|
|
|
|
|
string day_entry_html = DayEntryHTML;
|
|
|
|
|
string event_entry_html = EventEntryHTML;
|
|
|
|
|
string event_log = "";
|
|
|
|
|
|
|
|
|
|
foreach (ActivityDay activity_day in activity_days) {
|
|
|
|
|
string event_entries = "";
|
|
|
|
|
|
|
|
|
|
foreach (SparkleCommit change_set in activity_day) {
|
|
|
|
|
string event_entry = "<dl>";
|
|
|
|
|
|
|
|
|
|
if (change_set.IsMerge) {
|
|
|
|
|
event_entry += "<dt>Merged a branch</dt>";
|
|
|
|
|
} else {
|
|
|
|
|
if (change_set.Edited.Count > 0) {
|
|
|
|
|
event_entry += "<dt>Edited</dt>";
|
2011-04-28 11:49:14 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
foreach (string file_path in change_set.Edited) {
|
|
|
|
|
string absolute_file_path = SparkleHelpers.CombineMore (SparklePaths.SparklePath,
|
|
|
|
|
name, file_path);
|
|
|
|
|
|
|
|
|
|
if (File.Exists (absolute_file_path))
|
|
|
|
|
event_entry += "<dd><a href='" + absolute_file_path + "'>" + file_path + "</a></dd>";
|
|
|
|
|
else
|
|
|
|
|
event_entry += "<dd>" + file_path + "</dd>";
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-04-28 11:49:14 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (change_set.Added.Count > 0) {
|
|
|
|
|
event_entry += "<dt>Added</dt>";
|
2011-04-28 11:49:14 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
foreach (string file_path in change_set.Added) {
|
2011-04-28 11:49:14 +00:00
|
|
|
|
string absolute_file_path = SparkleHelpers.CombineMore (SparklePaths.SparklePath,
|
|
|
|
|
name, file_path);
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (File.Exists (absolute_file_path))
|
2011-04-28 11:49:14 +00:00
|
|
|
|
event_entry += "<dd><a href='" + absolute_file_path + "'>" + file_path + "</a></dd>";
|
2011-05-02 15:46:31 +00:00
|
|
|
|
else
|
2011-04-28 11:49:14 +00:00
|
|
|
|
event_entry += "<dd>" + file_path + "</dd>";
|
2011-05-02 15:46:31 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-04-28 11:49:14 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (change_set.Deleted.Count > 0) {
|
|
|
|
|
event_entry += "<dt>Deleted</dt>";
|
2011-04-28 11:49:14 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
foreach (string file_path in change_set.Deleted) {
|
|
|
|
|
string absolute_file_path = SparkleHelpers.CombineMore (SparklePaths.SparklePath,
|
|
|
|
|
name, file_path);
|
|
|
|
|
|
|
|
|
|
if (File.Exists (absolute_file_path))
|
|
|
|
|
event_entry += "<dd><a href='" + absolute_file_path + "'>" + file_path + "</a></dd>";
|
|
|
|
|
else
|
|
|
|
|
event_entry += "<dd>" + file_path + "</dd>";
|
2011-04-28 11:49:14 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (change_set.MovedFrom.Count > 0) {
|
|
|
|
|
event_entry += "<dt>Moved</dt>";
|
|
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
foreach (string file_path in change_set.MovedFrom) {
|
|
|
|
|
string to_file_path = change_set.MovedTo [i];
|
|
|
|
|
string absolute_file_path = SparkleHelpers.CombineMore (SparklePaths.SparklePath,
|
|
|
|
|
name, file_path);
|
|
|
|
|
string absolute_to_file_path = SparkleHelpers.CombineMore (SparklePaths.SparklePath,
|
|
|
|
|
name, to_file_path);
|
|
|
|
|
|
|
|
|
|
if (File.Exists (absolute_file_path))
|
2011-05-02 15:46:31 +00:00
|
|
|
|
event_entry += "<dd><a href='" + absolute_file_path + "'>" + file_path + "</a><br/>" +
|
|
|
|
|
"<span class='moved-arrow'>→</span> ";
|
2011-04-28 11:49:14 +00:00
|
|
|
|
else
|
2011-05-02 15:46:31 +00:00
|
|
|
|
event_entry += "<dd>" + file_path + "<br/>" +
|
|
|
|
|
"<span class='moved-arrow'>→</span> ";
|
2011-04-28 11:49:14 +00:00
|
|
|
|
|
|
|
|
|
if (File.Exists (absolute_to_file_path))
|
|
|
|
|
event_entry += "<a href='" + absolute_to_file_path + "'>" + to_file_path + "</a></dd>";
|
|
|
|
|
else
|
|
|
|
|
event_entry += to_file_path + "</dd>";
|
|
|
|
|
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-05-02 15:46:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
event_entry += "</dl>";
|
|
|
|
|
event_entries += event_entry_html.Replace ("<!-- $event-entry-content -->", event_entry)
|
|
|
|
|
.Replace ("<!-- $event-user-name -->", change_set.UserName)
|
|
|
|
|
.Replace ("<!-- $event-avatar-url -->", "file://" + GetAvatar (change_set.UserEmail, 36) )
|
|
|
|
|
.Replace ("<!-- $event-time -->", change_set.DateTime.ToString ("H:mm"));
|
|
|
|
|
}
|
2010-12-22 16:27:18 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
string day_entry = "";
|
|
|
|
|
DateTime today = DateTime.Now;
|
|
|
|
|
DateTime yesterday = DateTime.Now.AddDays (-1);
|
2010-12-22 16:27:18 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (today.Day == activity_day.DateTime.Day &&
|
|
|
|
|
today.Month == activity_day.DateTime.Month &&
|
|
|
|
|
today.Year == activity_day.DateTime.Year) {
|
2010-12-22 16:27:18 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
day_entry = day_entry_html.Replace ("<!-- $day-entry-header -->", "<b>Today</b>");
|
2010-12-22 16:27:18 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
} else if (yesterday.Day == activity_day.DateTime.Day &&
|
|
|
|
|
yesterday.Month == activity_day.DateTime.Month &&
|
|
|
|
|
yesterday.Year == activity_day.DateTime.Year) {
|
2010-12-22 16:27:18 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
day_entry = day_entry_html.Replace ("<!-- $day-entry-header -->", "<b>Yesterday</b>");
|
2010-12-22 16:27:18 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
} else {
|
2010-12-22 16:27:18 +00:00
|
|
|
|
|
2011-03-16 23:21:13 +00:00
|
|
|
|
if (activity_day.DateTime.Year != DateTime.Now.Year) {
|
|
|
|
|
|
|
|
|
|
// TRANSLATORS: This is the date in the event logs
|
|
|
|
|
day_entry = day_entry_html.Replace ("<!-- $day-entry-header -->",
|
2011-05-02 15:46:31 +00:00
|
|
|
|
"<b>" + activity_day.DateTime.ToString (_("ddd MMM d, yyyy")) + "</b>");
|
2011-03-16 23:21:13 +00:00
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
// TRANSLATORS: This is the date in the event logs, without the year
|
|
|
|
|
day_entry = day_entry_html.Replace ("<!-- $day-entry-header -->",
|
|
|
|
|
"<b>" + activity_day.DateTime.ToString (_("ddd MMM d")) + "</b>");
|
|
|
|
|
}
|
2011-05-02 15:46:31 +00:00
|
|
|
|
}
|
2010-12-22 16:27:18 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
event_log += day_entry.Replace ("<!-- $day-entry-content -->", event_entries);
|
|
|
|
|
}
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
string html = event_log_html.Replace ("<!-- $event-log-content -->", event_log);
|
|
|
|
|
return html;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Creates a folder in the user's home folder to store configuration
|
|
|
|
|
private void CreateConfigurationFolders ()
|
|
|
|
|
{
|
|
|
|
|
if (!Directory.Exists (SparklePaths.SparkleTmpPath))
|
|
|
|
|
Directory.CreateDirectory (SparklePaths.SparkleTmpPath);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
string config_path = SparklePaths.SparkleConfigPath;
|
|
|
|
|
string local_icon_path = SparklePaths.SparkleLocalIconPath;
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (!Directory.Exists (config_path)) {
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Create a folder to store settings
|
|
|
|
|
Directory.CreateDirectory (config_path);
|
|
|
|
|
SparkleHelpers.DebugInfo ("Config", "Created '" + config_path + "'");
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Create a folder to store the avatars
|
|
|
|
|
Directory.CreateDirectory (local_icon_path);
|
|
|
|
|
SparkleHelpers.DebugInfo ("Config", "Created '" + local_icon_path + "'");
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
string notify_setting_file = SparkleHelpers.CombineMore (config_path, "sparkleshare.notify");
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Enable notifications by default
|
|
|
|
|
if (!File.Exists (notify_setting_file))
|
|
|
|
|
File.Create (notify_setting_file);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Creates a .desktop entry in autostart folder to
|
|
|
|
|
// start SparkleShare automatically at login
|
|
|
|
|
public abstract void EnableSystemAutostart ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Installs a launcher so the user can launch SparkleShare
|
|
|
|
|
// from the Internet category if needed
|
|
|
|
|
public abstract void InstallLauncher ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Adds the SparkleShare folder to the user's
|
|
|
|
|
// list of bookmarked places
|
|
|
|
|
public abstract void AddToBookmarks ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Creates the SparkleShare folder in the user's home folder
|
|
|
|
|
public abstract bool CreateSparkleShareFolder ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Opens the SparkleShare folder or an (optional) subfolder
|
|
|
|
|
public abstract void OpenSparkleShareFolder (string subfolder);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2010-11-02 10:45:10 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Fires events for the current syncing state
|
|
|
|
|
private void UpdateState ()
|
|
|
|
|
{
|
|
|
|
|
foreach (SparkleRepo repo in Repositories) {
|
|
|
|
|
if (repo.IsSyncing || repo.IsBuffering) {
|
|
|
|
|
if (OnSyncing != null)
|
|
|
|
|
OnSyncing ();
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
return;
|
|
|
|
|
} else if (repo.HasUnsyncedChanges) {
|
|
|
|
|
if (OnError != null)
|
|
|
|
|
OnError ();
|
2010-11-02 10:45:10 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (OnIdle != null)
|
|
|
|
|
OnIdle ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
FolderSize = GetFolderSize ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (FolderSizeChanged != null)
|
|
|
|
|
FolderSizeChanged (FolderSize);
|
|
|
|
|
}
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Adds a repository to the list of repositories
|
|
|
|
|
private void AddRepository (string folder_path)
|
|
|
|
|
{
|
2011-05-08 23:44:29 +00:00
|
|
|
|
// TODO: determine the backend type here
|
|
|
|
|
// need to keep track of a table with folder+backendtype
|
|
|
|
|
// and use GitBackend.IsValidFolder (string path);
|
|
|
|
|
|
|
|
|
|
// Check if the folder is a Git repository TODO: remove later
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (!SparkleRepo.IsRepo (folder_path))
|
|
|
|
|
return;
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-08 23:44:29 +00:00
|
|
|
|
|
2011-05-08 23:04:53 +00:00
|
|
|
|
SparkleRepo repo = new SparkleRepo (folder_path, SparkleBackend.DefaultBackend);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
repo.NewCommit += delegate (SparkleCommit commit, string repository_path) {
|
|
|
|
|
string message = FormatMessage (commit);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (NotificationRaised != null)
|
|
|
|
|
NotificationRaised (commit.UserName, commit.UserEmail, message, repository_path);
|
|
|
|
|
};
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
repo.FetchingStarted += delegate {
|
|
|
|
|
UpdateState ();
|
|
|
|
|
};
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
repo.FetchingFinished += delegate {
|
|
|
|
|
UpdateState ();
|
|
|
|
|
};
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
repo.FetchingFailed += delegate {
|
|
|
|
|
UpdateState ();
|
|
|
|
|
};
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
repo.ChangesDetected += delegate {
|
|
|
|
|
UpdateState ();
|
|
|
|
|
};
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
repo.PushingStarted += delegate {
|
|
|
|
|
UpdateState ();
|
|
|
|
|
};
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
repo.PushingFinished += delegate {
|
|
|
|
|
UpdateState ();
|
|
|
|
|
};
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
repo.CommitEndedUpEmpty += delegate {
|
|
|
|
|
UpdateState ();
|
|
|
|
|
};
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
repo.PushingFailed += delegate {
|
|
|
|
|
UpdateState ();
|
|
|
|
|
};
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
repo.ConflictDetected += delegate {
|
|
|
|
|
if (ConflictNotificationRaised != null)
|
|
|
|
|
ConflictNotificationRaised ();
|
|
|
|
|
};
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
Repositories.Add (repo);
|
|
|
|
|
}
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Removes a repository from the list of repositories and
|
|
|
|
|
// updates the statusicon menu
|
|
|
|
|
private void RemoveRepository (string folder_path)
|
|
|
|
|
{
|
|
|
|
|
string folder_name = Path.GetFileName (folder_path);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
for (int i = 0; i < Repositories.Count; i++) {
|
|
|
|
|
SparkleRepo repo = Repositories [i];
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (repo.Name.Equals (folder_name)) {
|
|
|
|
|
Repositories.Remove (repo);
|
|
|
|
|
repo.Dispose ();
|
|
|
|
|
repo = null;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-03-24 00:16:51 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Updates the list of repositories with all the
|
|
|
|
|
// folders in the SparkleShare folder
|
|
|
|
|
private void PopulateRepositories ()
|
|
|
|
|
{
|
|
|
|
|
Repositories = new List <SparkleRepo> ();
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
foreach (string folder_path in Directory.GetDirectories (SparklePaths.SparklePath))
|
|
|
|
|
AddRepository (folder_path);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-03-25 10:24:46 +00:00
|
|
|
|
if (FolderListChanged != null)
|
|
|
|
|
FolderListChanged ();
|
|
|
|
|
|
|
|
|
|
FolderSize = GetFolderSize ();
|
|
|
|
|
|
|
|
|
|
if (FolderSizeChanged != null)
|
|
|
|
|
FolderSizeChanged (FolderSize);
|
2011-05-02 15:46:31 +00:00
|
|
|
|
}
|
2011-03-25 10:24:46 +00:00
|
|
|
|
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public bool NotificationsEnabled {
|
|
|
|
|
get {
|
|
|
|
|
string notify_setting_file_path = Path.Combine (SparklePaths.SparkleConfigPath,
|
|
|
|
|
"sparkleshare.notify");
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
return File.Exists (notify_setting_file_path);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public void ToggleNotifications () {
|
|
|
|
|
string notify_setting_file_path = Path.Combine (SparklePaths.SparkleConfigPath,
|
|
|
|
|
"sparkleshare.notify");
|
|
|
|
|
|
|
|
|
|
if (File.Exists (notify_setting_file_path))
|
|
|
|
|
File.Delete (notify_setting_file_path);
|
|
|
|
|
else
|
|
|
|
|
File.Create (notify_setting_file_path);
|
|
|
|
|
}
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
private string GetFolderSize ()
|
|
|
|
|
{
|
|
|
|
|
double folder_size = CalculateFolderSize (new DirectoryInfo (SparklePaths.SparklePath));
|
|
|
|
|
return FormatFolderSize (folder_size);
|
|
|
|
|
}
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
private string FormatMessage (SparkleCommit commit)
|
|
|
|
|
{
|
|
|
|
|
string file_name = "";
|
|
|
|
|
string message = null;
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (commit.Added.Count > 0) {
|
|
|
|
|
foreach (string added in commit.Added) {
|
|
|
|
|
file_name = added;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
message = String.Format (_("added ‘{0}’"), file_name);
|
|
|
|
|
}
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (commit.Edited.Count > 0) {
|
|
|
|
|
foreach (string modified in commit.Edited) {
|
|
|
|
|
file_name = modified;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
message = String.Format (_("edited ‘{0}’"), file_name);
|
|
|
|
|
}
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (commit.Deleted.Count > 0) {
|
|
|
|
|
foreach (string removed in commit.Deleted) {
|
|
|
|
|
file_name = removed;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
message = String.Format (_("deleted ‘{0}’"), file_name);
|
|
|
|
|
}
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
int changes_count = (commit.Added.Count +
|
|
|
|
|
commit.Edited.Count +
|
|
|
|
|
commit.Deleted.Count) - 1;
|
2011-03-20 14:59:35 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (changes_count > 0)
|
2011-05-05 09:21:16 +00:00
|
|
|
|
message += "" + String.Format (" and {0} more", changes_count);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
return message;
|
|
|
|
|
}
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Recursively gets a folder's size in bytes
|
|
|
|
|
private double CalculateFolderSize (DirectoryInfo parent)
|
|
|
|
|
{
|
|
|
|
|
if (!Directory.Exists (parent.ToString ()))
|
|
|
|
|
return 0;
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
double size = 0;
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Ignore the temporary 'rebase-apply' and '.tmp' directories. This prevents potential
|
|
|
|
|
// crashes when files are being queried whilst the files have already been deleted.
|
|
|
|
|
if (parent.Name.Equals ("rebase-apply") ||
|
|
|
|
|
parent.Name.Equals (".tmp"))
|
|
|
|
|
return 0;
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
foreach (FileInfo file in parent.GetFiles()) {
|
|
|
|
|
if (!file.Exists)
|
|
|
|
|
return 0;
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
size += file.Length;
|
|
|
|
|
}
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
foreach (DirectoryInfo directory in parent.GetDirectories())
|
|
|
|
|
size += CalculateFolderSize (directory);
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
return size;
|
|
|
|
|
}
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Format a file size nicely with small caps.
|
|
|
|
|
// Example: 1048576 becomes "1 ᴍʙ"
|
2011-03-13 21:03:40 +00:00
|
|
|
|
private string FormatFolderSize (double byte_count)
|
|
|
|
|
{
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (byte_count >= 1099511627776)
|
|
|
|
|
return String.Format ("{0:##.##} ᴛʙ", Math.Round (byte_count / 1099511627776, 1));
|
|
|
|
|
else if (byte_count >= 1073741824)
|
|
|
|
|
return String.Format ("{0:##.##} ɢʙ", Math.Round (byte_count / 1073741824, 1));
|
2011-03-13 21:03:40 +00:00
|
|
|
|
else if (byte_count >= 1048576)
|
2011-05-02 15:46:31 +00:00
|
|
|
|
return String.Format ("{0:##.##} ᴍʙ", Math.Round (byte_count / 1048576, 1));
|
|
|
|
|
else if (byte_count >= 1024)
|
|
|
|
|
return String.Format ("{0:##.##} ᴋʙ", Math.Round (byte_count / 1024, 1));
|
|
|
|
|
else
|
|
|
|
|
return byte_count.ToString () + " bytes";
|
2010-10-16 22:48:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-18 00:29:31 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public void OpenSparkleShareFolder ()
|
|
|
|
|
{
|
|
|
|
|
OpenSparkleShareFolder ("");
|
|
|
|
|
}
|
2010-11-18 00:29:31 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
|
|
|
|
|
// Adds the user's SparkleShare key to the ssh-agent,
|
|
|
|
|
// so all activity is done with this key
|
|
|
|
|
public void AddKey ()
|
|
|
|
|
{
|
|
|
|
|
string keys_path = SparklePaths.SparkleKeysPath;
|
|
|
|
|
string key_file_name = "sparkleshare." + UserEmail + ".key";
|
|
|
|
|
|
|
|
|
|
Process process = new Process ();
|
|
|
|
|
process.StartInfo.RedirectStandardOutput = true;
|
|
|
|
|
process.StartInfo.UseShellExecute = false;
|
|
|
|
|
process.StartInfo.FileName = "ssh-add";
|
|
|
|
|
process.StartInfo.Arguments = Path.Combine (keys_path, key_file_name);
|
|
|
|
|
process.Start ();
|
|
|
|
|
process.WaitForExit ();
|
|
|
|
|
}
|
2010-11-18 00:29:31 +00:00
|
|
|
|
|
|
|
|
|
|
2011-03-13 00:15:19 +00:00
|
|
|
|
public bool BackendIsPresent {
|
|
|
|
|
get {
|
2011-05-08 23:04:53 +00:00
|
|
|
|
return SparkleBackend.DefaultBackend.IsPresent;
|
2011-03-13 00:15:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Looks up the user's name from the global configuration
|
|
|
|
|
public string UserName
|
|
|
|
|
{
|
|
|
|
|
get {
|
2011-05-09 23:30:20 +00:00
|
|
|
|
string global_config_file_path = Path.Combine (SparklePaths.SparkleConfigPath, "config.xml");
|
2011-05-02 15:46:31 +00:00
|
|
|
|
|
|
|
|
|
if (!File.Exists (global_config_file_path))
|
|
|
|
|
return "";
|
|
|
|
|
|
2011-05-09 23:30:20 +00:00
|
|
|
|
XmlDocument xml = new XmlDocument();
|
|
|
|
|
xml.Load (global_config_file_path);
|
|
|
|
|
|
|
|
|
|
XmlNode node = xml.SelectSingleNode("//user/name/text()");
|
|
|
|
|
return node.Value;
|
2011-05-02 15:46:31 +00:00
|
|
|
|
}
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
set {
|
|
|
|
|
WriteUserInfo (value, UserEmail);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Looks up the user's email from the global configuration
|
|
|
|
|
public string UserEmail
|
|
|
|
|
{
|
|
|
|
|
get {
|
2011-05-09 23:30:20 +00:00
|
|
|
|
string global_config_file_path = Path.Combine (SparklePaths.SparkleConfigPath, "config.xml");
|
|
|
|
|
|
|
|
|
|
if (!File.Exists (global_config_file_path))
|
2011-05-02 15:46:31 +00:00
|
|
|
|
return "";
|
2011-05-09 23:30:20 +00:00
|
|
|
|
|
|
|
|
|
XmlDocument xml = new XmlDocument();
|
|
|
|
|
xml.Load (global_config_file_path);
|
|
|
|
|
|
|
|
|
|
XmlNode node = xml.SelectSingleNode("//user/email/text()");
|
|
|
|
|
return node.Value;
|
2011-05-02 15:46:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set {
|
|
|
|
|
WriteUserInfo (UserName, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void WriteUserInfo (string user_name, string user_email)
|
|
|
|
|
{
|
2011-05-09 23:30:20 +00:00
|
|
|
|
string global_config_file_path = Path.Combine (SparklePaths.SparkleConfigPath, "config.xml");
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Write the user's information to a text file
|
|
|
|
|
TextWriter writer = new StreamWriter (global_config_file_path);
|
2011-05-09 23:30:20 +00:00
|
|
|
|
string n = Environment.NewLine;
|
|
|
|
|
writer.WriteLine ("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + n +
|
|
|
|
|
"<sparkleshare>" + n +
|
|
|
|
|
" <user>" + n +
|
|
|
|
|
" <name>" + user_name + "</name>" + n +
|
|
|
|
|
" <email>" + user_email + "</email>" + n +
|
|
|
|
|
" </user>" + n +
|
|
|
|
|
"</sparkleshare>" + n);
|
2011-05-02 15:46:31 +00:00
|
|
|
|
writer.Close ();
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Config", "Updated '" + global_config_file_path + "'");
|
|
|
|
|
}
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Generates and installs an RSA keypair to identify this system
|
|
|
|
|
public void GenerateKeyPair ()
|
|
|
|
|
{
|
|
|
|
|
string keys_path = SparklePaths.SparkleKeysPath;
|
|
|
|
|
string key_file_name = "sparkleshare." + UserEmail + ".key";
|
|
|
|
|
string key_file_path = Path.Combine (keys_path, key_file_name);
|
|
|
|
|
|
|
|
|
|
if (File.Exists (key_file_path)) {
|
|
|
|
|
SparkleHelpers.DebugInfo ("Config", "Key already exists ('" + key_file_name + "'), " +
|
|
|
|
|
"leaving it untouched");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (!Directory.Exists (keys_path))
|
|
|
|
|
Directory.CreateDirectory (keys_path);
|
|
|
|
|
|
|
|
|
|
if (!File.Exists (key_file_name)) {
|
|
|
|
|
Process process = new Process () {
|
|
|
|
|
EnableRaisingEvents = true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
process.StartInfo.WorkingDirectory = keys_path;
|
|
|
|
|
process.StartInfo.UseShellExecute = false;
|
|
|
|
|
process.StartInfo.RedirectStandardOutput = true;
|
|
|
|
|
process.StartInfo.FileName = "ssh-keygen";
|
|
|
|
|
|
|
|
|
|
// -t is the crypto type
|
|
|
|
|
// -P is the password (none)
|
|
|
|
|
// -f is the file name to store the private key in
|
|
|
|
|
process.StartInfo.Arguments = "-t rsa -P \"\" -f " + key_file_name;
|
|
|
|
|
process.Start ();
|
|
|
|
|
|
|
|
|
|
process.Exited += delegate {
|
|
|
|
|
SparkleHelpers.DebugInfo ("Config", "Created private key '" + key_file_name + "'");
|
|
|
|
|
SparkleHelpers.DebugInfo ("Config", "Created public key '" + key_file_name + ".pub'");
|
|
|
|
|
|
|
|
|
|
// Create an easily accessible copy of the public
|
|
|
|
|
// key in the user's SparkleShare folder
|
|
|
|
|
File.Copy (key_file_path + ".pub",
|
|
|
|
|
Path.Combine (SparklePath, UserName + "'s key.txt"));
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
|
|
|
|
|
private void DisableHostKeyCheckingForHost (string host)
|
|
|
|
|
{
|
|
|
|
|
string ssh_config_file_path = SparkleHelpers.CombineMore (
|
|
|
|
|
SparklePaths.HomePath, ".ssh", "config");
|
|
|
|
|
|
|
|
|
|
string ssh_config = Environment.NewLine + "Host " + host +
|
|
|
|
|
Environment.NewLine + "\tStrictHostKeyChecking no";
|
|
|
|
|
|
|
|
|
|
if (File.Exists (ssh_config_file_path)) {
|
|
|
|
|
TextWriter writer = File.AppendText (ssh_config_file_path);
|
|
|
|
|
writer.WriteLine (ssh_config);
|
|
|
|
|
writer.Close ();
|
|
|
|
|
} else {
|
|
|
|
|
TextWriter writer = new StreamWriter (ssh_config_file_path);
|
|
|
|
|
writer.WriteLine (ssh_config);
|
|
|
|
|
writer.Close ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UnixFileSystemInfo file_info = new UnixFileInfo (ssh_config_file_path);
|
|
|
|
|
file_info.FileAccessPermissions = (FileAccessPermissions.UserRead |
|
|
|
|
|
FileAccessPermissions.UserWrite);
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
private void EnableHostKeyCheckingForHost (string host)
|
|
|
|
|
{
|
|
|
|
|
string ssh_config_file_path = SparkleHelpers.CombineMore (
|
|
|
|
|
SparklePaths.HomePath, ".ssh", "config");
|
|
|
|
|
|
|
|
|
|
string ssh_config = Environment.NewLine + "Host " + host +
|
|
|
|
|
Environment.NewLine + "\tStrictHostKeyChecking no";
|
|
|
|
|
|
|
|
|
|
if (File.Exists (ssh_config_file_path)) {
|
|
|
|
|
StreamReader reader = new StreamReader (ssh_config_file_path);
|
|
|
|
|
string current_ssh_config = reader.ReadToEnd ();
|
|
|
|
|
reader.Close ();
|
|
|
|
|
|
|
|
|
|
current_ssh_config = current_ssh_config.Remove (
|
|
|
|
|
current_ssh_config.IndexOf (ssh_config), ssh_config.Length);
|
|
|
|
|
|
|
|
|
|
bool has_some_ssh_config = new Regex (@"[a-z]").IsMatch (current_ssh_config);
|
|
|
|
|
if (!has_some_ssh_config) {
|
|
|
|
|
File.Delete (ssh_config_file_path);
|
|
|
|
|
} else {
|
|
|
|
|
TextWriter writer = new StreamWriter (ssh_config_file_path);
|
|
|
|
|
writer.WriteLine (current_ssh_config);
|
|
|
|
|
writer.Close ();
|
|
|
|
|
|
|
|
|
|
UnixFileSystemInfo file_info = new UnixFileInfo (ssh_config_file_path);
|
|
|
|
|
file_info.FileAccessPermissions = (FileAccessPermissions.UserRead |
|
|
|
|
|
FileAccessPermissions.UserWrite);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-06 01:10:15 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
|
|
|
|
|
// Gets the avatar for a specific email address and size
|
|
|
|
|
public string GetAvatar (string email, int size)
|
|
|
|
|
{
|
|
|
|
|
string avatar_path = SparkleHelpers.CombineMore (SparklePaths.SparkleLocalIconPath,
|
|
|
|
|
size + "x" + size, "status");
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (!Directory.Exists (avatar_path)) {
|
|
|
|
|
Directory.CreateDirectory (avatar_path);
|
|
|
|
|
SparkleHelpers.DebugInfo ("Config", "Created '" + avatar_path + "'");
|
|
|
|
|
}
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
string avatar_file_path = SparkleHelpers.CombineMore (avatar_path, "avatar-" + email);
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (File.Exists (avatar_file_path)) {
|
|
|
|
|
return avatar_file_path;
|
|
|
|
|
} else {
|
|
|
|
|
// Let's try to get the person's gravatar for next time
|
|
|
|
|
WebClient web_client = new WebClient ();
|
|
|
|
|
Uri uri = new Uri ("http://www.gravatar.com/avatar/" + GetMD5 (email) +
|
|
|
|
|
".jpg?s=" + size + "&d=404");
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
string tmp_file_path = SparkleHelpers.CombineMore (SparklePaths.SparkleTmpPath, email + size);
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (!File.Exists (tmp_file_path)) {
|
|
|
|
|
web_client.DownloadFileAsync (uri, tmp_file_path);
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
web_client.DownloadFileCompleted += delegate {
|
|
|
|
|
if (File.Exists (avatar_file_path))
|
|
|
|
|
File.Delete (avatar_file_path);
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
FileInfo tmp_file_info = new FileInfo (tmp_file_path);
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (tmp_file_info.Length > 255)
|
|
|
|
|
File.Move (tmp_file_path, avatar_file_path);
|
|
|
|
|
|
|
|
|
|
if (AvatarFetched != null)
|
|
|
|
|
AvatarFetched ();
|
|
|
|
|
};
|
|
|
|
|
}
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Fall back to a generic icon if there is no gravatar
|
|
|
|
|
if (File.Exists (avatar_file_path))
|
|
|
|
|
return avatar_file_path;
|
|
|
|
|
else
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-02-06 01:10:15 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public void FetchFolder (string url, string name)
|
|
|
|
|
{
|
|
|
|
|
SparkleHelpers.DebugInfo ("Controller", "Formed URL: " + url);
|
|
|
|
|
|
|
|
|
|
// TODO: This is all too git specific,
|
|
|
|
|
// The controller should be ignorant of git
|
|
|
|
|
string host = url.Substring (url.IndexOf ("@") + 1);
|
|
|
|
|
if (host.Contains (":"))
|
|
|
|
|
host = host.Substring (0, host.IndexOf (":"));
|
|
|
|
|
else
|
|
|
|
|
host = host.Substring (0, host.IndexOf ("/"));
|
|
|
|
|
|
|
|
|
|
DisableHostKeyCheckingForHost (host);
|
|
|
|
|
|
|
|
|
|
// Strip the '.git' from the name
|
|
|
|
|
string canonical_name = Path.GetFileNameWithoutExtension (name);
|
|
|
|
|
string tmp_folder = Path.Combine (SparklePaths.SparkleTmpPath, canonical_name);
|
|
|
|
|
SparkleFetcher fetcher = new SparkleFetcher (url, tmp_folder);
|
|
|
|
|
bool folder_exists = Directory.Exists (Path.Combine (SparklePaths.SparklePath, canonical_name));
|
|
|
|
|
|
|
|
|
|
// Add a numbered suffix to the nameif a folder with the same name
|
|
|
|
|
// already exists. Example: "Folder (2)"
|
|
|
|
|
int i = 1;
|
|
|
|
|
while (folder_exists) {
|
|
|
|
|
i++;
|
|
|
|
|
folder_exists = Directory.Exists (
|
|
|
|
|
SparkleHelpers.CombineMore (SparklePaths.SparklePath, canonical_name + " (" + i + ")"));
|
|
|
|
|
}
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
string target_folder_name = canonical_name;
|
|
|
|
|
if (i > 1)
|
|
|
|
|
target_folder_name += " (" + i + ")";
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-03 17:30:39 +00:00
|
|
|
|
fetcher.Finished += delegate {
|
2011-05-02 15:46:31 +00:00
|
|
|
|
EnableHostKeyCheckingForHost (host);
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Needed to do the moving
|
|
|
|
|
SparkleHelpers.ClearAttributes (tmp_folder);
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
try {
|
|
|
|
|
string target_folder_path = SparkleHelpers.CombineMore (SparklePaths.SparklePath,
|
|
|
|
|
target_folder_name);
|
2011-02-11 01:31:10 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
Directory.Move (tmp_folder, target_folder_path);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
SparkleHelpers.DebugInfo ("Controller", "Error moving folder: " + e.Message);
|
|
|
|
|
}
|
2010-11-19 01:07:59 +00:00
|
|
|
|
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (FolderFetched != null)
|
|
|
|
|
FolderFetched ();
|
2011-05-03 17:30:39 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (FolderListChanged != null)
|
|
|
|
|
FolderListChanged ();
|
2011-05-08 19:35:26 +00:00
|
|
|
|
|
|
|
|
|
fetcher.Dispose ();
|
2011-05-02 15:46:31 +00:00
|
|
|
|
};
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2011-02-06 01:10:15 +00:00
|
|
|
|
|
2011-05-03 17:30:39 +00:00
|
|
|
|
fetcher.Failed += delegate {
|
2011-05-02 15:46:31 +00:00
|
|
|
|
EnableHostKeyCheckingForHost (host);
|
2011-02-06 01:10:15 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (Directory.Exists (tmp_folder)) {
|
|
|
|
|
SparkleHelpers.ClearAttributes (tmp_folder);
|
|
|
|
|
Directory.Delete (tmp_folder, true);
|
2011-02-06 01:10:15 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
SparkleHelpers.DebugInfo ("Config",
|
|
|
|
|
"Deleted temporary directory: " + tmp_folder);
|
|
|
|
|
}
|
2011-02-08 23:46:42 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
if (FolderFetchError != null)
|
|
|
|
|
FolderFetchError ();
|
2011-05-08 19:35:26 +00:00
|
|
|
|
|
|
|
|
|
fetcher.Dispose ();
|
2011-05-02 15:46:31 +00:00
|
|
|
|
};
|
2011-05-03 17:30:39 +00:00
|
|
|
|
|
2011-02-06 01:10:15 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
fetcher.Start ();
|
|
|
|
|
}
|
2011-02-06 01:10:15 +00:00
|
|
|
|
|
2011-05-03 17:30:39 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Creates an MD5 hash of input
|
2011-05-08 21:22:27 +00:00
|
|
|
|
private string GetMD5 (string s)
|
2011-05-02 15:46:31 +00:00
|
|
|
|
{
|
|
|
|
|
MD5 md5 = new MD5CryptoServiceProvider ();
|
|
|
|
|
Byte[] bytes = ASCIIEncoding.Default.GetBytes (s);
|
|
|
|
|
Byte[] encoded_bytes = md5.ComputeHash (bytes);
|
|
|
|
|
return BitConverter.ToString (encoded_bytes).ToLower ().Replace ("-", "");
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-06 01:10:15 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Checks whether there are any folders syncing and
|
|
|
|
|
// quits if safe
|
|
|
|
|
public void TryQuit ()
|
|
|
|
|
{
|
|
|
|
|
foreach (SparkleRepo repo in Repositories) {
|
|
|
|
|
if (repo.IsSyncing) {
|
|
|
|
|
if (OnQuitWhileSyncing != null)
|
|
|
|
|
OnQuitWhileSyncing ();
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Quit ();
|
|
|
|
|
}
|
2011-02-06 01:10:15 +00:00
|
|
|
|
|
2011-03-20 15:10:42 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public void Quit ()
|
|
|
|
|
{
|
|
|
|
|
foreach (SparkleRepo repo in Repositories)
|
|
|
|
|
repo.Dispose ();
|
2011-02-08 23:46:42 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
Environment.Exit (0);
|
|
|
|
|
}
|
2011-02-08 23:46:42 +00:00
|
|
|
|
|
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
// Checks to see if an email address is valid
|
|
|
|
|
public bool IsValidEmail (string email)
|
|
|
|
|
{
|
|
|
|
|
Regex regex = new Regex (@"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", RegexOptions.IgnoreCase);
|
|
|
|
|
return regex.IsMatch (email);
|
|
|
|
|
}
|
2011-03-24 10:22:10 +00:00
|
|
|
|
|
|
|
|
|
|
2011-04-17 23:49:50 +00:00
|
|
|
|
public void CheckForNewVersion ()
|
2011-03-24 10:22:10 +00:00
|
|
|
|
{
|
|
|
|
|
string new_version_file_path = System.IO.Path.Combine (SparklePaths.SparkleTmpPath,
|
|
|
|
|
"version");
|
|
|
|
|
|
|
|
|
|
if (File.Exists (new_version_file_path))
|
|
|
|
|
File.Delete (new_version_file_path);
|
|
|
|
|
|
|
|
|
|
WebClient web_client = new WebClient ();
|
|
|
|
|
Uri uri = new Uri ("http://www.sparkleshare.org/version");
|
|
|
|
|
|
|
|
|
|
web_client.DownloadFileCompleted += delegate {
|
|
|
|
|
|
|
|
|
|
if (new FileInfo (new_version_file_path).Length > 0) {
|
|
|
|
|
StreamReader reader = new StreamReader (new_version_file_path);
|
|
|
|
|
string downloaded_version_number = reader.ReadToEnd ().Trim ();
|
|
|
|
|
|
2011-04-17 23:49:50 +00:00
|
|
|
|
if (Defines.VERSION.Equals (downloaded_version_number)) {
|
2011-03-24 10:22:10 +00:00
|
|
|
|
if (VersionUpToDate != null)
|
|
|
|
|
VersionUpToDate ();
|
2011-04-17 23:49:50 +00:00
|
|
|
|
} else {
|
|
|
|
|
if (NewVersionAvailable != null)
|
|
|
|
|
NewVersionAvailable (downloaded_version_number);
|
2011-03-24 10:22:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
web_client.DownloadFileAsync (uri, new_version_file_path);
|
|
|
|
|
}
|
2011-05-02 15:46:31 +00:00
|
|
|
|
}
|
2010-10-16 22:48:50 +00:00
|
|
|
|
|
2010-11-27 17:44:13 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public class ChangeSet : SparkleCommit { }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// All commits that happened on a day
|
|
|
|
|
public class ActivityDay : List <SparkleCommit>
|
|
|
|
|
{
|
|
|
|
|
public DateTime DateTime;
|
2011-02-13 00:25:53 +00:00
|
|
|
|
|
2011-05-02 15:46:31 +00:00
|
|
|
|
public ActivityDay (DateTime date_time)
|
|
|
|
|
{
|
|
|
|
|
DateTime = date_time;
|
|
|
|
|
DateTime = new DateTime (DateTime.Year, DateTime.Month, DateTime.Day);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-13 21:03:40 +00:00
|
|
|
|
}
|