Allow renaming of folders. Closes #466

This commit is contained in:
Hylke Bons 2012-07-06 11:26:02 +02:00
parent c769f6c7a8
commit ec2f5f36a2
4 changed files with 234 additions and 291 deletions

View file

@ -771,49 +771,6 @@ namespace SparkleLib.Git {
return change_sets;
}
private string EnsureSpecialCharacters (string file_path)
{
if (file_path.StartsWith("\""))
{
System.Diagnostics.Debug.Assert(file_path.Length > 2 && file_path.EndsWith("\""), "unexpected path");
file_path = ResolveSpecialChars(file_path.Substring(1, file_path.Length - 2));
}
return file_path;
}
/// <summary>
/// Resolves special characters like \303\244 (ä) to their real character
/// </summary>
/// <param name="file_path"></param>
/// <returns></returns>
private string ResolveSpecialChars (string file_path)
{
var builder = new System.Text.StringBuilder(file_path.Length);
var codes = new List<byte>();
for (int i = 0; i < file_path.Length; i++)
{
while (file_path[i] == '\\'
&& file_path.Length - i > 3
&& char.IsNumber(file_path[i + 1])
&& char.IsNumber(file_path[i + 2])
&& char.IsNumber(file_path[i + 3]))
{
codes.Add (Convert.ToByte(file_path.Substring(i + 1, 3), 8));
i += 4;
}
if (codes.Count > 0)
{
builder.Append(System.Text.Encoding.UTF8.GetString (codes.ToArray ()));
codes.Clear ();
}
builder.Append(file_path[i]);
}
return builder.ToString();
//System.Text.Encoding.UTF8.
}
private string EnsureSpecialCharacters (string path)
{
@ -825,11 +782,6 @@ namespace SparkleLib.Git {
}
/// <summary>
/// Resolves special characters like \303\244 (ä) to their real character
/// </summary>
/// <param name="file_path"></param>
/// <returns></returns>
private string ResolveSpecialChars (string s)
{
StringBuilder builder = new StringBuilder (s.Length);

View file

@ -248,6 +248,15 @@ namespace SparkleLib {
}
public void RenameFolder (string identifier, string name)
{
XmlNode node_folder = SelectSingleNode (string.Format ("/sparkleshare/folder[identifier=\"{0}\"]", identifier));
node_folder ["name"].InnerText = name;
Save ();
}
public string GetBackendForFolder (string name)
{
return GetFolderValue (name, "backend");
@ -266,6 +275,17 @@ namespace SparkleLib {
}
public bool IdentifierExists (string identifier)
{
foreach (XmlNode node_folder in SelectNodes ("/sparkleshare/folder")) {
if (node_folder ["identifier"].InnerText.Equals (identifier))
return true;
}
return false;
}
public bool SetFolderOptionalAttribute (string folder_name, string key, string value)
{
XmlNode folder = GetFolder (folder_name);

View file

@ -50,7 +50,7 @@ namespace SparkleLib {
public readonly bool FetchPriorHistory = false;
public string TargetFolder { get; protected set; }
public bool IsActive { get; private set; }
public string Identifier = CreateIdentifier ();
public string Identifier;
public string [] Warnings {
get {
@ -66,13 +66,34 @@ namespace SparkleLib {
protected List<string> warnings = new List<string> ();
protected List<string> errors = new List<string> ();
protected List<string> errors = new List<string> ();
protected string [] ExcludeRules = new string [] {
"*.autosave", // Various autosaving apps
"*~", // gedit and emacs
".~lock.*", // LibreOffice
"*.part", "*.crdownload", // Firefox and Chromium temporary download files
".*.sw[a-z]", "*.un~", "*.swp", "*.swo", // vi(m)
".directory", // KDE
".DS_Store", "Icon\r\r", "._*", ".Spotlight-V100", ".Trashes", // Mac OS X
"*(Autosaved).graffle", // Omnigraffle
"Thumbs.db", "Desktop.ini", // Windows
"~*.tmp", "~*.TMP", "*~*.tmp", "*~*.TMP", // MS Office
"~*.ppt", "~*.PPT", "~*.pptx", "~*.PPTX",
"~*.xls", "~*.XLS", "~*.xlsx", "~*.XLSX",
"~*.doc", "~*.DOC", "~*.docx", "~*.DOCX",
"*/CVS/*", ".cvsignore", "*/.cvsignore", // CVS
"/.svn/*", "*/.svn/*", // Subversion
"/.hg/*", "*/.hg/*", "*/.hgignore", // Mercurial
"/.bzr/*", "*/.bzr/*", "*/.bzrignore" // Bazaar
};
private Thread thread;
public SparkleFetcherBase (string server, string required_fingerprint, string remote_path,
string target_folder, bool fetch_prior_history)
public SparkleFetcherBase (string server, string required_fingerprint,
string remote_path, string target_folder, bool fetch_prior_history)
{
RequiredFingerprint = required_fingerprint;
FetchPriorHistory = fetch_prior_history;
@ -105,7 +126,6 @@ namespace SparkleLib {
if (Directory.Exists (TargetFolder))
Directory.Delete (TargetFolder, true);
string host = RemoteUrl.Host;
string host_key = GetHostKey ();
@ -116,7 +136,6 @@ namespace SparkleLib {
return;
}
bool warn = true;
if (RequiredFingerprint != null) {
string host_fingerprint = GetFingerprint (host_key);
@ -141,30 +160,31 @@ namespace SparkleLib {
AcceptHostKey (host_key, warn);
this.thread = new Thread (
new ThreadStart (delegate {
if (Fetch ()) {
Thread.Sleep (500);
SparkleHelpers.DebugInfo ("Fetcher", "Finished");
this.thread = new Thread (new ThreadStart (delegate {
if (Fetch ()) {
Thread.Sleep (500);
SparkleHelpers.DebugInfo ("Fetcher", "Finished");
IsActive = false;
IsActive = false;
// TODO: Find better way to determine if folder should have crypto setup
bool repo_is_encrypted = RemoteUrl.ToString ().Contains ("crypto");
// TODO: Find better way to determine if folder should have crypto setup
bool repo_is_encrypted = RemoteUrl.ToString ().Contains ("crypto");
if (Finished != null)
Finished (repo_is_encrypted, IsFetchedRepoEmpty, Warnings);
if (Finished != null)
Finished (repo_is_encrypted, IsFetchedRepoEmpty, Warnings);
} else {
Thread.Sleep (500);
SparkleHelpers.DebugInfo ("Fetcher", "Failed");
} else {
Thread.Sleep (500);
SparkleHelpers.DebugInfo ("Fetcher", "Failed");
IsActive = false;
IsActive = false;
if (Failed != null)
Failed ();
}
}));
if (Failed != null)
Failed ();
}
})
);
this.thread.Start ();
}
@ -174,8 +194,13 @@ namespace SparkleLib {
{
string identifier_path = Path.Combine (TargetFolder, ".sparkleshare");
if (!File.Exists (identifier_path))
if (File.Exists (identifier_path)) {
Identifier = File.ReadAllText (identifier_path).Trim ();
} else {
Identifier = CreateIdentifier ();
File.WriteAllText (identifier_path, Identifier);
}
if (IsFetchedRepoEmpty)
CreateInitialChangeSet ();
@ -204,6 +229,32 @@ namespace SparkleLib {
}
public static string CreateIdentifier ()
{
Random random = new Random ();
string number = "" + random.Next () + "" + random.Next () + "" + random.Next ();
return SparkleHelpers.SHA1 (number);
}
public static string GetBackend (string path)
{
string extension = Path.GetExtension (path);
if (!string.IsNullOrEmpty (extension)) {
extension = extension.Substring (1);
char [] letters = extension.ToCharArray ();
letters [0] = char.ToUpper (letters [0]);
return new string (letters);
} else {
return "Git";
}
}
public void Dispose ()
{
if (this.thread != null) {
@ -213,15 +264,6 @@ namespace SparkleLib {
}
public static string CreateIdentifier ()
{
Random random = new Random ();
string number = "" + random.Next () + "" + random.Next () + "" + random.Next ();
return SparkleHelpers.SHA1 (number);
}
protected void OnProgressChanged (double percentage) {
if (ProgressChanged != null)
ProgressChanged (percentage);
@ -242,7 +284,7 @@ namespace SparkleLib {
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.FileName = "ssh-keyscan";
process.StartInfo.FileName = "ssh-keyscan";
process.StartInfo.Arguments = "-t rsa " + host;
process.Start ();
@ -283,7 +325,7 @@ namespace SparkleLib {
// WaitForExit, or it will hang forever on output > 4096 bytes
string fingerprint = process.StandardOutput.ReadToEnd ().Trim ();
process.WaitForExit ();
File.Delete (tmp_file_path);
try {
@ -329,98 +371,5 @@ namespace SparkleLib {
if (warn)
this.warnings.Add ("The following host key has been accepted:\n" + GetFingerprint (host_key));
}
public static string GetBackend (string path)
{
string extension = Path.GetExtension (path);
if (!string.IsNullOrEmpty (extension)) {
extension = extension.Substring (1);
char [] letters = extension.ToCharArray ();
letters [0] = char.ToUpper (letters [0]);
return new string (letters);
} else {
return "Git";
}
}
protected string [] ExcludeRules = new string [] {
// Various autosaving apps
"*.autosave",
// gedit and emacs
"*~",
// LibreOffice
".~lock.*",
// Firefox and Chromium temporary download files
"*.part",
"*.crdownload",
// vi(m)
".*.sw[a-z]",
"*.un~",
"*.swp",
"*.swo",
// KDE
".directory",
// Mac OS X
".DS_Store",
"Icon\r\r",
"._*",
".Spotlight-V100",
".Trashes",
// Omnigraffle
"*(Autosaved).graffle",
// Windows
"Thumbs.db",
"Desktop.ini",
// MS Office
"~*.tmp",
"~*.TMP",
"*~*.tmp",
"*~*.TMP",
"~*.ppt",
"~*.PPT",
"~*.pptx",
"~*.PPTX",
"~*.xls",
"~*.XLS",
"~*.xlsx",
"~*.XLSX",
"~*.doc",
"~*.DOC",
"~*.docx",
"~*.DOCX",
// CVS
"*/CVS/*",
".cvsignore",
"*/.cvsignore",
// Subversion
"/.svn/*",
"*/.svn/*",
// Mercurial
"/.hg/*",
"*/.hg/*",
"*/.hgignore",
// Bazaar
"/.bzr/*",
"*/.bzr/*",
"*/.bzrignore"
};
}
}

View file

@ -34,12 +34,8 @@ namespace SparkleShare {
public SparkleRepoBase [] Repositories {
get {
lock (this.repo_lock) {
SparkleRepoBase [] repositories =
this.repositories.GetRange (0, this.repositories.Count).ToArray ();
return repositories;
}
lock (this.repo_lock)
return this.repositories.GetRange (0, this.repositories.Count).ToArray ();
}
}
@ -170,8 +166,8 @@ namespace SparkleShare {
private SparkleFetcherBase fetcher;
private Object repo_lock = new Object ();
private Object delete_watcher_lock = new Object ();
private Object repo_lock = new Object ();
private Object check_repos_lock = new Object ();
// Short alias for the translations
@ -186,6 +182,110 @@ namespace SparkleShare {
}
public void HandleInvite (FileSystemEventArgs args)
{
if (this.fetcher != null &&
this.fetcher.IsActive) {
if (AlertNotificationRaised != null)
AlertNotificationRaised ("SparkleShare Setup seems busy",
"Please wait for it to finish");
} else {
if (InviteReceived != null) {
SparkleInvite invite = new SparkleInvite (args.FullPath);
// It may be that the invite we received a path to isn't
// fully downloaded yet, so we try to read it several times
int tries = 0;
while (!invite.IsValid) {
Thread.Sleep (1 * 250);
invite = new SparkleInvite (args.FullPath);
tries++;
if (tries > 20)
break;
}
if (invite.IsValid) {
InviteReceived (invite);
} else {
if (AlertNotificationRaised != null)
AlertNotificationRaised ("Oh noes!",
"This invite seems screwed up...");
}
File.Delete (args.FullPath);
}
}
}
public void CheckRepositories ()
{
lock (this.check_repos_lock) {
string path = SparkleConfig.DefaultConfig.FoldersPath;
foreach (string folder_path in Directory.GetDirectories (path)) {
string folder_name = Path.GetFileName (folder_path);
if (folder_name.Equals (".tmp"))
continue;
if (SparkleConfig.DefaultConfig.GetIdentifierForFolder (folder_name) == null) {
string identifier_file_path = Path.Combine (folder_path, ".sparkleshare");
if (!File.Exists (identifier_file_path))
continue;
string identifier = File.ReadAllText (identifier_file_path).Trim ();
if (SparkleConfig.DefaultConfig.IdentifierExists (identifier)) {
RemoveRepository (folder_path);
SparkleConfig.DefaultConfig.RenameFolder (identifier, folder_name);
string new_folder_path = Path.Combine (path, folder_name);
AddRepository (new_folder_path);
SparkleHelpers.DebugInfo ("Controller",
"Renamed folder with identifier " + identifier + " to '" + folder_name + "'");
}
}
}
foreach (string folder_name in SparkleConfig.DefaultConfig.Folders) {
string folder_path = new SparkleFolder (folder_name).FullPath;
if (!Directory.Exists (folder_path)) {
SparkleConfig.DefaultConfig.RemoveFolder (folder_name);
RemoveRepository (folder_path);
SparkleHelpers.DebugInfo ("Controller",
"Removed folder '" + folder_name + "' from config");
}
}
if (FolderListChanged != null)
FolderListChanged ();
}
}
public void OnFolderActivity (object o, FileSystemEventArgs args)
{
if (args != null &&
args.ChangeType == WatcherChangeTypes.Created &&
args.FullPath.EndsWith (".xml")) {
HandleInvite (args);
return;
} else {
CheckRepositories ();
}
}
public virtual void Initialize ()
{
SparklePlugin.PluginsPath = PluginsPath;
@ -201,69 +301,17 @@ namespace SparkleShare {
ImportPrivateKey ();
// Watch the SparkleShare folder
FileSystemWatcher watcher = new FileSystemWatcher (SparkleConfig.DefaultConfig.FoldersPath) {
FileSystemWatcher watcher = new FileSystemWatcher () {
Filter = "*",
IncludeSubdirectories = false,
EnableRaisingEvents = true,
Filter = "*"
Path = SparkleConfig.DefaultConfig.FoldersPath
};
watcher.Deleted += delegate (object o, FileSystemEventArgs args) {
lock (this.delete_watcher_lock) {
foreach (string folder_name in SparkleConfig.DefaultConfig.Folders) {
string folder_path = new SparkleFolder (folder_name).FullPath;
watcher.Deleted += OnFolderActivity;
watcher.Created += OnFolderActivity;
watcher.Renamed += OnFolderActivity;
if (!Directory.Exists (folder_path)) {
SparkleConfig.DefaultConfig.RemoveFolder (folder_name);
RemoveRepository (folder_path);
}
}
if (FolderListChanged != null)
FolderListChanged ();
}
};
watcher.Created += delegate (object o, FileSystemEventArgs args) {
if (!args.FullPath.EndsWith (".xml"))
return;
if (this.fetcher != null &&
this.fetcher.IsActive) {
if (AlertNotificationRaised != null)
AlertNotificationRaised ("SparkleShare Setup seems busy",
"Please wait for it to finish");
} else {
if (InviteReceived != null) {
SparkleInvite invite = new SparkleInvite (args.FullPath);
// It may be that the invite we received a path to isn't
// fully downloaded yet, so we try to read it several times
int tries = 0;
while (!invite.IsValid) {
Thread.Sleep (1 * 250);
invite = new SparkleInvite (args.FullPath);
tries++;
if (tries > 20)
break;
}
if (invite.IsValid) {
InviteReceived (invite);
} else {
invite = null;
if (AlertNotificationRaised != null)
AlertNotificationRaised ("Oh noes!",
"This invite seems screwed up...");
}
File.Delete (args.FullPath);
}
}
};
watcher.EnableRaisingEvents = true;
}
@ -327,11 +375,9 @@ namespace SparkleShare {
string path = new SparkleFolder (name).FullPath;
lock (this.repo_lock) {
foreach (SparkleRepoBase repo in Repositories) {
if (repo.LocalPath.Equals (path))
return repo.ChangeSets;
}
foreach (SparkleRepoBase repo in Repositories) {
if (repo.LocalPath.Equals (path))
return repo.ChangeSets;
}
return null;
@ -511,7 +557,6 @@ namespace SparkleShare {
}
// Adds a repository to the list of repositories
private void AddRepository (string folder_path)
{
SparkleRepoBase repo = null;
@ -531,16 +576,8 @@ namespace SparkleShare {
return;
}
repo.NewChangeSet += delegate (SparkleChangeSet change_set) {
if (NotificationRaised != null)
NotificationRaised (change_set);
};
repo.ConflictResolved += delegate {
if (AlertNotificationRaised != null)
AlertNotificationRaised ("Conflict detected",
"Don't worry, SparkleShare made a copy of each conflicting file.");
repo.ChangesDetected += delegate {
UpdateState ();
};
repo.SyncStatusChanged += delegate (SyncStatus status) {
@ -565,8 +602,15 @@ namespace SparkleShare {
UpdateState ();
};
repo.ChangesDetected += delegate {
UpdateState ();
repo.NewChangeSet += delegate (SparkleChangeSet change_set) {
if (NotificationRaised != null)
NotificationRaised (change_set);
};
repo.ConflictResolved += delegate {
if (AlertNotificationRaised != null)
AlertNotificationRaised ("Conflict detected",
"Don't worry, SparkleShare made a copy of each conflicting file.");
};
this.repositories.Add (repo);
@ -574,45 +618,25 @@ namespace SparkleShare {
}
// 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);
for (int i = 0; i < this.repositories.Count; i++) {
SparkleRepoBase repo = this.repositories [i];
for (int i = 0; i < Repositories.Length; i++) {
SparkleRepoBase repo = Repositories [i];
if (repo.Name.Equals (folder_name)) {
if (repo.LocalPath.Equals (folder_path)) {
repo.Dispose ();
lock (this.repo_lock) {
this.repositories.Remove (repo);
}
this.repositories.Remove (repo);
repo = null;
break;
return;
}
}
}
// Updates the list of repositories with all the
// folders in the SparkleShare folder
private void PopulateRepositories ()
{
lock (this.repo_lock) {
foreach (string folder_name in SparkleConfig.DefaultConfig.Folders) {
string folder_path = new SparkleFolder (folder_name).FullPath;
if (Directory.Exists (folder_path))
AddRepository (folder_path);
else
SparkleConfig.DefaultConfig.RemoveFolder (folder_name);
}
}
CheckRepositories ();
RepositoriesLoaded = true;
if (FolderListChanged != null)
@ -922,9 +946,7 @@ namespace SparkleShare {
target_folder_name, "announcements_url", announcements_url);
*/
lock (this.repo_lock) {
AddRepository (target_folder_path);
}
AddRepository (target_folder_path);
if (FolderListChanged != null)
FolderListChanged ();