Merge branch 'notes'

This commit is contained in:
Hylke 2011-08-25 15:37:01 +01:00
commit 19f428fd8f
16 changed files with 476 additions and 132 deletions

View file

@ -102,7 +102,10 @@ namespace SparkleLib {
// Ignore permission changes // Ignore permission changes
config = config.Replace ("filemode = true", "filemode = false"); config = config.Replace ("filemode = true", "filemode = false");
config = config.Replace ("fetch = +refs/heads/*:refs/remotes/origin/*",
"fetch = +refs/heads/*:refs/remotes/origin/*" + Environment.NewLine +
"\tfetch = +refs/notes/*:refs/notes/*");
Console.WriteLine (">>>>>>>>>>>>>>>" + config);
// Add user info // Add user info
string n = Environment.NewLine; string n = Environment.NewLine;
XmlDocument xml = new XmlDocument(); XmlDocument xml = new XmlDocument();

View file

@ -19,7 +19,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml;
namespace SparkleLib { namespace SparkleLib {
@ -89,6 +92,7 @@ namespace SparkleLib {
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Remote changes found. (" + remote_revision + ")"); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Remote changes found. (" + remote_revision + ")");
return true; return true;
} else { } else {
// FetchNotes ();TODO
return false; return false;
} }
} }
@ -115,8 +119,7 @@ namespace SparkleLib {
public override bool SyncDown () public override bool SyncDown ()
{ {
SparkleGit git = new SparkleGit (LocalPath, "fetch -v origin master"); SparkleGit git = new SparkleGit (LocalPath, "fetch -v");
git.Start (); git.Start ();
git.WaitForExit (); git.WaitForExit ();
@ -347,7 +350,7 @@ namespace SparkleLib {
List <SparkleChangeSet> change_sets = new List <SparkleChangeSet> (); List <SparkleChangeSet> change_sets = new List <SparkleChangeSet> ();
SparkleGit git_log = new SparkleGit (LocalPath, "log -" + count + " --raw -M --date=iso"); SparkleGit git_log = new SparkleGit (LocalPath, "log -" + count + " --raw -M --date=iso --show-notes=*");
Console.OutputEncoding = System.Text.Encoding.Unicode; Console.OutputEncoding = System.Text.Encoding.Unicode;
git_log.Start (); git_log.Start ();
@ -405,11 +408,12 @@ namespace SparkleLib {
if (match.Success) { if (match.Success) {
SparkleChangeSet change_set = new SparkleChangeSet (); SparkleChangeSet change_set = new SparkleChangeSet ();
change_set.Folder = Name; change_set.Folder = Name;
change_set.Revision = match.Groups [1].Value; change_set.Revision = match.Groups [1].Value;
change_set.UserName = match.Groups [2].Value; change_set.UserName = match.Groups [2].Value;
change_set.UserEmail = match.Groups [3].Value; change_set.UserEmail = match.Groups [3].Value;
change_set.IsMerge = is_merge_commit; change_set.IsMerge = is_merge_commit;
change_set.SupportsNotes = true;
change_set.Timestamp = new DateTime (int.Parse (match.Groups [4].Value), change_set.Timestamp = new DateTime (int.Parse (match.Groups [4].Value),
int.Parse (match.Groups [5].Value), int.Parse (match.Groups [6].Value), int.Parse (match.Groups [5].Value), int.Parse (match.Groups [6].Value),
@ -449,6 +453,26 @@ namespace SparkleLib {
change_set.MovedFrom.Add (file_path); change_set.MovedFrom.Add (file_path);
change_set.MovedTo.Add (to_file_path); change_set.MovedTo.Add (to_file_path);
} }
} else if (entry_line.StartsWith (" <note>")) {
Regex regex_notes = new Regex (@"<name>(.+)</name>.*" +
"<email>(.+)</email>.*" +
"<timestamp>([0-9]+)</timestamp>.*" +
"<body>(.+)</body>", RegexOptions.Compiled);
Match match_notes = regex_notes.Match (entry_line);
if (match_notes.Success) {
SparkleNote note = new SparkleNote () {
UserName = match_notes.Groups [1].Value,
UserEmail = match_notes.Groups [2].Value,
Timestamp = new DateTime (1970, 1, 1).AddSeconds (int.Parse (match_notes.Groups [3].Value)),
Body = match_notes.Groups [4].Value
};
change_set.Notes.Add (note);
}
} }
} }
@ -527,6 +551,55 @@ namespace SparkleLib {
return message.TrimEnd (); return message.TrimEnd ();
} }
public override void AddNote (string revision, string note)
{
int timestamp = (int) (DateTime.UtcNow - new DateTime (1970, 1, 1)).TotalSeconds;
// Create the note in one line for easier merging
note = "<note>" +
" <user>" +
" <name>" + SparkleConfig.DefaultConfig.UserName + "</name>" +
" <email>" + SparkleConfig.DefaultConfig.UserEmail + "</email>" +
" </user>" +
" <timestamp>" + timestamp + "</timestamp>" +
" <body>" + note + "</body>" +
"</note>";
string note_namespace = SHA1 (timestamp.ToString () + note);
SparkleGit git_notes = new SparkleGit (LocalPath,
"notes --ref=" + note_namespace + " append -m \"" + note + "\" " + revision);
git_notes.Start ();
git_notes.WaitForExit ();
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Added note to " + revision);
SyncUpNotes ();
}
public override void SyncUpNotes ()
{
while (Status != SyncStatus.Idle) {
System.Threading.Thread.Sleep (5 * 20);
}
SparkleGit git_push = new SparkleGit (LocalPath, "push origin refs/notes/*");
git_push.Start ();
git_push.WaitForExit ();
if (git_push.ExitCode == 0) {
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Notes pushed");
} else {
HasUnsyncedChanges = true;
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Pushing notes failed, trying again later");
}
SparkleAnnouncement announcement = new SparkleAnnouncement (Identifier, SHA1 (DateTime.Now.ToString ()));
base.listener.Announce (announcement);
}
public override bool UsesNotificationCenter public override bool UsesNotificationCenter
{ {
get { get {
@ -534,5 +607,15 @@ namespace SparkleLib {
return !File.Exists (file_path); return !File.Exists (file_path);
} }
} }
// Creates a SHA-1 hash of input
private string SHA1 (string s)
{
SHA1 sha1 = new SHA1CryptoServiceProvider ();
Byte[] bytes = ASCIIEncoding.Default.GetBytes (s);
Byte[] encoded_bytes = sha1.ComputeHash (bytes);
return BitConverter.ToString (encoded_bytes).ToLower ().Replace ("-", "");
}
} }
} }

View file

@ -24,15 +24,29 @@ namespace SparkleLib {
public string UserName; public string UserName;
public string UserEmail; public string UserEmail;
public string Folder; public string Folder;
public string Revision; public string Revision;
public DateTime Timestamp; public DateTime Timestamp;
public bool IsMerge = false; public bool SupportsNotes = false;
public bool IsMerge = false;
public List<string> Added = new List<string> (); public List<string> Added = new List<string> ();
public List<string> Deleted = new List<string> (); public List<string> Deleted = new List<string> ();
public List<string> Edited = new List<string> (); public List<string> Edited = new List<string> ();
public List<string> MovedFrom = new List<string> (); public List<string> MovedFrom = new List<string> ();
public List<string> MovedTo = new List<string> (); public List<string> MovedTo = new List<string> ();
public List<SparkleNote> Notes = new List<SparkleNote> ();
}
public class SparkleNote {
public string UserName;
public string UserEmail;
public DateTime Timestamp;
public string Body;
} }
} }

View file

@ -38,7 +38,6 @@ namespace SparkleLib {
private TimeSpan long_interval = new TimeSpan (0, 0, 10, 0); private TimeSpan long_interval = new TimeSpan (0, 0, 10, 0);
private SparkleWatcher watcher; private SparkleWatcher watcher;
private SparkleListenerBase listener;
private TimeSpan poll_interval; private TimeSpan poll_interval;
private Timer local_timer = new Timer () { Interval = 0.25 * 1000 }; private Timer local_timer = new Timer () { Interval = 0.25 * 1000 };
private Timer remote_timer = new Timer () { Interval = 10 * 1000 }; private Timer remote_timer = new Timer () { Interval = 10 * 1000 };
@ -47,6 +46,7 @@ namespace SparkleLib {
private bool has_changed = false; private bool has_changed = false;
private Object change_lock = new Object (); private Object change_lock = new Object ();
protected SparkleListenerBase listener;
protected SyncStatus status; protected SyncStatus status;
protected bool is_buffering = false; protected bool is_buffering = false;
protected bool server_online = true; protected bool server_online = true;
@ -110,8 +110,10 @@ namespace SparkleLib {
// In the unlikely case that we haven't synced up our // In the unlikely case that we haven't synced up our
// changes or the server was down, sync up again // changes or the server was down, sync up again
if (HasUnsyncedChanges) if (HasUnsyncedChanges) {
SyncUpBase (); SyncUpBase ();
SyncUpNotes ();
}
}; };
this.remote_timer.Start (); this.remote_timer.Start ();
@ -232,8 +234,10 @@ namespace SparkleLib {
SyncDownBase (); SyncDownBase ();
// Push changes that were made since the last disconnect // Push changes that were made since the last disconnect
if (HasUnsyncedChanges) if (HasUnsyncedChanges) {
SyncUpBase (); SyncUpBase ();
SyncUpNotes ();
}
}; };
// Start polling when the connection to the irc channel is lost // Start polling when the connection to the irc channel is lost
@ -392,8 +396,9 @@ namespace SparkleLib {
if (SyncStatusChanged != null) if (SyncStatusChanged != null)
SyncStatusChanged (SyncStatus.Idle); SyncStatusChanged (SyncStatus.Idle);
if (NewChangeSet != null) SparkleChangeSet change_set = GetChangeSets (1) [0];
NewChangeSet (GetChangeSets (1) [0], LocalPath); if (NewChangeSet != null && change_set.Revision != CurrentRevision)
NewChangeSet (change_set, LocalPath);
// There could be changes from a // There could be changes from a
// resolved conflict. Tries only once, // resolved conflict. Tries only once,
@ -442,6 +447,18 @@ namespace SparkleLib {
} }
public virtual void AddNote (string revision, string note)
{
}
public virtual void SyncUpNotes ()
{
}
// Recursively gets a folder's size in bytes // Recursively gets a folder's size in bytes
private double CalculateFolderSize (DirectoryInfo parent) private double CalculateFolderSize (DirectoryInfo parent)
{ {
@ -455,7 +472,7 @@ namespace SparkleLib {
if (parent.Name.Equals ("rebase-apply")) if (parent.Name.Equals ("rebase-apply"))
return 0; return 0;
foreach (FileInfo file in parent.GetFiles()) { foreach (FileInfo file in parent.GetFiles ()) {
if (!file.Exists) if (!file.Exists)
return 0; return 0;

View file

@ -20,6 +20,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using MonoMac.Foundation; using MonoMac.Foundation;
@ -220,10 +221,30 @@ namespace SparkleShare {
public override void DecidePolicyForNavigation (WebView web_view, NSDictionary action_info, public override void DecidePolicyForNavigation (WebView web_view, NSDictionary action_info,
NSUrlRequest request, WebFrame frame, NSObject decision_token) NSUrlRequest request, WebFrame frame, NSObject decision_token)
{ {
string file_path = request.Url.ToString (); string url = request.Url.ToString ();
file_path = file_path.Replace ("%20", " ");
if (url.StartsWith (Path.VolumeSeparatorChar.ToString ())) {
NSWorkspace.SharedWorkspace.OpenFile (file_path); string file_path = request.Url.ToString ();
file_path = file_path.Replace ("%20", " ");
NSWorkspace.SharedWorkspace.OpenFile (file_path);
} else {
Regex regex = new Regex (@"(.+)~(.+)~(.+)");
Match match = regex.Match (url);
if (match.Success) {
string folder_name = match.Groups [1].Value;
string revision = match.Groups [2].Value;
string note = match.Groups [3].Value.Replace ("%20", " ");
Thread thread = new Thread (new ThreadStart (delegate {
SparkleShare.Controller.AddNoteToFolder (folder_name, revision, note);
}));
thread.Start ();
}
}
} }
} }
} }

View file

@ -138,8 +138,11 @@ namespace SparkleShare {
StreamReader reader = new StreamReader (html_path); StreamReader reader = new StreamReader (html_path);
string html = reader.ReadToEnd (); string html = reader.ReadToEnd ();
reader.Close (); reader.Close ();
return html; html = html.Replace ("<!-- $jquery-url -->", "file://" +
Path.Combine (NSBundle.MainBundle.ResourcePath, "HTML", "jquery.js"));
return html;
} }
} }

View file

@ -62,7 +62,7 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\bin\Meebey.SmartIrc4net.dll</HintPath> <HintPath>..\..\bin\Meebey.SmartIrc4net.dll</HintPath>
</Reference> </Reference>
<Reference Include="SparkleLib, Version=0.2.1.0, Culture=neutral, PublicKeyToken=null"> <Reference Include="SparkleLib, Version=0.2.2.0, Culture=neutral, PublicKeyToken=null">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\bin\SparkleLib.dll</HintPath> <HintPath>..\..\bin\SparkleLib.dll</HintPath>
</Reference> </Reference>
@ -170,6 +170,9 @@
<Content Include="..\..\data\icons\document-moved-12.png"> <Content Include="..\..\data\icons\document-moved-12.png">
<Link>Pixmaps\document-moved-12.png</Link> <Link>Pixmaps\document-moved-12.png</Link>
</Content> </Content>
<Content Include="..\..\data\html\jquery.js">
<Link>HTML\jquery.js</Link>
</Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Pixmaps\" /> <Folder Include="Pixmaps\" />

View file

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
@ -28,7 +28,7 @@ using MonoMac.Growl;
namespace SparkleShare { namespace SparkleShare {
public partial class AppDelegate : NSApplicationDelegate { public partial class AppDelegate : NSApplicationDelegate {
public override void WillBecomeActive (NSNotification notification) public override void WillBecomeActive (NSNotification notification)
{ {
@ -45,22 +45,22 @@ namespace SparkleShare {
{ {
SparkleShare.Controller.Quit (); SparkleShare.Controller.Quit ();
} }
} }
public class SparkleUI : AppDelegate { public class SparkleUI : AppDelegate {
public static SparkleStatusIcon StatusIcon; public static SparkleStatusIcon StatusIcon;
public static SparkleEventLog EventLog; public static SparkleEventLog EventLog;
public static SparkleIntro Intro; public static SparkleIntro Intro;
public static SparkleAbout About; public static SparkleAbout About;
public static NSFont Font; public static NSFont Font;
private NSAlert alert; private NSAlert alert;
public SparkleUI () public SparkleUI ()
{ {
string content_path = Directory.GetParent ( string content_path = Directory.GetParent (
System.AppDomain.CurrentDomain.BaseDirectory).ToString (); System.AppDomain.CurrentDomain.BaseDirectory).ToString ();
@ -95,7 +95,7 @@ namespace SparkleShare {
SparkleShare.Controller.NotificationRaised += delegate (string user_name, string user_email, SparkleShare.Controller.NotificationRaised += delegate (string user_name, string user_email,
string message, string repository_path) { string message, string repository_path) {
InvokeOnMainThread (delegate { InvokeOnMainThread (delegate {
if (EventLog != null) if (EventLog != null)
EventLog.UpdateEvents (); EventLog.UpdateEvents ();
@ -103,7 +103,7 @@ namespace SparkleShare {
if (NSApplication.SharedApplication.DockTile.BadgeLabel == null) if (NSApplication.SharedApplication.DockTile.BadgeLabel == null)
NSApplication.SharedApplication.DockTile.BadgeLabel = "1"; NSApplication.SharedApplication.DockTile.BadgeLabel = "1";
else else
NSApplication.SharedApplication.DockTile.BadgeLabel = NSApplication.SharedApplication.DockTile.BadgeLabel =
(int.Parse (NSApplication.SharedApplication.DockTile.BadgeLabel) + 1).ToString (); (int.Parse (NSApplication.SharedApplication.DockTile.BadgeLabel) + 1).ToString ();
if (GrowlApplicationBridge.IsGrowlRunning ()) { if (GrowlApplicationBridge.IsGrowlRunning ()) {
@ -114,13 +114,13 @@ namespace SparkleShare {
bubble.Show (); bubble.Show ();
} else { } else {
NSApplication.SharedApplication.RequestUserAttention NSApplication.SharedApplication.RequestUserAttention (
(NSRequestUserAttentionType.InformationalRequest); NSRequestUserAttentionType.InformationalRequest);
} }
} }
}); });
}; };
SparkleShare.Controller.ConflictNotificationRaised += delegate { SparkleShare.Controller.ConflictNotificationRaised += delegate {
string title = "Ouch! Mid-air collision!"; string title = "Ouch! Mid-air collision!";
@ -130,13 +130,13 @@ namespace SparkleShare {
}; };
SparkleShare.Controller.AvatarFetched += delegate { SparkleShare.Controller.AvatarFetched += delegate {
InvokeOnMainThread (delegate { InvokeOnMainThread (delegate {
if (EventLog != null) if (EventLog != null)
EventLog.UpdateEvents (); EventLog.UpdateEvents ();
}); });
}; };
SparkleShare.Controller.OnIdle += delegate { SparkleShare.Controller.OnIdle += delegate {
InvokeOnMainThread (delegate { InvokeOnMainThread (delegate {
@ -156,29 +156,29 @@ namespace SparkleShare {
}; };
if (SparkleShare.Controller.FirstRun) { if (SparkleShare.Controller.FirstRun) {
Intro = new SparkleIntro (); Intro = new SparkleIntro ();
Intro.ShowAccountForm (); Intro.ShowAccountForm ();
} }
} }
public void SetFolderIcon () public void SetFolderIcon ()
{ {
string folder_icon_path = Path.Combine (NSBundle.MainBundle.ResourcePath, string folder_icon_path = Path.Combine (NSBundle.MainBundle.ResourcePath,
"sparkleshare-mac.icns"); "sparkleshare-mac.icns");
NSImage folder_icon = new NSImage (folder_icon_path); NSImage folder_icon = new NSImage (folder_icon_path);
NSWorkspace.SharedWorkspace.SetIconforFile (folder_icon, NSWorkspace.SharedWorkspace.SetIconforFile (folder_icon,
SparkleShare.Controller.SparklePath, 0); SparkleShare.Controller.SparklePath, 0);
} }
public void Run () public void Run ()
{ {
NSApplication.Main (new string [0]); NSApplication.Main (new string [0]);
} }
[Export("registrationDictionaryForGrowl")] [Export("registrationDictionaryForGrowl")]

View file

@ -286,6 +286,7 @@ namespace SparkleShare {
public string GetHTMLLog (List<SparkleChangeSet> change_sets) public string GetHTMLLog (List<SparkleChangeSet> change_sets)
{ {
List <ActivityDay> activity_days = new List <ActivityDay> (); List <ActivityDay> activity_days = new List <ActivityDay> ();
List<string> emails = new List<string> ();
change_sets.Sort ((x, y) => (x.Timestamp.CompareTo (y.Timestamp))); change_sets.Sort ((x, y) => (x.Timestamp.CompareTo (y.Timestamp)));
change_sets.Reverse (); change_sets.Reverse ();
@ -294,7 +295,8 @@ namespace SparkleShare {
return null; return null;
foreach (SparkleChangeSet change_set in change_sets) { foreach (SparkleChangeSet change_set in change_sets) {
GetAvatar (change_set.UserEmail, 36); if (!emails.Contains (change_set.UserEmail))
emails.Add (change_set.UserEmail);
bool change_set_inserted = false; bool change_set_inserted = false;
foreach (ActivityDay stored_activity_day in activity_days) { foreach (ActivityDay stored_activity_day in activity_days) {
@ -315,6 +317,10 @@ namespace SparkleShare {
} }
} }
new Thread (new ThreadStart (delegate {
FetchAvatars (emails, 36);
})).Start ();
string event_log_html = EventLogHTML; string event_log_html = EventLogHTML;
string day_entry_html = DayEntryHTML; string day_entry_html = DayEntryHTML;
string event_entry_html = EventEntryHTML; string event_entry_html = EventEntryHTML;
@ -389,14 +395,35 @@ namespace SparkleShare {
} }
} }
} }
string comments = "";
if (change_set.SupportsNotes) {
comments = "<table class=\"comments\">";
change_set.Notes.Sort ((x, y) => (x.Timestamp.CompareTo (y.Timestamp)));
foreach (SparkleNote note in change_set.Notes) {
comments += "<tr>" +
" <td class=\"comment-author\">" + note.UserName + "</td>" +
" <td class=\"comment-timestamp\">" + note.Timestamp.ToString ("d MMM") + "</td>" +
"</tr>" +
"<tr>" +
" <td class=\"comment-text\" colspan=\"2\">" + note.Body + "</td>" +
"</tr>";
}
comments += "</table>";
}
event_entry += "</dl>"; event_entry += "</dl>";
event_entries += event_entry_html.Replace ("<!-- $event-entry-content -->", event_entry) event_entries += event_entry_html.Replace ("<!-- $event-entry-content -->", event_entry)
.Replace ("<!-- $event-user-name -->", change_set.UserName) .Replace ("<!-- $event-user-name -->", change_set.UserName)
.Replace ("<!-- $event-avatar-url -->", "file://" + GetAvatar (change_set.UserEmail, 36)) .Replace ("<!-- $event-avatar-url -->", "file://" + GetAvatar (change_set.UserEmail, 36))
.Replace ("<!-- $event-time -->", change_set.Timestamp.ToString ("H:mm")) .Replace ("<!-- $event-time -->", change_set.Timestamp.ToString ("H:mm"))
.Replace ("<!-- $event-folder -->", change_set.Folder) .Replace ("<!-- $event-folder -->", change_set.Folder)
.Replace ("<!-- $event-folder-color -->", AssignColor (change_set.Folder)); .Replace ("<!-- $event-revision -->", change_set.Revision)
.Replace ("<!-- $event-folder-color -->", AssignColor (change_set.Folder))
.Replace ("<!-- $event-comments -->", comments);
} }
string day_entry = ""; string day_entry = "";
@ -431,7 +458,8 @@ namespace SparkleShare {
event_log += day_entry.Replace ("<!-- $day-entry-content -->", event_entries); event_log += day_entry.Replace ("<!-- $day-entry-content -->", event_entries);
} }
return event_log_html.Replace ("<!-- $event-log-content -->", event_log); return event_log_html.Replace ("<!-- $event-log-content -->", event_log)
.Replace ("<!-- $username -->", UserName);
} }
@ -815,63 +843,69 @@ namespace SparkleShare {
// Gets the avatar for a specific email address and size // Gets the avatar for a specific email address and size
public string GetAvatar (string email, int size) public void FetchAvatars (List<string> emails, int size)
{ {
string avatar_path = SparkleHelpers.CombineMore (SparklePaths.SparkleLocalIconPath, List<string> old_avatars = new List<string> ();
size + "x" + size, "status"); bool avatar_fetched = false;
string avatar_path = SparkleHelpers.CombineMore (
SparklePaths.SparkleLocalIconPath, size + "x" + size, "status");
string avatar_file_path = Path.Combine (avatar_path, "avatar-" + email); if (!Directory.Exists (avatar_path)) {
Directory.CreateDirectory (avatar_path);
SparkleHelpers.DebugInfo ("Config", "Created '" + avatar_path + "'");
}
if (File.Exists (avatar_file_path)) { foreach (string email in emails) {
FileInfo avatar_info = new FileInfo (avatar_file_path); string avatar_file_path = Path.Combine (avatar_path, "avatar-" + email);
// Delete avatars older than a month and get a new one if (File.Exists (avatar_file_path)) {
if (avatar_info.CreationTime < DateTime.Now.AddMonths (-1)) { FileInfo avatar_info = new FileInfo (avatar_file_path);
avatar_info.Delete ();
return GetAvatar (email, size); // Delete avatars older than a month
if (avatar_info.CreationTime < DateTime.Now.AddMonths (-1)) {
avatar_info.Delete ();
old_avatars.Add (email);
}
} else { } else {
return avatar_file_path; WebClient client = new WebClient ();
} string url = "http://gravatar.com/avatar/" + GetMD5 (email) +
".jpg?s=" + size + "&d=404";
} else { try {
if (!Directory.Exists (avatar_path)) { // Fetch the avatar
Directory.CreateDirectory (avatar_path); byte [] buffer = client.DownloadData (url);
SparkleHelpers.DebugInfo ("Config", "Created '" + avatar_path + "'");
}
// Let's try to get the person's gravatar for next time // Write the avatar data to a
WebClient web_client = new WebClient (); // if not empty
Uri uri = new Uri ("https://secure.gravatar.com/avatar/" + GetMD5 (email) + if (buffer.Length > 255) {
".jpg?s=" + size + "&d=404"); avatar_fetched = true;
File.WriteAllBytes (avatar_file_path, buffer);
SparkleHelpers.DebugInfo ("Controller", "Fetched gravatar for " + email);
}
string tmp_file_path = SparkleHelpers.CombineMore (SparklePaths.SparkleTmpPath, email + size); } catch (WebException) {
SparkleHelpers.DebugInfo ("Controller", "Failed fetching gravatar for " + email);
if (!File.Exists (tmp_file_path)) { }
web_client.DownloadFileAsync (uri, tmp_file_path); }
web_client.DownloadFileCompleted += delegate {
if (File.Exists (avatar_file_path))
File.Delete (avatar_file_path);
FileInfo tmp_file_info = new FileInfo (tmp_file_path);
if (tmp_file_info.Length > 255)
File.Move (tmp_file_path, avatar_file_path);
SparkleHelpers.DebugInfo ("Controller", "Fetched gravatar: " + email);
if (AvatarFetched != null)
AvatarFetched ();
};
}
// Fall back to a generic icon if there is no gravatar
if (File.Exists (avatar_file_path))
return avatar_file_path;
else
return null;
} }
// Fetch new versions of the avatars that we
// deleted because they were too old
if (old_avatars.Count > 0)
FetchAvatars (old_avatars, size);
if (AvatarFetched != null && avatar_fetched)
AvatarFetched ();
}
public string GetAvatar (string email, int size)
{
string avatar_file_path = SparkleHelpers.CombineMore (
SparklePaths.SparkleLocalIconPath, size + "x" + size, "status", "avatar-" + email);
return avatar_file_path;
} }
@ -1023,6 +1057,15 @@ namespace SparkleShare {
} }
public void AddNoteToFolder (string folder_name, string revision, string note)
{
foreach (SparkleRepoBase repo in Repositories) {
if (repo.Name.Equals (folder_name))
repo.AddNote (revision, note);
}
}
public void CheckForNewVersion () public void CheckForNewVersion ()
{ {
WebClient web_client = new WebClient (); WebClient web_client = new WebClient ();

View file

@ -84,7 +84,6 @@ namespace SparkleShare {
LinkStatus = args.Link; LinkStatus = args.Link;
}; };
// FIXME: Use the right event, waiting for newer webkit bindings: NavigationPolicyDecisionRequested
WebView.NavigationRequested += delegate (object o, WebKit.NavigationRequestedArgs args) { WebView.NavigationRequested += delegate (object o, WebKit.NavigationRequestedArgs args) {
if (args.Request.Uri == LinkStatus) { if (args.Request.Uri == LinkStatus) {
Process process = new Process (); Process process = new Process ();
@ -92,9 +91,26 @@ namespace SparkleShare {
process.StartInfo.Arguments = args.Request.Uri.Replace (" ", "\\ "); // Escape space-characters process.StartInfo.Arguments = args.Request.Uri.Replace (" ", "\\ "); // Escape space-characters
process.Start (); process.Start ();
// Don't follow HREFs (as this would cause a page refresh) } else {
args.RetVal = 1; Regex regex = new Regex (@"(.+)~(.+)~(.+)");
Match match = regex.Match (args.Request.Uri);
if (match.Success) {
string folder_name = match.Groups [1].Value;
string revision = match.Groups [2].Value;
string note = match.Groups [3].Value.Replace ("%20", " ");
Thread thread = new Thread (new ThreadStart (delegate {
SparkleShare.Controller.AddNoteToFolder (folder_name, revision, note);
}));
thread.Start ();
}
} }
// Don't follow HREFs (as this would cause a page refresh)
if (!args.Request.Uri.Equals ("file:"))
args.RetVal = 1;
}; };
ScrolledWindow.Add (WebView); ScrolledWindow.Add (WebView);
@ -142,6 +158,12 @@ namespace SparkleShare {
return (item == "---"); return (item == "---");
}; };
if (this.selected_log != null &&
!SparkleShare.Controller.Folders.Contains (this.selected_log)) {
this.selected_log = null;
}
this.combo_box.Changed += delegate { this.combo_box.Changed += delegate {
TreeIter iter; TreeIter iter;
this.combo_box.GetActiveIter (out iter); this.combo_box.GetActiveIter (out iter);

View file

@ -166,8 +166,13 @@ namespace SparkleShare {
get { get {
string path = SparkleHelpers.CombineMore (Defines.PREFIX, string path = SparkleHelpers.CombineMore (Defines.PREFIX,
"share", "sparkleshare", "html", "event-log.html"); "share", "sparkleshare", "html", "event-log.html");
string html = String.Join (Environment.NewLine, File.ReadAllLines (path));
html = html.Replace ("<!-- $jquery-url -->", "file://" +
SparkleHelpers.CombineMore (Defines.PREFIX, "share", "sparkleshare", "html", "jquery.js"));
return String.Join (Environment.NewLine, File.ReadAllLines (path)); return html;
} }
} }

View file

@ -79,12 +79,12 @@ namespace SparkleShare {
return; return;
SparkleBubble bubble = new SparkleBubble (user_name, message); SparkleBubble bubble = new SparkleBubble (user_name, message);
string avatar_file_path = SparkleShare.Controller.GetAvatar (user_email, 32); string avatar_file_path = SparkleShare.Controller.GetAvatar (user_email, 36);
if (avatar_file_path != null) if (avatar_file_path != null)
bubble.Icon = new Gdk.Pixbuf (avatar_file_path); bubble.Icon = new Gdk.Pixbuf (avatar_file_path);
else else
bubble.Icon = SparkleUIHelpers.GetIcon ("avatar-default", 32); bubble.Icon = SparkleUIHelpers.GetIcon ("avatar-default", 36);
bubble.Show (); bubble.Show ();
}); });

View file

@ -1,7 +1,8 @@
dist_html_DATA = \ dist_html_DATA = \
day-entry.html \ day-entry.html \
event-entry.html \ event-entry.html \
event-log.html event-log.html \
jquery.js
htmldir = $(pkgdatadir)/html/ htmldir = $(pkgdatadir)/html/

View file

@ -8,9 +8,20 @@
<b><!-- $event-user-name --></b><br/> <b><!-- $event-user-name --></b><br/>
<small><!-- $event-time --></small> <small><!-- $event-time --></small>
</div> </div>
<div class='event-time' style='background-color: <!-- $event-folder-color -->'><!-- $event-folder --></div> <div class='event-folder' style='background-color: <!-- $event-folder-color -->'><!-- $event-folder --></div>
</div> </div>
<!-- $event-entry-content --> <!-- $event-entry-content -->
<div class="comments-section">
<div class="comments-wrapper">
<!-- $event-comments -->
<textarea class="comment-textarea"></textarea>
<input class="comment-button" type="button" value="Add note"
id="<!-- $event-folder -->~<!-- $event-revision -->">
<div style='clear: both'></div>
</div>
<div style='clear: both'></div>
</div>
<div style='clear: both'></div> <div style='clear: both'></div>
</div> </div>
</div> </div>

View file

@ -2,6 +2,47 @@
<html> <html>
<head> <head>
<title>SparkleShare Event Log</title> <title>SparkleShare Event Log</title>
<script type="text/javascript" src="<!-- $jquery-url -->"></script>
<script type="text/javascript">
$(document).ready(function () {
$('.comments-wrapper').each (function () {
if ($(this).find ('.comments').children ().size () < 1) {
$(this).hide ();
} else {
$(this).css ('cursor', 'default');
}
});
$('.comments-section').click(function () {
$(this).find ('.comments-wrapper').show ();
$(this).find ('.comments-wrapper').css ('cursor', 'default');
});
$("input").click(function () {
textarea = $(this).parent ().find ('textarea');
text = textarea.val ();
if (text == '')
return;
textarea.val ('');
table = $(this).parent ().find ("table");
comments = table.html ();
comments += '<tr>' +
' <td class=\"comment-author\"><!-- $username --></td>' +
' <td class=\"comment-timestamp\">just now</td>' +
'</tr>' +
'<tr>' +
' <td class=\"comment-text\" colspan=\"2\">' + text + '</td>' +
'</tr>';
table.html (comments);
location = this.id + '~' + text;
});
});
</script>
<style> <style>
body { body {
background-color: #dedede; background-color: #dedede;
@ -22,6 +63,11 @@
color: <!-- $secondary-font-color -->; color: <!-- $secondary-font-color -->;
} }
td {
vertical-align: top;
font-size: 12px;
}
.day-entry-header { .day-entry-header {
font-size: <!-- $day-entry-header-font-size -->; font-size: <!-- $day-entry-header-font-size -->;
color: #444; color: #444;
@ -30,7 +76,6 @@
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
a { a {
color: <!-- $a-color -->; color: <!-- $a-color -->;
@ -41,8 +86,8 @@
color: <!-- $a-hover-color -->; color: <!-- $a-hover-color -->;
} }
.event-time { .event-folder {
margin: 15px; margin: 6px;
opacity: 0.8; opacity: 0.8;
font-size: 80%; font-size: 80%;
color: #fff; color: #fff;
@ -50,19 +95,19 @@
padding: 3px 15px; padding: 3px 15px;
font-weight: bold; font-weight: bold;
-webkit-border-radius: 100px; -webkit-border-radius: 100px;
cursor: pointer;
} }
.event-entry-content { .event-entry-content {
display: block; display: block;
background-color: #f0f0f0; background-color: #f0f0f0;
margin-bottom: 12px; margin-bottom: 12px;
padding: 0px; padding: 0px;
padding-bottom: 6px; border: #ccc 1px solid;
border: #ccc 1px solid;
} }
.wrapper { .wrapper {
margin: 15px; margin: 9px;
float: left; float: left;
} }
@ -89,6 +134,7 @@
dd:last-child { dd:last-child {
border: none; border: none;
margin-bottom: 6px;
} }
.document-added { .document-added {
@ -122,11 +168,65 @@
} }
.event-info { .event-info {
padding: 6px;
float: left; float: left;
margin-bottom: 8px; margin-bottom: 8px;
width: 100%; width: 100%;
background-color: #fff; background-color: #fff;
border-bottom: 1px #ccc solid; border-bottom: 1px #ccc solid;
box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.comments {
width: 100%;
}
.comment-author {
font-weight: bold;
width: 75%;
}
.comment-timestamp {
color: <!-- $secondary-font-color -->;
text-align: right;
}
.comment-button {
float:right;
margin-top: 6px;
margin-bottom: 12px;
}
.comment-textarea {
box-sizing: border-box;
-webkit-box-sizing: border-box;
width: 100%;
height: 50px;
padding-bottom: 6px;
}
.comments-section {
box-sizing: border-box;
-webkit-box-sizing: border-box;
border-top: 1px #ccc solid;
width: 100%;
height: 9px;
}
.comments-wrapper {
box-sizing: border-box;
-webkit-box-sizing: border-box;
width: 100%;
padding: 12px;
}
.comments-section:hover {
cursor: pointer;
}
.comment-text {
padding-bottom: 15px;
} }
</style> </style>
</head> </head>

18
data/html/jquery.js vendored Normal file

File diff suppressed because one or more lines are too long